1. 程式人生 > >《劍指offer》面試題答案彙總(Java版)

《劍指offer》面試題答案彙總(Java版)

面試題2:實現Singleton模式 (1)餓漢模式 public class Singleton{ private static Singleton instance = new Singleton(); private Singleton(){} public static Singleton getInstance(){ return instance; } }需要使用static來保證是類層面的 (2)懶漢模式 public class Singleton{ private static Singleton instance = new Singleton(); private Singleton(){} public static synchronized Singleton getInstance(){ if(instance == null){ instance = new Singleton(); } return instance; } } (3)雙重鎖 public class Singleton{ private static Singleton instance = new Singleton(); private Singleton(){} public static Singleton getInstance(){ if(instance == null){ synchronized(Singleton.class){ if(instance == null){ instance = new Singleton(); } } } return instance; } } (4)靜態內部類 public class Singleton{ private static class SingletonHandler{ public static Singleton newInstance = new Singleton(); } private Singleton(){} public static Singleton getInstance(){ return SingletonHandler.newInstance; } } 面試題3:二維陣列中的查詢 陣列的每一行按照從左到右遞增,從上到下遞增排列,給定一個數和陣列,判斷這個數是否在陣列中出現。 時間複雜度:O(max(m,n)),空間複雜度:O(1) 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(isIn(arr,7)); } public static boolean isIn(int[][] a,int num){ if(a != null){ int m = a.length; int n = a[0].length; int i = 0; while(i < m && n > 0){ if(a[i][n-1] == num){ return true; }else if(a[i][n-1] > num){ n--; }else i++; } } return false; } 面試題4:替換空格 時間複雜度:O(n),空間複雜度 public static void main(String[] args) { char[] arr = {'w','e',' ','a','r','e',' ','h','a','p','p','y'}; String str = new String(arr); System.out.println(""+replace2(str)); System.out.println(""+replace3(arr)); } public static String replace3(char[] arr){ if(arr == null || arr.length == 0){ return ""; } int len = arr.length; int count = 0; for(int i = 0; i < len; i++){ if(arr[i] == ' ') count++; } int nlen = len + count*2; char[] res = new char[nlen]; int p1 = len - 1; int p2 = nlen - 1; while(p1 >= 0 && p2 >= 0){ if(arr[p1] == ' '){ res[p2--] = '0'; res[p2--] = '2'; res[p2--] = '%'; }else{ res[p2--] = arr[p1]; } p1--; } return new String(res); } public static String replace2(String str){ if(str == null || str.length() == 0){ return ""; } int len = str.length(); StringBuilder res = new StringBuilder(); for(int i = 0; i < len; i++){ if(str.charAt(i) == ' '){ res.append("%20"); }else{ res.append(str.charAt(i)); } } return res.toString(); }/*當使用split的時候要考慮可能會出現幾個空格相連的情況,還有全是空格的情況,這時候用split其實就不可取了*/ 面試題5:從尾到頭列印連結串列 時間複雜度:O(n),空間複雜度:O(n) public static void printList(ListNode head){ if(head == null) return; Stack<ListNode> stack = new Stack<ListNode>(); while(head != null){ stack.push(head); head = head.next; } while(!stack.isEmpty()){ System.out.print(stack.pop().val+" "); } }/*當連結串列很長的時候用遞迴可能會棧溢位,所以還是使用棧的方法魯棒性更好*/ public static void printList2(ListNode head){ if(head == null) return; if(head.next != null) printList2(head.next); System.out.print(head.val+" "); } 面試題6:重建二叉樹(樹的遍歷) 時間複雜度:O(n)?,空間複雜度:O(1) public static TreeNode reBuild(int[] preorder, int[] inorder){ if(preorder == null || inorder == null || inorder.length <= 0 || inorder.length != preorder.length){ return null; } int len = inorder.length; return constructTree(preorder,inorder,0,len-1,0,len-1); } public static TreeNode constructTree(int[] preorder, int[] inorder, int pstart, int pend, int istart, int iend){ if(pstart > pend || istart > iend) return null; int rootVal = preorder[pstart]; TreeNode root = new TreeNode(rootVal); int index = -1; for(int i = istart; i <= iend; i++){ if(inorder[i] == rootVal){ index = i; break; } } int cur = index - istart; TreeNode left = constructTree(preorder, inorder, pstart+1, pstart + cur, istart, index - 1); TreeNode right = constructTree(preorder, inorder, pstart + cur + 1, pend, index + 1, iend); root.left = left; root.right = right; return root; } 面試題7:用兩個棧實現佇列 public class stackToQueue7 { Stack<Integer> stack1 = new Stack<Integer>(); Stack<Integer> stack2 = new Stack<Integer>(); public int pop(){ int res = -1; if(!stack2.isEmpty()){ res = stack2.pop(); }else{ while(!stack1.isEmpty()){ int tmp = stack1.pop(); stack2.push(tmp); } if(!stack2.isEmpty()){ res = stack2.pop(); } } return res; } public void push(int num){ stack1.push(num); } } 用兩個佇列實現一個棧: public class queueToStack7 { Queue<Integer> queue1 = new LinkedList<Integer>(); Queue<Integer> queue2 = new LinkedList<Integer>(); public int pop() throws Exception{ if(queue1.isEmpty() && queue2.isEmpty()){ throw new Exception("stack is empty"); } if(queue1.isEmpty()){ while(queue2.size() > 1){ queue1.add(queue2.poll()); } return queue2.poll(); } if(queue2.isEmpty()){ while(queue1.size() > 1){ queue2.add(queue1.poll()); } return queue1.poll(); } return (Integer) null; } public void push(int num){ if(queue1.isEmpty() && queue2.isEmpty()){ queue1.add(num); return; } if(!queue1.isEmpty()){ queue1.add(num); return; } if(!queue2.isEmpty()){ queue2.add(num); return; } } } 面試題8:旋轉陣列的最小數字(二分查詢) 注意要考慮到陣列是全部順序以及10111順序的陣列等特殊情況。 時間複雜度O(lgn) 注:二分查詢適合於排序或者部分排序陣列的處理 public static int minInSpiral(int[] arr) throws Exception{ if(arr == null || arr.length == 0){ throw new Exception("Error"); } int p1 = 0; int p2 = arr.length - 1; int mid = p1; while(arr[p1] >= arr[p2]){ if(p2 - p1 == 1){ mid = p2; break; } mid = (p1 + p2) / 2; if(arr[p1] == arr[p2] && arr[p1] == arr[mid]){ return getMinorder(arr,p1,p2); } if(arr[p1] <= arr[mid]){ p1 = mid; }else if(arr[p2] >= arr[mid]){ p2 = mid; } } return arr[mid]; } public static int getMinorder(int[] arr,int start, int end){ int tmp = arr[start]; for(int i = start+1; i <= end; i++){ if(arr[i] < tmp) tmp = arr[i]; } return tmp; } 面試題9:斐波那契數列 private static long[] frr = new long[10000]; public static long fabon(int n){ if(n == 0) return 0; if(n == 1) return 1; if(frr[n] != 0) return frr[n]; frr[n] = fabon(n-1) + fabon(n-2); return frr[n]; }/*遞迴的方法,但是效率和空間利用率都很低,且容易棧溢位*/ public static int Fibonacci(int n) { if(n == 0) return 0; if(n == 1) return 1; int a1 = 1; int a2 = 1; for(int i=3;i<=n;i++){ int tmp = a2; a2 = a1 + a2; a1 = tmp; } return a2; }/*時間複雜度是O(n),但是很使用*/ 拓展:青蛙跳臺階(一次可以跳1階或者2階,f(n) = f(n-1)+f(n-2))就是菲波那切數列; 如果改成一次可以跳任意階,則可以用數學歸納法得到f(n) = 2^n。 用2*1的矩形去覆蓋大矩形也是菲波那切數列。 面試題10:二進位制中1的個數 public int NumberOf1(int n) { int res = 0; while( n != 0){ res++; n = n & (n -1); } return res; } 相似的問題還有:判斷一個數是否是2的整數次方(就用n&(n-1)); 判斷兩個整數m和n,其中一個需要變化多少位才能變成另一個數,先異或兩個數,然後求異或結果中有多少個1。 面試題11:數值的整數次方 注意double是不精確的值,不能用==判斷與0的關係 public static double getPow(double base, int exp) throws Exception{ if(equal(base,0.0) && exp < 0) throw new Exception("0 error"); if(exp == 0) return 1.0; int tmp = 0; if(exp < 0){ tmp = exp; exp = - exp; } double res = getPowtmp(base,exp); if(tmp != 0){ res = 1.0 / res; } return res; } public static double getPowtmp(double base, int exp){ if(exp == 0) return 1.0; if(exp == 1) return base; double result = getPowtmp(base,exp >> 1); result *= result; if((exp & 0x1) == 1){ result *= base; } return result; } public static boolean equal(double d1, double d2){ if(d1 - d2 > -0.0000001 && d1 - d2 < 0.0000001){ return true; } return false; } 面試題12:列印1到最大的n位數 public static void printN(int n){ if(n <= 0) return; char[] number = new char[n+1]; for(int i = 0; i < n+1; i++){ number[i] = '0'; } while(!increment(number)){ sprint(number); } } public static void sprint(char[] arr){ int len = arr.length; boolean isStart = false; for(int i = 0; i < len; i++){ if(!isStart && arr[i] != '0') isStart = true; if(isStart){ System.out.print(arr[i]); } } } public static boolean increment(char[] arr){ boolean res = false; int cre = 1; int len = arr.length; for(int i = len - 1; i >= 0; i--){ int sum = arr[i] - '0' + cre; if(sum >= 10){ if(i == 0) res = true; else{ sum -= 10; cre = 1; arr[i] = (char) ('0' + sum); } }else{ arr[i] = (char)('0' + sum); break; } } return res; } 上面是常規解法,實際上還可以把此題看作是n個從0到9的全排列問題,只是在前面的0不列印。 public static void printN2(int n){ if(n <= 0) return; char[] number = new char[n+1]; for(int i = 0; i < n+1; i++){ number[i] = '0'; } for(int i = 0; i < 10; i++){ number[0] = (char)(i + '0'); printRe(number,n,0); } } public static void printRe(char[] arr,int len, int index){ if(index == len - 1){ sprint(arr); return; } for(int i = 0; i < 10; i++){ arr[index+1] = (char)(i + '0'); printRe(arr,len,index+1); } } 面試題13:在O(1)時間刪除連結串列節點 將待刪除結點的下一個結點的值賦給待刪除的結點,然後刪除一個結點,就達到了刪除結點的目的。 找到合適的演算法之後,還有幾種情況需要考慮: 1)正常, 多個結點,刪除的不是尾結點。 2)只有一個結點,刪除尾結點(也是頭結點) 3)有多個結點,刪除尾結點 (注意一種特殊情況就是要刪除的點不在連結串列中) public static void deleteOne(ListNode head,ListNode del){ if(head == null || del == null) return; if(del.next != null){ del.val = del.next.val; del.next = del.next.next; }else if(del == head){ head = null; }else{ ListNode tmp = head; while(tmp.next != del) tmp = tmp.next; tmp.next = null; } } 面試題14:調整陣列順序使基數位於偶數前面 public static void changeOrder(int[] arr){ if(arr == null) return; int len = arr.length; int start = 0; int end = len - 1; while(start < end){ while(start < end && !isEven(arr[start])){ start++; } while(start < end && isEven(arr[end])) end--; if(start < end){ int tmp = arr[start]; arr[start] = arr[end]; arr[end] = tmp; } } } public static boolean isEven(int n){ if((n & 1) == 0){ return true; } return false; } 面試題15:連結串列中的倒數第K個結點 public static ListNode getKth(ListNode head,int k){ if(head == null || k <= 0) return null; ListNode pre = head; for(int i = 0; i < k; i++){ pre = pre.next; if(pre == null) return pre; } ListNode cur = head; while(pre != null){ pre = pre.next; cur = cur.next; } return cur; } 面試題16:反轉連結串列 public static ListNode reverse(ListNode head){ ListNode pre = null; ListNode cur = head; ListNode tmp = null; while(cur != null){ tmp = cur.next; cur.next = pre; pre = cur; cur = tmp; } return pre; } 面試題17:合併兩個排序的連結串列 public static ListNode mergeTwo(ListNode list1,ListNode list2){ if(list1 == null) return list2; if(list2 == null) return list1; ListNode nHead = null; if(list1.val >= list2.val){ nHead = list1; list1 = list1.next; }else{ nHead = list2; list2 = list2.next; } ListNode p = nHead; while(list1 != null && list2 != null){ if(list1.val >= list2.val){ p.next = list1; list1 = list1.next; }else{ p.next = list2; list2 = list2.next; } p = p.next; } p.next = list1 == null ? list2 : list1; return nHead; } 面試題18:樹的子結構(樹的遍歷) public static boolean isSubtr(TreeNode pRoot,TreeNode sRoot){ if(pRoot == null || sRoot == null) return false; return isSubImpl(pRoot,sRoot); } public static boolean isSubImpl(TreeNode pRoot,TreeNode sRoot){ if(sRoot == null) return true; if(pRoot == null) return false; boolean res = false; if(pRoot.val == sRoot.val){ res = isSubImpl(pRoot.left,sRoot.left) && isSubImpl(pRoot.right,sRoot.right); } if(!res){ res = isSubImpl(pRoot.left,sRoot) || isSubImpl(pRoot.right,sRoot); } return res; } 面試題19:二叉樹的映象 public static void mirror(TreeNode root){ if(root == null || (root.left == null && root.right == null)) return ; TreeNode tmp = root.left; root.left = root.right; root.right = tmp; if(root.left != null){ mirror(root.left); } if(root.right != null) mirror(root.right); } 面試題20:順時針列印矩陣 public static void printCircle(int[][] matrix){ if(matrix == null) return; int rows = matrix.length; int cols = matrix[0].length;

相關推薦

offer試題答案彙總Java

面試題2:實現Singleton模式 (1)餓漢模式 public class Singleton{ private static Singleton instance = new Singleton(); private Singleton(){}

offer之變態跳臺階Java實現

變態跳臺階 NowCoder 題目描述: 一隻青蛙一次可以跳上1級臺階,也可以跳上2級……它也可以跳上n級。求該青蛙跳上一個n級的臺階總共有多少種跳法。 ###解題思路: 關於本題,前提是n個臺階會有一次n階的跳法。分析如下: f(1) = 1 f(2)

Offer試題15Java:鏈表中倒數第K個結點

head 計數器 easy sta 相同 ret white style 輸出 題目: 輸入一個鏈表。輸出該鏈表中倒數第k哥結點。 為了符合大多數人的習慣,本題從1開始計數。即鏈表的尾結點是倒數第1個結點。 比如一個鏈表有6個結點。從頭結點開始它們的值依次是1。2。

Offer試題43Java:n個骰子的點數

pac pos max mod ins pri class pro bili 題目:把n個骰子仍在地上。全部骰子朝上一面的點數之和為s,輸入n,打印出s的全部可能的值出現的概率。 解法一:基於遞歸求骰子的點數,時間效率不夠高 如今我們考慮怎樣統計每個點數出現的次數。要向

offer試題9:用兩個棧實現佇列兩個佇列模擬棧

 題目描述: 用兩個棧來實現一個佇列,完成佇列的Push和Pop操作。 佇列中的元素為int型別。 思路一:有點死腦筋,每次pop後都預設下次是push操作,,,,。233主要是由於沒把握好兩個棧模擬時入隊和出隊的時機。考慮stack1和stack2的大小和入隊出隊的關係即可改

offer試題三 :陣列中重複的數字 google 面試

目錄 參考部落格: 題目一:找出陣列中重複的數字 思路一 思路二 題目二:不修改陣列找出重複的數字 測試: 牛客:  牛客高贊(和思路二類似都是hash對映,網友思路真是腦洞大開,這裡相關溢位問題考慮的只有~(1<<31)>>1,

offer試題7:重建二叉樹java實現

題目:輸入某二叉樹的前序遍歷和中序遍歷的結果,請重建該二叉樹,假設輸入的前序遍歷和中序遍歷的結果都不含重複的數字。例如:輸入前序遍歷序列{1,2,4,7,3,5,6,8}和中序遍歷序列{4,7,2,1,5,3,8,6}則重建二叉樹:其中二叉樹的定義如下:  * publi

offer試題13:機器人的運動範圍java

題目:地上有一個m行n列的方格。一個機器人從座標(0,0)的格子開始移動,他每次可以向上,下,左,右移動一格。但不能進入行座標和列座標的數位之和大於k的格子。例如,當K=18時,機器人能夠進入方格(35,37),因為3+5+3+7 =18,但是它不能進入方格(35,38),

offer試題14:剪繩子Java 實現)

題目:給你一根長度位n的繩子,請把繩子減成m段(m,n都是整數,n>1並且m>1),每段繩子的長度記為k[0],k[1],k[2],....k[m]。請問k[0] ✖️k[1]✖️k[2]....✖️k[m]可能的最大乘積是多少?例如:當繩子的長度是8時,我們把

offer試題30:包含min函式的棧Java實現

題目:定義棧的資料結構,請在該型別中實現一個能夠得到棧的最小元素的min函式,在該棧中,呼叫min,push,及pop的時間複雜度都為O(1)。 直接上程式碼: import java.util.Stack; public class Solution {

Offer試題40Java:陣列出現一次的數字

題目:一個整型數組裡除了兩個數字之外,其他的數字都出現了兩次。 * 請些程式找出這兩個只出現一次的數字。要求時間複雜度為O(n),空間複雜度為O(1) 例如輸入陣列{2,4,3,6,3,2,5,5},

Offer試題10Java:二進位制中的1的個數

題目:請實現一個函式,輸入一個整數,輸出該數二進位制表示中1的個數。例如把9表示成二進位制是1001,有2位是1,因此如果輸入9,該函式輸出2. 1、可能引起死迴圈的解法 這是一道很基本的考察二進位制

Offer試題39Java:二叉樹的深度

題目:輸入一棵二叉樹的根節點,求該數的深度。從根節點到葉結點依次進過的結點(含根,葉結點)形成樹的一條路徑,最長路徑的長度為樹的深度。 例如,如下圖的二叉樹的深度為4,因為它從根節點到葉結點的最長的路徑包含4個結點(從根結點1開始,經過2和結點5,最終到達葉結點7) 我們

Offer試題36Java:陣列中的逆序對

題目:在陣列中的兩個數字如果前面一個數字大於後面的數字,則這兩個數字組成一個逆序對。輸入一個數組,求出這個陣列中的逆序對的總數 例如在陣列{7,5,6,4}中,一共存在5對逆序對,分別是{7,6},{7,5},{7,4},{6,4},{5,4}。 看到這個題目,我們的第一反

offer試題8:二叉樹的下一個節點Java 實現

題目:給定一個二叉樹和其中的一個節點,如何找出中序遍歷序列的下一個節點?樹中的節點除了左右子節點外,還包含父節點。 思路: 節點分為有右子樹和沒有右子樹兩大類: 如果節點有右子樹,那麼它的下一個節點為它右子樹的最左節點 如果節點沒有右子樹,也可以分為兩類:(程

offer試題7:重建二叉樹Java實現

題目:輸入某二叉樹的前序遍歷和中序遍歷的結果,請重建出該二叉樹。假設輸入的前序遍歷和中序遍歷的結果中都不含重複的數字。例如輸入前序遍歷序列{1,2,4,7,3,5,6,8}和中序遍歷序列{4,7,2,1,5,3,8,6},則重建二叉樹並返回。 思路:可以把二叉樹分為左右子樹分別構建,前序

offer試題6:從尾到頭列印連結串列Java實現

題目:輸入一個連結串列的頭結點,從尾到頭反過來打印出每個結點的值。 思路:因為要實現從頭到尾遍歷,然後從尾到頭列印,也就是說第一個遍歷到的最後一個列印,最後遍歷到的第一個列印,這很明顯符合棧 “先進後出” 的特點,所以我們可以利用棧來實現這種順序。 測試用例: 功能測試:

offer試題5:替換空格Java 實現

題目: 請實現一個函式,將一個字串中的空格替換成“%20”。例如,當字串為We Are Happy.則經過替換之後的字串為We%20Are%20Happy。 測試用例: 功能測試:輸入的字串包含空格(最前面、中間、最後面、連續多個空格) 邊界測試:輸入的字串沒有空格。

offer試題10:斐波那契數列Java 實現

題目:大家都知道斐波那契數列,現在要求輸入一個整數n,請你輸出斐波那契數列的第n項。 思路:使用遞迴會重複計算,效率較低,可以用迴圈自下到上計算。 測試用例: 功能測試:輸入3、5、10 等。 邊界測試:輸入0、1、2 效能測試:輸入較大的數(如40、50、

offer試題12:矩陣中的路徑Java實現

題目:請設計一個函式,用來判斷在一個矩陣中是否存在一條包含某字串所有字元的路徑。路徑可以從矩陣中的任意一個格子開始,每一步可以在矩陣中向左,向右,向上,向下移動一個格子。如果一條路徑經過了矩陣中的某一個格子,則該路徑不能再進入該格子。 例如 a b c e s f c s a d e e 矩