最近在用Java刷劍指offer(第二版)的面試題。書中原題的代碼採用C++編寫,有些題的初衷是爲了考察C++的指針、模板等特性,這些題使用Java編寫有些不合適。但多數題仍是考察通用的算法、數據結構以及編程思想等,與語言自己無太大關係。所以在選擇編程語言時,我仍是選擇了Java。好吧,主要是我C++忘得差很少了,僅僅是曾經學過倆月,使用Java順手一些。後續可能再用Python刷一遍。java
github見:https://github.com/ikheu/sword_offernode
題目一:找出數組中重複的數字git
題目二:不修改數組找出重複的數字github
package sword_offer; // page 39 數組中重複的數字 public class Solution3 { // 題目1 // 輸出數組中重複的數字,空間複雜度O(1),時間複雜度O(n) // 數組長度爲n,數字在0~n-1範圍內 public static int duplicate(int[] arr) { for (int i = 0; i < arr.length; i++) { //System.out.println(i); while (arr[i] != i) { if (arr[i] == arr[arr[i]]) return arr[i]; else { int temp = arr[i]; arr[i] = arr[temp]; arr[temp] = temp; //System.out.println(Arrays.toString(arr)); } } } return -1; } // 題目2 // 輸出數組中重複的數字,空間複雜度O(1),時間複雜度O(nlog(n)) // 數組長度爲n+1,數字在1~n範圍內,要求不修改數組,並不使用輔助空間 public static int getDuplication(int[] arr) { int start = 1; int end = arr.length - 1; while(end >= start) { int middle = (end - start) / 2 + start; int count = getCount(arr, start, middle); if (end == start) { if (count > 1) return start; else break; } if (count > middle - start + 1) { end = middle; } else start = middle + 1; } return -1; } // 計算數組arr元素處在[start, end]範圍內元素個數 private static int getCount(int[] arr, int start, int end) { int count = 0; for (int i = 0; i < arr.length; i++) { if (arr[i] >= start && arr[i] <= end) count++; } return count; } // 測試 public static void main(String[] args) { int[] arr = {1, 2, 3, 1}; System.out.println(duplicate(arr));int[] arr2 = {2, 3, 5, 4, 3, 2, 6, 7}; System.out.println(getDuplication(arr2)); } }
例如,從二維數組$\left[ {\begin{array}{*{20}{c}}
{1}&{2}&{8}&9 \\
{1}&{4}&{9}&{12} \\
{4}&{7}&{10}&{13} \\
{6}&{8}&{11}&{15}
\end{array}} \right]$中尋找是否包含數字7。面試
從右上角查找時,逐漸向左下方縮小範圍。紅色的表明包含目標值7的區域,過程以下:算法
$$\left[ {\begin{array}{*{20}{c}}
{\color{red}1}&{\color{red}2}&{\color{red}8}&9 \\
{\color{red}1}&{\color{red}4}&{\color{red}9}&{12} \\
{\color{red}4}&{\color{red}7}&{\color{red}{10}}&{13} \\
{\color{red}6}&{\color{red}8}&{\color{red}{11}}&{15}
\end{array}} \right]\to\left[ {\begin{array}{*{20}{c}}
{\color{red}1}&{\color{red}2}&{8}&9 \\
{\color{red}1}&{\color{red}4}&{9}&{12} \\
{\color{red}4}&{\color{red}7}&{10}&{13} \\
{\color{red}6}&{\color{red}8}&{11}&{15}
\end{array}} \right]\to\left[ {\begin{array}{*{20}{c}}
{1}&{2}&{8}&9 \\
{\color{red}1}&{\color{red}4}&{9}&{12} \\
{\color{red}4}&{\color{red}7}&{10}&{13} \\
{\color{red}6}&{\color{red}8}&{11}&{15}
\end{array}} \right]\to\left[ {\begin{array}{*{20}{c}}
{1}&{2}&{8}&9 \\
{1}&{4}&{9}&{12} \\
{\color{red}4}&{\color{red}7}&{10}&{13} \\
{\color{red}6}&{\color{red}8}&{11}&{15}
\end{array}} \right]$$編程
從左下角查找時,逐漸向右上方縮小範圍。過程以下:小程序
$$\left[ {\begin{array}{*{20}{c}}
{1}&{\color{red}2}&{\color{red}8}&{\color{red}9} \\
{1}&{\color{red}4}&{\color{red}9}&{\color{red}{12}} \\
{4}&{\color{red}7}&{\color{red}{10}}&{\color{red}{13}} \\
{6}&{\color{red}8}&{\color{red}{11}}&{\color{red}{15}}
\end{array}} \right]\to\left[ {\begin{array}{*{20}{c}}
{1}&{\color{red}2}&{\color{red}8}&{\color{red}9} \\
{1}&{\color{red}4}&{\color{red}9}&{\color{red}{12}} \\
{4}&{\color{red}7}&{\color{red}{10}}&{\color{red}{13}} \\
{6}&{8}&{11}&{15}
\end{array}} \right]$$數組
package sword_offer; // page 44 二維數組中的查找 public class Solution4 { // 從右上角的元素開始查找,逐漸縮小範圍 public static boolean findNum(int[][] arr, int target) { boolean found = false; int row = 0; int col = arr[0].length - 1; while (col > 0 && row <= arr.length) { int diff = arr[row][col] - target; if (diff == 0) { found = true; break; } else if (diff > 0) col--; else row++; } return found; } // 測試 public static void main(String[] args) { int[][] arr = {{1,2,8,9},{2,4,9,12},{4,7,10,13},{6,8,11,15}}; System.out.println(findNum(arr, 9)); } }
package sword_offer; // page 51 替換空格 public class Solution5 { // 在Java中字符串時不可變的,於是只能構造一個新的字符串。原文中該題的難點也沒法體現出來了。 public static String replaceBlank(String str) { StringBuilder strb = new StringBuilder(); for (int i = 0; i < str.length(); i++) { if (str.charAt(i) == ' ') { strb.append("%20"); } else strb.append(str.charAt(i)); } return strb.toString(); } // 測試 public static void main(String[] args) { String str = "We are happr."; System.out.println(replaceBlank(str)); } }
package sword_offer; //page 58 從尾到頭打印鏈表 import java.util.Stack; //鏈表類 class ListNode{ ListNode next = null; int value; public void printOut() { System.out.println(value); ListNode tmp = next; while (tmp != null) { System.out.println(tmp.value); tmp = tmp.next; } } } public class Solution06 { //方法1:使用Stack棧的先push後pop public static void printListReverse(ListNode listNode) { Stack<ListNode> stack = new Stack<ListNode>(); while(listNode != null) { stack.push(listNode); listNode = listNode.next; } while(!stack.isEmpty()) { System.out.println(stack.pop().value); } } //方法2:使用遞歸的方式,至關於從內部往外部推 public static void printListReverse_rec(ListNode listNode) { if(listNode != null) { if (listNode.next != null) printListReverse_rec(listNode.next); System.out.println(listNode.value); } } //測試 public static void main(String[] args) { ListNode ln1 = new ListNode(); ListNode ln2 = new ListNode(); ListNode ln3 = new ListNode(); ln1.next = ln2; ln2.next = ln3; ln1.value = 1; ln2.value = 2; ln3.value = 3; printListReverse_rec(ln1); printListReverse(ln1); } }
package sword_offer; // page 62 重建二叉樹 // 二叉樹類,包含左右子樹,以及用於查看的方法 class BinaryTreeNode { int value; BinaryTreeNode leftNode; BinaryTreeNode rightNode; // 中序遍歷輸出查看 public void printList() { if (leftNode != null) leftNode.printList(); System.out.println(value); if (rightNode != null) rightNode.printList(); } } public class Solution7 { // 重建二叉樹函數 public static BinaryTreeNode rebultTree(int[] preorder, int[] inorder) { BinaryTreeNode root = rebultTree(preorder, 0, preorder.length - 1, inorder, 0, inorder.length - 1); return root; }
// 重寫函數 private static BinaryTreeNode rebultTree(int[] preorder, int startPre, int endPre, int[] inorder, int startIn, int endIn) { if (startPre > endPre || startIn > endIn) return null; BinaryTreeNode root = new BinaryTreeNode(); root.value = preorder[startPre]; for (int i = startIn; i <= endIn; i++) { if (inorder[i] == preorder[startPre]) { root.leftNode = rebultTree(preorder, startPre + 1, startPre + i - startIn, inorder, startIn, i - 1); root.rightNode = rebultTree(preorder, startPre + i - startIn + 1, endPre, inorder, i + 1, endIn); } } return root; }
// 測試 public static void main(String[] args) { int[] preorder = { 1, 2, 4, 7, 3, 5, 6, 8 }; int[] inorder = { 4, 7, 2, 1, 5, 3, 8, 6 }; BinaryTreeNode root = rebultTree(preorder, inorder); //System.out.println(root.leftNode.rightNode.value); root.printList(); } }
package sword_offer; // page 65 二叉樹的下一個節點 // 定義二叉樹類,包含左右子樹、父節點,以及用於查看的函數 class TreeNode { char value; TreeNode left; TreeNode right; TreeNode father; // 中序遍歷輸出查看 public void printList() { if (left != null) left.printList(); System.out.println(value); if (right != null) right.printList(); } } public class Solution8 { public static TreeNode findNext(TreeNode node) { // 有右節點,找到右子樹的最左節點 if (node.right!= null) { node = node.right; while(node.left != null) node = node.left; return node; } // 無右節點,則向上遍歷,直至找到節點爲父節點的左子節點 while(node.father != null) { if (node.father.left == node) return node.father; node = node.father; } return null; }
public static void main(String[] args) { // 創建一個二叉樹節點的數組,並tree[i].value賦值 TreeNode[] tree = new TreeNode[9]; char[] chars = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i'}; for (int i = 0; i < tree.length; i++) { tree[i] = new TreeNode(); tree[i].value = chars[i]; } /* * a * / \ * b c * / \ / \ * d e f g * / \ * h i */ // 左右節點關係 tree[0].left = tree[1]; tree[0].right = tree[2]; tree[1].left = tree[3]; tree[1].right = tree[4]; tree[2].left = tree[5]; tree[2].right = tree[6]; tree[4].left = tree[7]; tree[4].right = tree[8]; // 父節點關係 tree[1].father = tree[0]; tree[2].father = tree[0]; tree[3].father = tree[1]; tree[4].father = tree[1]; tree[5].father = tree[2]; tree[6].father = tree[2]; tree[7].father = tree[4]; tree[8].father = tree[4]; tree[0].printList(); } }
package sword_offer; // page 68 兩個棧實現隊列 import java.util.Stack; // 隊列類,包含兩個棧、兩個操做隊列的方法 class Queue <T>{ Stack<T> stack1 = new Stack<>(); Stack<T> stack2 = new Stack<>();
// 插入節點 public void appendTail(T element) { stack1.push(element); }
// 刪除節點 public T deleteHead(){ if (stack2.isEmpty()) { while(!stack1.isEmpty()) { T data = stack1.pop(); stack2.push(data); } } // 爲空時,輸出異常 if (stack2.isEmpty()) throw new IllegalArgumentException("queue is empty"); return stack2.pop(); } }
public class Solution9 { // 測試 public static void main(String[] args) { Queue<Integer> queue = new Queue<>(); queue.appendTail(1); queue.appendTail(2); queue.appendTail(3); System.out.println(queue.deleteHead()); System.out.println(queue.deleteHead()); queue.appendTail(4); System.out.println(queue.deleteHead()); System.out.println(queue.deleteHead()); System.out.println(queue.deleteHead()); } }
$$f\left( n \right) = \left\{ \begin{array}{l}
0\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;n = 0\\
1\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;n = 1\\
f\left( {n - 1} \right) + f\left( {n - 2} \right)\;\;\;\;n > 1
\end{array} \right.$$緩存
package sword_offer; // page 74 斐波那契數列 public class Solution10 { // 得到fibonacci(n) public static long fibonacci(int n) { if (n <= 1) return n; long fibMinusOne = 0; long fibMinusTwo = 1; long fib = 0; for (int i = 2; i <= n; i++) { fib = fibMinusOne + fibMinusTwo; fibMinusOne = fibMinusTwo; fibMinusTwo = fib; } return fib; }
// 測試函數 public static void main(String[] args) { System.out.println(fibonacci(6)); } }
package sword_offer; // page 82 旋轉數組的最小數字 public class Solution11 { public static int min(int[] arr) { int start = 0; int end = arr.length - 1; if (arr[start] < arr[end]) // 旋轉數組是其自己的情形 return arr[start]; while (start <= end) { int mid = start + ((end - start) >> 1); if (arr[mid] == arr[start] && arr[mid] == arr[end]) // 考慮順序查找情形 return min(arr, start, end); if (arr[mid] >= arr[start]) start = mid; else end = mid; if (start == end - 1) break; } return arr[end]; }
// 特殊情形下的順序查找 private static int min(int[] arr, int start, int end) { int result = arr[start]; for (int i = start + 1; i <= end; i++) if (arr[i] < result) result = arr[i]; return result; }
// 測試 public static void main(String[] args) { int[] arr = { 4, 5, 1, 2, 3 }; int[] arr2 = { 1, 0 , 1, 1, 1 }; System.out.println(min(arr)); System.out.println(min(arr2)); } }
package sword_offer; public class Solution12 { public static boolean hasPath(char[][] matrix, String str) { int rows = matrix.length; int cols = matrix[0].length; boolean[][] visited = new boolean[rows][cols]; int pathLength = 0; for (int row = 0; row < rows; row++) { for (int col = 0; col < cols; col++) { if (hasPathCore(matrix, rows, cols, row, col, str, pathLength, visited)) return true; } } return false; } // 上下左右遞歸搜索 private static boolean hasPathCore(char[][] matrix, int rows, int cols, int row, int col, String str, int pathLength, boolean[][] visited) { boolean hasPath = false; if (pathLength > str.length() - 1) return true; if (row >= 0 && row < rows && col >= 0 && col < cols && matrix[row][col] == str.charAt(pathLength) && !visited[row][col]) { ++pathLength; visited[row][col] = true; hasPath = hasPathCore(matrix, rows, cols, row + 1, col, str, pathLength, visited) || hasPathCore(matrix, rows, cols, row, col + 1, str, pathLength, visited) || hasPathCore(matrix, rows, cols, row - 1, col, str, pathLength, visited) || hasPathCore(matrix, rows, cols, row, col - 1, str, pathLength, visited); if (!hasPath) { --pathLength; visited[row][col] = false; } } return hasPath; } // 測試 public static void main(String[] args) { char[][] matrix = { { 'a', 'b', 't', 'g' }, { 'c', 'f', 'c', 's' }, { 'j', 'd', 'e', 'h' } }; String str = "bfc"; System.out.println(hasPath(matrix, str)); } }
package sword_offer; // page 96 剪繩子 import java.lang.Math; public class Solution14 { // 動態規劃算法,比較容易想到 public static int maxProduct_s1(int len) { if (len < 2) return 0; if (len == 2) return 1; if (len == 3) return 2; int[] products = new int[len + 1]; products[0] = 0; products[1] = 1; products[2] = 2; products[3] = 3; int max = 0; for (int i = 4; i <= len; i++) { for (int j = 1; j <= i / 2; j++) { int product = products[j] * products[i - j]; if (max < product) max = product; products[i] = max; } } max = products[len]; return max; } // 貪婪算法,很不容易想到 public static int maxProduct_s2(int len) { if (len < 2) return 0; if (len == 2) return 1; if (len == 3) return 2; int times3 = len / 3; if (len - times3 * 3 == 1) times3--; int times2 = (len - times3 * 3) / 2; return (int) (Math.pow(3, times3)) * (int) (Math.pow(2, times2)); } // 測試 public static void main(String[] args) { System.out.println(maxProduct_s1(20)); System.out.println(maxProduct_s2(20)); } }
package sword_offer; //page 100 二進制中1的個數 public class Solution15 { // 方法1:逐位進行與運算 public static int numOfOne_a(int n) { int flag = 1; int count = 0; while (flag != 0) { if ((n & flag) != 0) count++; flag = flag << 1; } return count; } // 方法二:進行n = (n - 1) & n運算,n含有1的位數減1 public static int numOfOne_b(int n) { int count = 0; while (n != 0) { n = (n - 1) & n; count++; } return count; } // 測試 public static void main(String[] args) { System.out.println(Integer.toBinaryString(10000)); System.out.println(numOfOne_a(10000)); System.out.println(numOfOne_b(10000)); } }
package sword_offer; // page 16 數值的整數次方 public class Solution16 { // 冪運算函數,f=x^n,不用考慮大數問題,n可能爲負整數 public static double myPow(double x, int n) { if (n == 0) return 1.0; if (n < 0) return 1.0 / (x * myPow(x, -n - 1));// 避免溢出 double result = myPow(x, n >> 1);// 位運算除2 result *= result; if ((n & 1) == 1)// 位運算判斷奇偶 result *= x; return result; }
// 測試 public static void main(String[] args) { int a = -2147483648; System.out.println(myPow(1.0, a)); System.out.println(myPow(2.0, 4)); System.out.println(myPow(2.0, 3)); } }
package sword_offer; // page 114 打印從1到最大的n位數 public class Solution17 { // 數字排列方法 public static void maxOfND(int n) { if (n < 0) { return; } char[] num = new char[n]; for (int i = 0; i < 10; i++) { num[0] = (char) (i + '0'); printNumRec(num, n, 0); } } // 遞歸部分 public static void printNumRec(char[] num, int n, int index) { if (index == n - 1) { printNum(num); return; } for (int i = 0; i < 10; i++) { num[index + 1] = (char) (i + '0'); printNumRec(num, n, index + 1); } } // 以閱讀習慣輸出 public static void printNum(char[] num) { boolean isBegin0 = true; for (int i = 0; i < num.length; i++) { if (isBegin0 && num[i] != '0') { isBegin0 = false; } if (!isBegin0) { System.out.print(num[i]); } } System.out.println(); } // 測試 public static void main(String[] args) { maxOfND(5); } }
package sword_offer; // page 119 刪除鏈表中的節點 public class Solution18 { public static void deleteNode(ListNode root, ListNode toBeDeleted) { // 要刪的節點是根節點(這種狀況下的操做是無效的,沒真正刪除) if (root.equals(toBeDeleted)) { root = root.next; return; } ListNode tmp = toBeDeleted.next; // 是不是最後一個節點?不是則經過賦值完成刪除,是則經過從前向後遍歷,完成刪除 if (tmp != null) { toBeDeleted.value = tmp.value; if (tmp.next != null) toBeDeleted.next = tmp.next; tmp.next = null; } else { tmp = root; while (!tmp.next.equals(toBeDeleted)) { tmp = tmp.next; } tmp.next = null; } } // 測試 public static void main(String[] args) { ListNode[] ln = new ListNode[6]; // 1 -> 2 -> 3 -> 4 -> 5 -> 6 for (int i = 0; i < ln.length; i++) { ln[i] = new ListNode(); ln[i].value = i; if (i > 0) ln[i - 1].next = ln[i]; } ln[0].printOut(); deleteNode(ln[0], ln[2]); System.out.println("==== after delete ===="); ln[0].printOut(); } }
package sword_offer; // page 21 調整數組順序使奇數位於偶數前面 import java.util.Arrays; public class Solution21 { // 按必定規則調整數組的程序 public static void reorderOddEven(int[] arr) { int start = 0; int end = arr.length - 1; while (start < end) { while (start < end && isOdd(arr[start])) start++; while (start < end && !isOdd(arr[end])) end--; if (start < end) { int tmp = arr[start]; arr[start] = arr[end]; arr[end] = tmp; } } } // 單獨定義一個函數,提升擴展性 private static boolean isOdd(int x) { return (x & 1) == 1; } // 測試 public static void main(String[] args) { int[] arr = { 0, 1, 3, 5, 4, 9 }; reorderOddEven(arr); System.out.println(Arrays.toString(arr)); } }
package sword_offer; // page 134 鏈表中倒數第k個節點 /*class ListNode{ Solution6定義過了 ListNode next = null; int value; }*/ public class Solution22 { public static ListNode findKthTail(ListNode head, int k) { ListNode res = head; int i = 1; while (head.next != null) { i++; head = head.next; if (i > k) res = res.next; } if (i < k || k < 1) throw new IllegalArgumentException("Not exist"); return res; } public static void main(String[] args) { ListNode[] ln = new ListNode[6]; // 1 -> 2 -> 3 -> 4 -> 5 -> 6 for (int i = 0; i < ln.length; i++) { ln[i] = new ListNode(); ln[i].value = i + 1; if (i > 0) ln[i - 1].next = ln[i]; } System.out.println(findKthTail(ln[0], 1).value); } }
package sword_offer; // page 142 反轉鏈表 public class Solution24 { public static ListNode reverseList(ListNode head) { if (head == null) return null; ListNode pre = null; ListNode next = null; while (head != null) { next = head.next; head.next = pre; pre = head; head = next; } return pre; } // 測試 public static void main(String[] args) { ListNode[] ln = new ListNode[6]; // 1 -> 2 -> 3 -> 4 -> 5 -> 6 for (int i = 0; i < ln.length; i++) { ln[i] = new ListNode(); ln[i].value = i + 1; if (i > 0) ln[i - 1].next = ln[i]; } ln[0].printOut(); reverseList(ln[0]); System.out.println("=========反轉先後========"); ln[5].printOut(); } }
package sword_offer; // page 合併兩個排序的鏈表 public class Solution25 { public static ListNode merge(ListNode head1, ListNode head2) { if (head1 == null) return head2; if (head2 == null) return head1; ListNode mergeHead = null; if (head1.value < head2.value) { mergeHead = head1; mergeHead.next = merge(head1.next, head2); } else { mergeHead = head2; mergeHead.next = merge(head1, head2.next); } return mergeHead; } public static void main(String[] args) { int[] arr1 = { 1, 3, 5, 7 }; int[] arr2 = { 2, 4, 6, 10 }; // 定義鏈表1 ListNode[] ln1 = new ListNode[arr1.length]; for (int i = 0; i < ln1.length; i++) { ln1[i] = new ListNode(); ln1[i].value = arr1[i]; if (i > 0) ln1[i - 1].next = ln1[i]; } // 定義鏈表2 ListNode[] ln2 = new ListNode[arr2.length]; for (int i = 0; i < ln1.length; i++) { ln2[i] = new ListNode(); ln2[i].value = arr2[i]; if (i > 0) ln2[i - 1].next = ln2[i]; } merge(ln1[0], ln2[0]).printOut(); } }
package sword_offer; // page 157 二叉樹的鏡像 public class Solution26 { public static void mirrorTree(BinaryTreeNode root) { if (root == null || (root.leftNode == null && root.rightNode == null)) return; BinaryTreeNode tmp = root.leftNode; root.leftNode = root.rightNode; root.rightNode = tmp; if (root.leftNode != null) mirrorTree(root.leftNode); if (root.rightNode != null) mirrorTree(root.rightNode); } // 測試 public static void main(String[] args) { BinaryTreeNode a = new BinaryTreeNode(); BinaryTreeNode b = new BinaryTreeNode(); BinaryTreeNode c = new BinaryTreeNode(); BinaryTreeNode d = new BinaryTreeNode(); BinaryTreeNode e = new BinaryTreeNode(); BinaryTreeNode f = new BinaryTreeNode(); BinaryTreeNode g = new BinaryTreeNode(); a.leftNode = b; a.rightNode = c; b.leftNode = d; b.rightNode = e; c.leftNode = f; c.rightNode = g; a.value = 8; b.value = 6; c.value = 10; d.value = 5; e.value = 7; f.value = 9; g.value = 11; a.printList(); mirrorTree(a); System.out.println("=========="); // 中序輸出也是對稱的 a.printList(); } }
package sword_offer; // page 159 對稱的二叉樹 public class Solution28 { public static boolean isSymmetrical(BinaryTreeNode root) { return isSymmetrical(root, root); } private static boolean isSymmetrical(BinaryTreeNode root1, BinaryTreeNode root2) { if (root1 == null && root2 == null) return true; if (root1 == null || root2 == null) return false; if (root1.value != root2.value) return false; return isSymmetrical(root1.leftNode, root2.rightNode) && isSymmetrical(root1.rightNode, root2.leftNode); } // 測試 public static void main(String[] args) { BinaryTreeNode a = new BinaryTreeNode(); BinaryTreeNode b = new BinaryTreeNode(); BinaryTreeNode c = new BinaryTreeNode(); BinaryTreeNode d = new BinaryTreeNode(); BinaryTreeNode e = new BinaryTreeNode(); BinaryTreeNode f = new BinaryTreeNode(); BinaryTreeNode g = new BinaryTreeNode(); a.leftNode = b; a.rightNode = c; b.leftNode = d; b.rightNode = e; c.leftNode = f; c.rightNode = g; a.value = 8; b.value = 6; c.value = 6; d.value = 5; e.value = 7; f.value = 7; g.value = 5; System.out.println(isSymmetrical(a)); } }
package sword_offer; // page 171 從上到下打印二叉樹 import java.util.LinkedList; import java.util.Queue; public class Solution32 { public static void printTreeTopToBottom(BinaryTreeNode root) { if (root == null) return; // 建立個隊列queue Queue<BinaryTreeNode> queue = new LinkedList<>(); queue.add(root); while (!queue.isEmpty()) { // 彈出後打印,再插入該節點的左節點、右節點 BinaryTreeNode node = queue.poll(); System.out.println(node.value); if (node.leftNode != null) queue.add(node.leftNode); if (node.rightNode != null) queue.add(node.rightNode); } } // 測試 public static void main(String[] args) { BinaryTreeNode a = new BinaryTreeNode(); BinaryTreeNode b = new BinaryTreeNode(); BinaryTreeNode c = new BinaryTreeNode(); BinaryTreeNode d = new BinaryTreeNode(); BinaryTreeNode e = new BinaryTreeNode(); BinaryTreeNode f = new BinaryTreeNode(); BinaryTreeNode g = new BinaryTreeNode(); a.leftNode = b; a.rightNode = c; b.leftNode = d; b.rightNode = e; c.leftNode = f; c.rightNode = g; a.value = 8; b.value = 6; c.value = 10; d.value = 5; e.value = 7; f.value = 9; g.value = 11; printTreeTopToBottom(a); } }
package sword_offer; //page 205 數組中出現次數超過一半的數字 public class Solution39 { // 方法1:利用數組中位數的特性以及快速排序的思想 public static int moreThanHalfNum_s1(int[] arr) { int mid = arr.length >> 1; int start = 0; int end = arr.length - 1; int index = partition(arr, start, end); while (index != mid) { if (index > mid) { end = index - 1; index = partition(arr, start, end); } else { start = index + 1; index = partition(arr, start, end); } } int result = arr[mid]; return result; } // 快排肯定一個索引 public static int partition(int[] arr, int start, int end) { int mid = end; end = end - 1; while (start < end) { while (start < end && arr[start] < arr[mid]) start++; while (start < end && arr[end] >= arr[mid]) end--; swap(arr, start, end); } if (arr[start] >= arr[mid]) { swap(arr, start, mid); } else start++; return start; } // 交換 private static void swap(int[] arr, int x, int y) { int temp = arr[x]; arr[x] = arr[y]; arr[y] = temp; } // 方法2:基於數組特色 public static int moreThanHalfNum_s2(int[] arr) { int result = arr[0]; int times = 1; for (int i = 1; i < arr.length; i++) { if (times == 0) { result = arr[i]; times = 1; } else if (arr[i] == result) times++; else times--; } return result; } // 測試 public static void main(String[] args) { int[] arr = { 3, 3, 1, 1, 1, 4, 4, 1 }; System.out.println(moreThanHalfNum_s1(arr)); System.out.println(moreThanHalfNum_s2(arr)); } }
package sword_offer; // page 209 最小的k個數 import java.util.Arrays; public class Solution40 { public static void getLeastNums(int[] arr, int k) { int start = 0; int end = arr.length - 1; int index = partition(arr, start, end); while (index != k - 1) { // 注意索引與個數的關係,注意差1錯誤 if (index < k - 1) { start = index + 1; index = partition(arr, start, end); } else { end = index - 1; index = partition(arr, start, end); } } } // 快排肯定一個索引 public static int partition(int[] arr, int start, int end) { int mid = end; end = end - 1; while (start < end) { while (start < end && arr[start] < arr[mid]) start++; while (start < end && arr[end] >= arr[mid]) end--; swap(arr, start, end); } if (arr[start] >= arr[mid]) { swap(arr, start, mid); } else start++; return start; } // 交換 private static void swap(int[] arr, int x, int y) { int temp = arr[x]; arr[x] = arr[y]; arr[y] = temp; } // 測試 public static void main(String[] args) { int[] arr = { 3, 3, 1, 1, 1, 4, 4, 1 }; getLeastNums(arr, 1); System.out.println(Arrays.toString(arr)); } }
package sword_offer; // page 218 連續子數組的最大和 public class Solution42 { public static int maxSub(int[] arr) { int maxSum = 0, tempSum = 0; for (int i = 0; i < arr.length; i++) { tempSum += arr[i]; if (tempSum > maxSum) maxSum = tempSum; else if (tempSum < 0) tempSum = 0; } return maxSum; } // 測試 public static void main(String[] args) { int[] arr = { 1, -2, 3, 10, -4, 7, 2, -5 }; System.out.println(maxSub(arr)); } }
package sword_offer; // page 221 1~n整數中出現1的個數 public class Solution43 { // 計算整數n中含有1的個數 private static int countOne(int n) { int count = 0; while (n != 0) { if (n % 10 == 1) count++; n = n / 10; } return count; } // 計算整數1~n中含有1的個數 public static int countTotalOne(int n) { int count = 0; for (int i = 1; i <= n; i++) { count += countOne(i); } return count; } // 測試 public static void main(String[] args) { System.out.println(countTotalOne(100)); } }
package sword_offer; // page 225 數組序列中的某一位數字 import java.lang.Math; public class Solution44 { // 這裏沒采用原文中將程序拆分爲幾個小程序的形式,考慮到個位數的特殊性,直接在前面處理 public static int digitAtIndex(int index) { if (index < 10) return index; int n = 2; //位數 index = index - 10;//初始化位位數爲1的個數 while(true) { int numsOfN = 9 * (int) Math.pow(10, n - 1); //n位的個數 if (index < numsOfN * n) { //是否在這個區間 int number = (int) Math.pow(10, n - 1) + index / n; //對應的整數 int indexFromRight = n - index % n; //從右邊數第幾位 for (int i = 1; i < indexFromRight; i++) { //找到那一位 number /= 10; } return number % 10; } index -= numsOfN * n; n++; } } // 測試 public static void main(String[] args) { System.out.println(digitAtIndex(1001)); } }
package sword_offer; // page 227 把數組排成最小的數 public class Solution45 { public static void printMin(int[] arr) { printMinNumber(arr, 0, arr.length - 1); for (int i : arr) System.out.print(i); System.out.println(); } // 注意快排的不一樣形式 public static void printMinNumber(int[] arr, int left, int right) { if (left < right) { int main_number = arr[right]; int small_cur = left; for (int j = left; j < right; j++) { if (isSmall(String.valueOf(arr[j]), String.valueOf(main_number))) { // 小於時交換 int temp = arr[j]; arr[j] = arr[small_cur]; arr[small_cur] = temp; small_cur++; } } arr[right] = arr[small_cur]; arr[small_cur] = main_number; printMinNumber(arr, 0, small_cur - 1); printMinNumber(arr, small_cur + 1, right); } } // 判斷字符串是否知足mn < nm public static boolean isSmall(String m, String n) { String left = m + n; String right = n + m; return left.compareTo(right) < 0; // 也能夠重寫函數 } // 測試 public static void main(String[] args) { int arr[] = { 3, 1, 2, 11 }; printMin(arr); } }
package sword_offer; // page 240 醜數 public class Solution49 { // 方法1:一個一個判斷是不是醜數,直到找到目標值 public static int getUglyNum_s1(int index) { int count = 0; int num = 0; while (count < index) { num++; // 把num++放在循環的開頭而非末尾 if (isUglyNum(num)) count++; } return num; } // 判斷是不是醜數 private static boolean isUglyNum(int n) { while (n % 2 == 0) n /= 2; while (n % 3 == 0) n /= 3; while (n % 5 == 0) n /= 5; return n == 1 ? true : false; } // 方法2:額外定義個緩存數組,將醜數存下來,注意裏面使用3個變量跟蹤的技巧 public static int getUglyNum_s2(int index) { int[] uglyNums = new int[index]; uglyNums[0] = 1; int pm2 = 0; int pm3 = 0; int pm5 = 0; int nextIndex = 1; while (nextIndex < index) { int min = minNum(uglyNums[pm2] * 2, uglyNums[pm3] * 3, uglyNums[pm5] * 5); uglyNums[nextIndex] = min; while (uglyNums[pm2] * 2 <= min) pm2++; while (uglyNums[pm3] * 3 <= min) pm3++; while (uglyNums[pm5] * 5 <= min) pm5++; nextIndex++; // 這裏將nextIndex++放到末尾,由於是索引 } return uglyNums[nextIndex - 1]; } // 三個數中的最小值 public static int minNum(int x, int y, int z) { int min = x < y ? x : y; min = z < min ? z : min; return min; } // 測試 public static void main(String[] args) { long t0 = System.currentTimeMillis(); System.out.println("方法1計算的第1500個醜數:" + getUglyNum_s1(1500)); long t1 = System.currentTimeMillis(); System.out.println("方法1耗時:" + (t1 - t0) + "ms"); System.out.println("方法2計算的第1500個醜數:" + getUglyNum_s2(1500)); long t2 = System.currentTimeMillis(); System.out.println("方法2耗時:" +(t2 - t1) + "ms"); } }
package sword_offer; // 第一個只出現一次的字符 public class Solution50 { // 將ASCII做爲數組下標,存儲出現的個數,至關於實現了一個簡單的哈希表,也能夠直接用map public static char firstRepeatchar(String str) { int maxNum = 256; char target = str.charAt(0); int[] arr = new int[maxNum]; for (int i = 0; i < str.length(); i++) arr[(int)(str.charAt(i))]++; for (int i = 0; i < str.length(); i++) { if (arr[(int)(str.charAt(i))] == 1){ target = str.charAt(i); break; } } return target; } // 相關題目:刪除重複的字符 public static String delRepeatchar(String str) { int maxNum = 256; StringBuilder strb = new StringBuilder(); int[] arr = new int[maxNum]; for (int i = 0; i < str.length(); i++) { if (arr[(int)(str.charAt(i))] == 0) { arr[(int)(str.charAt(i))]++; strb.append(str.charAt(i)); } } return strb.toString(); } // 相關題目:從第一個字符串中刪除在第二個字符串中出現過的全部字符 public static String delCharInStr2(String str1, String str2) { int maxNum = 256; StringBuilder strb = new StringBuilder(); int[] arr = new int[maxNum]; for (int i = 0; i < str2.length(); i++) { if (arr[(int)(str2.charAt(i))] == 0) { arr[(int)(str2.charAt(i))]++; } } for (int i = 0; i < str1.length(); i++) { if (arr[(int)(str1.charAt(i))] == 0) { // str1中的字符對應的哈希表在arr中不存在 strb.append(str1.charAt(i)); } } return strb.toString(); } // 相關題目:判斷是不是變位詞 public static boolean isAnagram(String str1, String str2) { int maxNum = 256; int[] arr = new int[maxNum]; if (str1.length() != str2.length()) return false; for (int i = 0; i < str1.length(); i++) { arr[(int)(str1.charAt(i))]++; arr[(int)(str2.charAt(i))]--; } for (int i = 0; i < arr.length; i++) { if (arr[i] != 0) return false; } return true; } // 測試 public static void main(String[] args) { System.out.println(firstRepeatchar("abaccdeff")); System.out.println(delRepeatchar("abaccdeff")); System.out.println(delCharInStr2("We are student.", "aeiou")); System.out.println(isAnagram("silent", "listen")); } }
package sword_offer; // page 271 二叉樹的深度 public class Solution55 { public static int treeDepth(BinaryTreeNode root) { // 基準狀況 if (root == null) return 0; // 左子樹深度 int nleft = treeDepth(root.leftNode); // 右子樹深度 int nright = treeDepth(root.rightNode); return (nleft > nright) ? nleft + 1 : nright + 1; } // 測試 public static void main(String[] args) { BinaryTreeNode a = new BinaryTreeNode(); BinaryTreeNode b = new BinaryTreeNode(); BinaryTreeNode c = new BinaryTreeNode(); a.rightNode = b; b.leftNode = c; System.out.println(treeDepth(a)); } }
package sword_offer; // page 307 求1+2+...+n public class Solution64 { public static int sum1(int n) { // 利用與運算的性質,實現了遞歸的終止 boolean flag = (n > 0) && (n = n + sum1(n - 1)) > 0; // (n == 0) || (n = n + sum1(n - 1)) > 0; return n; } // 測試 public static void main(String[] args) { System.out.println(sum1(2)); } }