演算法經典面試題整理(java實現)
以下從Java角度解釋面試常見的演算法和資料結構:字串,連結串列,樹,圖,排序,遞迴 vs. 迭代,動態規劃,位操作,概率問題,排列組合,以及一些需要尋找規律的題目。
1. 字串和陣列
字串和陣列是最常見的面試題目型別,應當分配最大的時間。
關於字串,首先需要注意的是和C++不同,Java字串不是char陣列。沒有IDE程式碼自動補全功能,應該記住下面的這些常用的方法。
- toCharArray() //獲得字串對應的char陣列
- Arrays.sort() //陣列排序
- Arrays.toString(char[] a) //陣列轉成字串
-
charAt(int
- length() //字串長度
- length //陣列大小
- substring(int beginIndex)
- substring(int beginIndex, int endIndex)
- Integer.valueOf() //string to integer
- String.valueOf() /integer to string
字串和陣列本身很簡單,但是相關的題目需要更復雜的演算法來解決。比如說動態規劃,搜尋,等等。
2. 連結串列
在Java中,連結串列的實現非常簡單,每個節點Node都有一個值val和指向下個節點的連結next。
- class Node {
- int val;
- Node next;
- Node(int x) {
- val = x;
- next = null;
- }
- }
連結串列兩個著名的應用是棧Stack和佇列Queue。在Java標準庫都都有實現,一個是Stack,另一個是LinkedList(Queue是它實現的介面)。
經典題目:
3. 樹
這裡的樹通常是指二叉樹,每個節點都包含一個左孩子節點和右孩子節點,像下面這樣:
-
class
- int value;
- TreeNode left;
- TreeNode right;
- }
下面是與樹相關的一些概念:
二叉搜尋樹:左結點 <= 中結點 <= 右結點
平衡 vs. 非平衡:平衡二叉樹中,每個節點的左右子樹的深度相差至多為1(1或0)。
滿二叉樹(Full Binary Tree):除葉子節點以為的每個節點都有兩個孩子。
完美二叉樹(Perfect Binary Tree):是具有下列性質的滿二叉樹:所有的葉子節點都有相同的深度或處在同一層次,且每個父節點都必須有兩個孩子。
完全二叉樹(Complete Binary Tree):二叉樹中,可能除了最後一個,每一層都被完全填滿,且所有節點都必須儘可能想左靠。
經典題目:
4. 圖
圖相關的問題主要集中在深度優先搜尋(depth first search)和廣度優先搜尋(breath first search)。深度優先搜尋很簡單,廣度優先要注意使用queue. 下面是一個簡單的用佇列Queue實現廣度優先搜尋。
- publicclass GraphTest {
- publicstaticvoid breathFirstSearch(GraphNode root, int x){
- if(root.val == x)
- System.out.println("find in root");
- Queue queue = new Queue();
- root.visited = true;
- queue.enqueue(root);
- while(queue.first != null){
- GraphNode c = (GraphNode) queue.dequeue();
- for(GraphNode n: c.neighbors){
- if(!n.visited){
- System.out.print(n + " ");
- n.visited = true;
- if(n.val == x)
- System.out.println("Find "+n);
- queue.enqueue(n);
- }
- }
- }
- }
- }
經典題目:
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;
- publicstaticint f(int n){
- if(n <= 2) return n;
- int x = f(n-1) + f(n-2);
- return x;
- }
遞迴方法的時間複雜度是指數級,因為有很多冗餘的計算: 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)
直接的想法是將遞迴轉換為迭代:
- publicstaticint f(int n) {
- if (n <= 2){
- return n;
- }
- int first = 1, second = 2;
- int third = 0;
- for (int i = 3; i <= n; i++) {
- third = first + second;
- first = second;
- second = third;
- }
- return third;
- }
7. 動態規劃
動態規劃是解決下面這些性質類問題的技術:
- 一個問題可以通過更小子問題的解決方法來解決,或者說問題的最優解包含了其子問題的最優解
- 有些子問題的解可能需要計算多次
- 子問題的解儲存在一張表格裡,這樣每個子問題只用計算一次
- 需要額外的空間以節省時間
爬臺階問題完全符合上面的四條性質,因此可以用動態規劃法來解決。
- publicstaticint[] A = newint[100];
- publicstaticint f3(int n) {
- if (n <= 2)
- A[n]= n;
- if(A[n] > 0)
- return A[n];
- else
- A[n] = f3(n-1) + f3(n-2);//store results so only calculate once!
- return A[n];
- }
經典題目:
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計數並從右邊開始)
- publicstaticboolean getBit(