1. 程式人生 > >演算法經典面試題整理(java實現)

演算法經典面試題整理(java實現)

以下從Java角度解釋面試常見的演算法和資料結構:字串,連結串列,樹,圖,排序,遞迴 vs. 迭代,動態規劃,位操作,概率問題,排列組合,以及一些需要尋找規律的題目。

1. 字串和陣列

字串和陣列是最常見的面試題目型別,應當分配最大的時間。

關於字串,首先需要注意的是和C++不同,Java字串不是char陣列。沒有IDE程式碼自動補全功能,應該記住下面的這些常用的方法。

  1. toCharArray() //獲得字串對應的char陣列
  2. Arrays.sort()  //陣列排序
  3. Arrays.toString(char[] a) //陣列轉成字串
  4. charAt(int
     x) //獲得某個索引處的字元
  5. length() //字串長度
  6. length //陣列大小
  7. substring(int beginIndex)   
  8. substring(int beginIndex, int endIndex)  
  9. Integer.valueOf() //string to integer
  10. String.valueOf() /integer to string  

字串和陣列本身很簡單,但是相關的題目需要更復雜的演算法來解決。比如說動態規劃,搜尋,等等。

2. 連結串列

在Java中,連結串列的實現非常簡單,每個節點Node都有一個值val和指向下個節點的連結next。

  1. class Node {  
  2.     int val;  
  3.     Node next;  
  4.     Node(int x) {  
  5.         val = x;  
  6.         next = null;  
  7.     }  
  8. }  


連結串列兩個著名的應用是棧Stack和佇列Queue。在Java標準庫都都有實現,一個是Stack,另一個是LinkedList(Queue是它實現的介面)。

經典題目:

3. 樹

這裡的樹通常是指二叉樹,每個節點都包含一個左孩子節點和右孩子節點,像下面這樣:

  1. class
     TreeNode{  
  2.     int value;  
  3.     TreeNode left;  
  4.     TreeNode right;  
  5. }  

下面是與樹相關的一些概念:

二叉搜尋樹:左結點 <= 中結點 <= 右結點
平衡 vs. 非平衡:平衡二叉樹中,每個節點的左右子樹的深度相差至多為1(1或0)。
滿二叉樹(Full Binary Tree):除葉子節點以為的每個節點都有兩個孩子。
完美二叉樹(Perfect Binary Tree):是具有下列性質的滿二叉樹:所有的葉子節點都有相同的深度或處在同一層次,且每個父節點都必須有兩個孩子。
完全二叉樹(Complete Binary Tree):二叉樹中,可能除了最後一個,每一層都被完全填滿,且所有節點都必須儘可能想左靠。

經典題目:

4. 圖

圖相關的問題主要集中在深度優先搜尋(depth first search)和廣度優先搜尋(breath first search)。深度優先搜尋很簡單,廣度優先要注意使用queue. 下面是一個簡單的用佇列Queue實現廣度優先搜尋。

  1. publicclass GraphTest {  
  2.     publicstaticvoid breathFirstSearch(GraphNode root, int x){  
  3.         if(root.val == x)  
  4.             System.out.println("find in root");  
  5.         Queue queue = new Queue();  
  6.         root.visited = true;  
  7.         queue.enqueue(root);  
  8.         while(queue.first != null){  
  9.             GraphNode c = (GraphNode) queue.dequeue();  
  10.             for(GraphNode n: c.neighbors){  
  11.                 if(!n.visited){  
  12.                     System.out.print(n + " ");  
  13.                     n.visited = true;  
  14.                     if(n.val == x)  
  15.                         System.out.println("Find "+n);  
  16.                     queue.enqueue(n);  
  17.                 }  
  18.             }  
  19.         }  
  20.     }  
  21. }  


經典題目:

5. 排序

下面是不同排序演算法的時間複雜度,你可以去wiki看一下這些演算法的基本思想。

Algorithm Average Time Worst Time Space
氣泡排序(Bubble sort) n^2 n^2 1
選擇排序(Selection sort) n^2 n^2 1
插入排序(Insertion sort) n^2 n^2
快速排序(Quick sort) n log(n) n^2
歸併排序(Merge sort) n log(n) n log(n) depends

* 另外還有BinSort, RadixSort和CountSort 三種比較特殊的排序。

經典題目:

6. 遞迴 vs. 迭代

對程式設計師來說,遞迴應該是一個與生俱來的思想(a built-in thought),可以通過一個簡單的例子來說明。

問題:

有n步臺階,一次只能上1步或2步,共有多少種走法。

步驟1:找到走完前n步臺階和前n-1步臺階之間的關係。

為了走完n步臺階,只有兩種方法:從n-1步臺階爬1步走到或從n-2步臺階處爬2步走到。如果f(n)是爬到第n步臺階的方法數,那麼f(n) = f(n-1) + f(n-2)。

步驟2: 確保開始條件是正確的。

f(0) = 0;
f(1) = 1;

  1. publicstaticint f(int n){  
  2.     if(n <= 2return n;  
  3.     int x = f(n-1) + f(n-2);  
  4.     return x;  
  5. }  

遞迴方法的時間複雜度是指數級,因為有很多冗餘的計算: f(5)
f(4) + f(3)
f(3) + f(2) + f(2) + f(1)
f(2) + f(1) + f(1) + f(0) + f(1) + f(0) + f(1)
f(1) + f(0) + f(1) + f(1) + f(0) + f(1) + f(0) + f(1)

直接的想法是將遞迴轉換為迭代:

  1. publicstaticint f(int n) {  
  2.     if (n <= 2){  
  3.         return n;  
  4.     }  
  5.     int first = 1, second = 2;  
  6.     int third = 0;  
  7.     for (int i = 3; i <= n; i++) {  
  8.         third = first + second;  
  9.         first = second;  
  10.         second = third;  
  11.     }  
  12.     return third;  
  13. }  


7. 動態規劃

動態規劃是解決下面這些性質類問題的技術:

  1. 一個問題可以通過更小子問題的解決方法來解決,或者說問題的最優解包含了其子問題的最優解
  2. 有些子問題的解可能需要計算多次
  3. 子問題的解儲存在一張表格裡,這樣每個子問題只用計算一次
  4. 需要額外的空間以節省時間

爬臺階問題完全符合上面的四條性質,因此可以用動態規劃法來解決。

  1. publicstaticint[] A = newint[100];  
  2. publicstaticint f3(int n) {  
  3.     if (n <= 2)  
  4.         A[n]= n;  
  5.     if(A[n] > 0)  
  6.         return A[n];  
  7.     else
  8.         A[n] = f3(n-1) + f3(n-2);//store results so only calculate once!
  9.     return A[n];  
  10. }  


經典題目:

8. 位操作

常用位操作符:

OR (|) AND (&) XOR (^) Left Shift (<<) Right Shift (>>) Not (~)
1|0=1 1&0=0 1^0=1 0010<<2=1000 1100>>2=0011 ~1=0

用一個題目來理解這些操作 -

獲得給定數字n的第i位:(i從0計數並從右邊開始)

  1. publicstaticboolean getBit(