初級4-1 佇列棧題目
題目四:貓狗佇列
【題目】 寵物、狗和貓的類如下:
public class Pet { private String type;
public Pet(String type) { this.type = type; }
public String getPetType() { return this.type; }
}
public class Dog extends Pet { public Dog() { super("dog"); } }
public class Cat extends Pet { public Cat() { super("cat"); } }
實現一種狗貓佇列的結構,要求如下: 使用者可以呼叫add方法將cat類或dog類的
例項放入佇列中; 使用者可以呼叫pollAll方法,將佇列中所有的例項按照進佇列
的先後順序依次彈出; 使用者可以呼叫pollDog方法,將佇列中dog類的例項按照
進佇列的先後順序依次彈出; 使用者可以呼叫pollCat方法,將佇列中cat類的實
例按照進佇列的先後順序依次彈出; 使用者可以呼叫isEmpty方法,檢查佇列中是
否還有dog或cat的例項; 使用者可以呼叫isDogEmpty方法,檢查佇列中是否有dog
類的例項; 使用者可以呼叫isCatEmpty方法,檢查佇列中是否有cat類的例項。
package class_03; import java.util.LinkedList; import java.util.Queue; public class Code_04_DogCatQueue { public static class Pet { private String type; public Pet(String type) { this.type = type; } public String getPetType() {return this.type; } } public static class Dog extends Pet { public Dog() { super("dog"); } } public static class Cat extends Pet { public Cat() { super("cat"); } } public static class PetEnterQueue {private Pet pet; private long count; public PetEnterQueue(Pet pet, long count) { this.pet = pet; this.count = count; } public Pet getPet() { return this.pet; } public long getCount() { return this.count; } public String getEnterPetType() { return this.pet.getPetType(); } } public static class DogCatQueue { private Queue<PetEnterQueue> dogQ; private Queue<PetEnterQueue> catQ; private long count; public DogCatQueue() { this.dogQ = new LinkedList<PetEnterQueue>(); this.catQ = new LinkedList<PetEnterQueue>(); this.count = 0; } public void add(Pet pet) { if (pet.getPetType().equals("dog")) { this.dogQ.add(new PetEnterQueue(pet, this.count++)); } else if (pet.getPetType().equals("cat")) { this.catQ.add(new PetEnterQueue(pet, this.count++)); } else { throw new RuntimeException("err, not dog or cat"); } } public Pet pollAll() { if (!this.dogQ.isEmpty() && !this.catQ.isEmpty()) { if (this.dogQ.peek().getCount() < this.catQ.peek().getCount()) { return this.dogQ.poll().getPet(); } else { return this.catQ.poll().getPet(); } } else if (!this.dogQ.isEmpty()) { return this.dogQ.poll().getPet(); } else if (!this.catQ.isEmpty()) { return this.catQ.poll().getPet(); } else { throw new RuntimeException("err, queue is empty!"); } } public Dog pollDog() { if (!this.isDogQueueEmpty()) { return (Dog) this.dogQ.poll().getPet(); } else { throw new RuntimeException("Dog queue is empty!"); } } public Cat pollCat() { if (!this.isCatQueueEmpty()) { return (Cat) this.catQ.poll().getPet(); } else throw new RuntimeException("Cat queue is empty!"); } public boolean isEmpty() { return this.dogQ.isEmpty() && this.catQ.isEmpty(); } public boolean isDogQueueEmpty() { return this.dogQ.isEmpty(); } public boolean isCatQueueEmpty() { return this.catQ.isEmpty(); } } public static void main(String[] args) { DogCatQueue test = new DogCatQueue(); Pet dog1 = new Dog(); Pet cat1 = new Cat(); Pet dog2 = new Dog(); Pet cat2 = new Cat(); Pet dog3 = new Dog(); Pet cat3 = new Cat(); test.add(dog1); test.add(cat1); test.add(dog2); test.add(cat2); test.add(dog3); test.add(cat3); test.add(dog1); test.add(cat1); test.add(dog2); test.add(cat2); test.add(dog3); test.add(cat3); test.add(dog1); test.add(cat1); test.add(dog2); test.add(cat2); test.add(dog3); test.add(cat3); while (!test.isDogQueueEmpty()) { System.out.println(test.pollDog().getPetType()); } while (!test.isEmpty()) { System.out.println(test.pollAll().getPetType()); } } }
題目五轉圈列印矩陣
【題目】 給定一個整型矩陣matrix,請按照轉圈的方式列印它。
例如: 1 2 3 4 5 6 7 8 9 10 11 12 13 14
15 16 列印結果為:1,2,3,4,8,12,16,15,14,13,9,
5,6,7,11, 10
【要求】 額外空間複雜度為O(1)。
package class_03; public class Code_06_PrintMatrixSpiralOrder { public static void spiralOrderPrint(int[][] matrix) { int tR = 0; int tC = 0; int dR = matrix.length - 1; int dC = matrix[0].length - 1; while (tR <= dR && tC <= dC) { printEdge(matrix, tR++, tC++, dR--, dC--); } } public static void printEdge(int[][] m, int tR, int tC, int dR, int dC) { if (tR == dR) { for (int i = tC; i <= dC; i++) { System.out.print(m[tR][i] + " "); } } else if (tC == dC) { for (int i = tR; i <= dR; i++) { System.out.print(m[i][tC] + " "); } } else { int curC = tC; int curR = tR; while (curC != dC) { System.out.print(m[tR][curC] + " "); curC++; } while (curR != dR) { System.out.print(m[curR][dC] + " "); curR++; } while (curC != tC) { System.out.print(m[dR][curC] + " "); curC--; } while (curR != tR) { System.out.print(m[curR][tC] + " "); curR--; } } } public static void main(String[] args) { int[][] matrix = { { 1, 2, 3, 4 }, { 5, 6, 7, 8 }, { 9, 10, 11, 12 }, { 13, 14, 15, 16 } }; spiralOrderPrint(matrix); } }
題目六:旋轉正方形矩陣
【題目】 給定一個整型正方形矩陣matrix,請把該矩陣調整成
順時針旋轉90度的樣子。
【要求】 額外空間複雜度為O(1)。
先轉外圈
1、 1,4,13,16
2、2,8,15,9
3、5,12,14,5
package class_03; public class Code_05_RotateMatrix { public static void rotate(int[][] matrix) { int tR = 0; int tC = 0; int dR = matrix.length - 1; int dC = matrix[0].length - 1; while (tR < dR) { rotateEdge(matrix, tR++, tC++, dR--, dC--); } } public static void rotateEdge(int[][] m, int tR, int tC, int dR, int dC) { int times = dC - tC; int tmp = 0; for (int i = 0; i != times; i++) { tmp = m[tR][tC + i]; m[tR][tC + i] = m[dR - i][tC]; m[dR - i][tC] = m[dR][dC - i]; m[dR][dC - i] = m[tR + i][dC]; m[tR + i][dC] = tmp; } } public static void printMatrix(int[][] matrix) { for (int i = 0; i != matrix.length; i++) { for (int j = 0; j != matrix[0].length; j++) { System.out.print(matrix[i][j] + " "); } System.out.println(); } } public static void main(String[] args) { int[][] matrix = { { 1, 2, 3, 4 }, { 5, 6, 7, 8 }, { 9, 10, 11, 12 }, { 13, 14, 15, 16 } }; printMatrix(matrix); rotate(matrix); System.out.println("========="); printMatrix(matrix); } }
題目七:轉單向和雙向連結串列
【題目】 分別實現反轉單向連結串列和反轉雙向連結串列的函式。
【要求】 如果連結串列長度為N,時間複雜度要求為O(N),額外空間
複雜度要求為O(1)
package class_03; public class Code_07_ReverseList { public static class Node { public int value; public Node next; public Node(int data) { this.value = data; } } public static Node reverseList(Node head) { Node pre = null; Node next = null; while (head != null) { next = head.next; head.next = pre; pre = head; head = next; } return pre; } public static class DoubleNode { public int value; public DoubleNode last; public DoubleNode next; public DoubleNode(int data) { this.value = data; } } public static DoubleNode reverseList(DoubleNode head) { DoubleNode pre = null; DoubleNode next = null; while (head != null) { next = head.next; head.next = pre; head.last = next; pre = head; head = next; } return pre; } public static void printLinkedList(Node head) { System.out.print("Linked List: "); while (head != null) { System.out.print(head.value + " "); head = head.next; } System.out.println(); } public static void printDoubleLinkedList(DoubleNode head) { System.out.print("Double Linked List: "); DoubleNode end = null; while (head != null) { System.out.print(head.value + " "); end = head; head = head.next; } System.out.print("| "); while (end != null) { System.out.print(end.value + " "); end = end.last; } System.out.println(); } public static void main(String[] args) { Node head1 = new Node(1); head1.next = new Node(2); head1.next.next = new Node(3); printLinkedList(head1); head1 = reverseList(head1); printLinkedList(head1); DoubleNode head2 = new DoubleNode(1); head2.next = new DoubleNode(2); head2.next.last = head2; head2.next.next = new DoubleNode(3); head2.next.next.last = head2.next; head2.next.next.next = new DoubleNode(4); head2.next.next.next.last = head2.next.next; printDoubleLinkedList(head2); printDoubleLinkedList(reverseList(head2)); } }
題目八:“之”字形列印矩陣
【題目】 給定一個矩陣matrix,按照“之”字形的方式列印這
個矩陣,例如: 1 2 3 4 5 6 7 8 9 10 11 12
“之”字形列印的結果為:1,2,5,9,6,3,4,7,10,11,
8,12
【要求】 額外空間複雜度為O(1)。
coding:對資料加工的一種技巧
設計巨集觀結構
bool型別
A--> 往左,到邊界向下
B--> 往下,到邊界往右
每次列印對角線,AB分開移動
package class_03; public class Code_08_ZigZagPrintMatrix { public static void printMatrixZigZag(int[][] matrix) { int tR = 0; int tC = 0; int dR = 0; int dC = 0; int endR = matrix.length - 1; int endC = matrix[0].length - 1; boolean fromUp = false; while (tR != endR + 1) { printLevel(matrix, tR, tC, dR, dC, fromUp); tR = tC == endC ? tR + 1 : tR; tC = tC == endC ? tC : tC + 1; dC = dR == endR ? dC + 1 : dC; dR = dR == endR ? dR : dR + 1; fromUp = !fromUp; } System.out.println(); } public static void printLevel(int[][] m, int tR, int tC, int dR, int dC, boolean f) { if (f) { while (tR != dR + 1) { System.out.print(m[tR++][tC--] + " "); } } else { while (dR != tR - 1) { System.out.print(m[dR--][dC++] + " "); } } } public static void main(String[] args) { int[][] matrix = { { 1, 2, 3, 4 }, { 5, 6, 7, 8 }, { 9, 10, 11, 12 } }; printMatrixZigZag(matrix); } }
“巨集觀”解決列印問題
題目九:在行列都排好序的矩陣中找數
【題目】 給定一個有N*M的整型矩陣matrix和一個整數K,
matrix的每一行和每一 列都是排好序的。實現一個函式,判斷K
是否在matrix中。 例如: 0 1 2 5 2 3 4 7 4
4 4 8 5 7 7 9 如果K為7,返回true;如果K為6,返
回false。
【要求】 時間複雜度為O(N+M),額外空間複雜度為O(1)。
package class_03; public class Code_09_FindNumInSortedMatrix { public static boolean isContains(int[][] matrix, int K) { int row = 0; int col = matrix[0].length - 1; while (row < matrix.length && col > -1) { if (matrix[row][col] == K) { return true; } else if (matrix[row][col] > K) { col--; } else { row++; } } return false; } public static void main(String[] args) { int[][] matrix = new int[][] { { 0, 1, 2, 3, 4, 5, 6 },// 0 { 10, 12, 13, 15, 16, 17, 18 },// 1 { 23, 24, 25, 26, 27, 28, 29 },// 2 { 44, 45, 46, 47, 48, 49, 50 },// 3 { 65, 66, 67, 68, 69, 70, 71 },// 4 { 96, 97, 98, 99, 100, 111, 122 },// 5 { 166, 176, 186, 187, 190, 195, 200 },// 6 { 233, 243, 321, 341, 356, 370, 380 } // 7 }; int K = 233; System.out.println(isContains(matrix, K)); } }
兩大思路
一個題的最優解來自這個題目的 資料狀況
一個題的最優解來自這個題目的 本身問法
題目十:列印兩個有序連結串列的公共部分
【題目】 給定兩個有序連結串列的頭指標head1和head2,列印兩個
連結串列的公共部分。
類似快排中的merge
package class_03; public class Code_10_PrintCommonPart { public static class Node { public int value; public Node next; public Node(int data) { this.value = data; } } public static void printCommonPart(Node head1, Node head2) { System.out.print("Common Part: "); while (head1 != null && head2 != null) { if (head1.value < head2.value) { head1 = head1.next; } else if (head1.value > head2.value) { head2 = head2.next; } else { System.out.print(head1.value + " "); head1 = head1.next; head2 = head2.next; } } System.out.println(); } public static void printLinkedList(Node node) { System.out.print("Linked List: "); while (node != null) { System.out.print(node.value + " "); node = node.next; } System.out.println(); } public static void main(String[] args) { Node node1 = new Node(2); node1.next = new Node(3); node1.next.next = new Node(5); node1.next.next.next = new Node(6); Node node2 = new Node(1); node2.next = new Node(2); node2.next.next = new Node(5); node2.next.next.next = new Node(7); node2.next.next.next.next = new Node(8); printLinkedList(node1); printLinkedList(node2); printCommonPart(node1, node2); } }
連結串列問題
空間複雜度O(1) 面試中重點是O(1)
時間複雜度O(n)
如果用輔助空間,下面的題目都很easy
題目十一:判斷一個連結串列是否為迴文結構
【題目】 給定一個連結串列的頭節點head,請判斷該連結串列是否為回
文結構。 例如: 1->2->1,返回true。 1->2->2->1,返回true。
15->6->15,返回true。 1->2->3,返回false。
進階: 如果連結串列長度為N,時間複雜度達到O(N),額外空間複雜
度達到O(1)。
1、放入棧 O(N)
2、快慢指標 O(N/2)
3、面試中,快慢指標 O(1)
完美解法
快指標 2步,走完
慢指標1步,重點
右半部分逆序
一一比對,相同的話,True,False
資料要恢復回來
package class_03; import java.util.Stack; public class Code_11_IsPalindromeList { public static class Node { public int value; public Node next; public Node(int data) { this.value = data; } } // need n extra space public static boolean isPalindrome1(Node head) { Stack<Node> stack = new Stack<Node>(); Node cur = head; while (cur != null) { stack.push(cur); cur = cur.next; } while (head != null) { if (head.value != stack.pop().value) { return false; } head = head.next; } return true; } // need n/2 extra space public static boolean isPalindrome2(Node head) { if (head == null || head.next == null) { return true; } Node right = head.next; Node cur = head; while (cur.next != null && cur.next.next != null) { right = right.next; cur = cur.next.next; } Stack<Node> stack = new Stack<Node>(); while (right != null) { stack.push(right); right = right.next; } while (!stack.isEmpty()) { if (head.value != stack.pop().value) { return false; } head = head.next; } return true; } // need O(1) extra space public static boolean isPalindrome3(Node head) { if (head == null || head.next == null) { return true; } Node n1 = head; Node n2 = head; while (n2.next != null && n2.next.next != null) { // find mid node n1 = n1.next; // n1 -> mid n2 = n2.next.next; // n2 -> end } n2 = n1.next; // n2 -> right part first node n1.next = null; // mid.next -> null Node n3 = null; while (n2 != null) { // right part convert n3 = n2.next; // n3 -> save next node n2.next = n1; // next of right node convert n1 = n2; // n1 move n2 = n3; // n2 move } n3 = n1; // n3 -> save last node n2 = head;// n2 -> left first node boolean res = true; while (n1 != null && n2 != null) { // check palindrome if (n1.value != n2.value) { res = false; break; } n1 = n1.next; // left to mid n2 = n2.next; // right to mid } n1 = n3.next; n3.next = null; while (n1 != null) { // recover list n2 = n1.next; n1.next = n3; n3 = n1; n1 = n2; } return res; } public static void printLinkedList(Node node) { System.out.print("Linked List: "); while (node != null) { System.out.print(node.value + " "); node = node.next; } System.out.println(); } public static void main(String[] args) { Node head = null; printLinkedList(head); System.out.print(isPalindrome1(head) + " | "); System.out.print(isPalindrome2(head) + " | "); System.out.println(isPalindrome3(head) + " | "); printLinkedList(head); System.out.println("========================="); head = new Node(1); printLinkedList(head); System.out.print(isPalindrome1(head) + " | "); System.out.print(isPalindrome2(head) + " | "); System.out.println(isPalindrome3(head) + " | "); printLinkedList(head); System.out.println("========================="); head = new Node(1); head.next = new Node(2); printLinkedList(head); System.out.print(isPalindrome1(head) + " | "); System.out.print(isPalindrome2(head) + " | "); System.out.println(isPalindrome3(head) + " | "); printLinkedList(head); System.out.println("========================="); head = new Node(1); head.next = new Node(1); printLinkedList(head); System.out.print(isPalindrome1(head) + " | "); System.out.print(isPalindrome2(head) + " | "); System.out.println(isPalindrome3(head) + " | "); printLinkedList(head); System.out.println("========================="); head = new Node(1); head.next = new Node(2); head.next.next = new Node(3); printLinkedList(head); System.out.print(isPalindrome1(head) + " | "); System.out.print(isPalindrome2(head) + " | "); System.out.println(isPalindrome3(head) + " | "); printLinkedList(head); System.out.println("========================="); head = new Node(1); head.next = new Node(2); head.next.next = new Node(1); printLinkedList(head); System.out.print(isPalindrome1(head) + " | "); System.out.print(isPalindrome2(head) + " | "); System.out.println(isPalindrome3(head) + " | "); printLinkedList(head); System.out.println("========================="); head = new Node(1); head.next = new Node(2); head.next.next = new Node(3); head.next.next.next = new Node(1); printLinkedList(head); System.out.print(isPalindrome1(head) + " | "); System.out.print(isPalindrome2(head) + " | "); System.out.println(isPalindrome3(head) + " | "); printLinkedList(head); System.out.println("========================="); head = new Node(1); head.next = new Node(2); head.next.next = new Node(2); head.next.next.next = new Node(1); printLinkedList(head); System.out.print(isPalindrome1(head) + " | "); System.out.print(isPalindrome2(head) + " | "); System.out.println(isPalindrome3(head) + " | "); printLinkedList(head); System.out.println("========================="); head = new Node(1); head.next = new Node(2); head.next.next = new Node(3); head.next.next.next = new Node(2); head.next.next.next.next = new Node(1); printLinkedList(head); System.out.print(isPalindrome1(head) + " | "); System.out.print(isPalindrome2(head) + " | "); System.out.println(isPalindrome3(head) + " | "); printLinkedList(head); System.out.println("========================="); } }
左神:推薦用C++程式碼
不要求有語言特性
比如 range(len(array))
題目十二:將單向連結串列按某值劃分成左邊小、中間相等、右邊大的形式
荷蘭國旗問題
【題目】 給定一個單向連結串列的頭節點head,節點的值型別是整型,再給定一個
整 數pivot。實現一個調整連結串列的函式,將連結串列調整為左部分都是值小於 pivot
的節點,中間部分都是值等於pivot的節點,右部分都是值大於 pivot的節點。
除這個要求外,對調整後的節點順序沒有更多的要求。 例如:連結串列9->0->4->5-
>1,pivot=3。 調整後連結串列可以是1->0->4->9->5,也可以是0->1->9->5->4。總
之,滿 足左部分都是小於3的節點,中間部分都是等於3的節點(本例中這個部
分為空),右部分都是大於3的節點即可。對某部分內部的節點順序不做 要求。
與面試官聊天
1、什麼是穩定性
2、荷蘭國旗不具有穩定性
3、連結串列問題可以省略空間完成
4、你coding達標
進階: 在原問題的要求之上再增加如下兩個要求。
在左、中、右三個部分的內部也做順序要求,要求每部分裡的節點從左 到右的
順序與原連結串列中節點的先後次序一致。 例如:連結串列9->0->4->5->1,pivot=3。
調整後的連結串列是0->1->9->4->5。 在滿足原問題要求的同時,左部分節點從左到
右為0、1。在原連結串列中也 是先出現0,後出現1;中間部分在本例中為空,不再
討論;右部分節點 從左到右為9、4、5。在原連結串列中也是先出現9,然後出現4,
最後出現5。
如果連結串列長度為N,時間複雜度請達到O(N),額外空間複雜度請達到O(1)。