前段時間,寫了面試必備的一系列文章,反應還不錯。有一些讀者反饋說,能不能整理一些面試常見的算法。前段時間,我恰好總結了 LeetCode 常見的面試算法題目。
https://github.com/gdutxiaoxu/Android_interview
剛開始準備刷算法題目的時候,感覺真的是好難,十道題目有九道是不會的。心中曾一萬隻草泥馬跑過,自己怎麼這麼辣雞。
慢慢得,我發現算法也是一個可以通過練習慢慢成長的。
首先我們要掌握基本的數據結構,數組,鍊表,哈希表, Set,二叉樹,堆,棧等。你要知道他們有什麼優缺點,適應場景是什麼,時間複雜度和空間複雜度是多少。而不能知道簡單的 API。
接著,掌握了這些基本的數據結構之後,一些基本的算法你也要掌握以下,比如快速排序,歸併排序,對排序,二分查找。這些基本的一定要掌握,面試當中經常也會問到。
分類刷題,我們在力扣上面可以看到,https://leetcode-cn.com/problemset/algorithms/ ,刷題是可以按標籤來的。比如鍊表,數組,二分查找,二叉樹,動態規劃等
學好算法不是一日之功,需要長期的積累。建議的做法是每天做一兩道題,題目不在多,貴在於理解。堅持一兩個月,你會發現你的感覺逐漸好起來了
廢話不多說了,開始進入今天的正文。
二叉樹的概念俗話說得好,萬丈高樓平地起。講解算法之前,我們先來了解一下一些基本的概念。
二叉樹(Binary Tree)是包含n個節點的有限集合,該集合或者為空集(此時,二叉樹稱為空樹),或者由一個根節點和兩棵互不相交的、分別稱為根節點的左子樹和右子樹的二叉樹組成。
一棵典型的二叉樹如下圖所示:
由上述的定義可以看出,二叉樹中的節點至多包含兩棵子樹,分別稱為左子樹和右子樹,而左子樹和右子樹又分別至多包含兩棵子樹。由上述的定義,二叉樹的定義是一種遞歸的定義。滿二叉樹
對於一棵二叉樹,如果每一個非葉子節點都存在左右子樹,並且二叉樹中所有的葉子節點都在同一層中,這樣的二叉樹稱為滿二叉樹。
完全二叉樹
對於一棵具有n個節點的二叉樹按照層次編號,同時,左右子樹按照先左後右編號,如果編號為i的節點與同樣深度的滿二叉樹中編號為i的節點在二叉樹中的位置完全相同,則這棵二叉樹稱為完全二叉樹。
二叉排序樹:
又稱二叉查找樹(Binary Search Tree),亦稱二叉搜索樹。二叉排序樹或者是一棵空樹,或者是具有下列性質的二叉樹:
二分查找的時間複雜度是O(log(n)),最壞情況下的時間複雜度是O(n)(相當於順序查找)
平衡二叉樹:
又稱 AVL 樹。平衡二叉樹是二叉搜索樹的進化版,所謂平衡二叉樹指的是,左右兩個子樹的高度差的絕對值不超過 1。
紅黑樹:
紅黑樹是每個節點都帶顏色的樹,節點顏色或是紅色或是黑色,紅黑樹是一種查找樹。紅黑樹有一個重要的性質,從根節點到葉子節點的最長的路徑不多於最短的路徑的長度的兩倍。對於紅黑樹,插入,刪除,查找的複雜度都是O(log N)。
哈夫曼樹:
給定n個權值作為n的葉子結點,構造一棵二叉樹,若帶權路徑長度達到最小,稱這樣的二叉樹為最優二叉樹,也稱為哈夫曼樹(Huffman tree)。
遍歷方式二叉樹主要有四種遍歷方式
前提:這裡先給出測試中的二叉樹結構,如下圖所示
該二叉樹對應的幾種遍歷方式的結果順序:
先序遍歷:10->6->4->8->14->12->16
中序遍歷:4->6->8->10->12->14->16
後序遍歷:4->8->6->12->16->14->10
層次遍歷:10->6->14->4->8->12->16
遞歸遞歸的基本思想就是把規模大的問題轉化為規模小的相似的子問題來解決。
特別地,在函數實現時,因為解決大問題的方法和解決小問題的方法往往是同一個方法,所以就產生了函數調用它自身的情況,這也正是遞歸的定義所在。格外重要的是,這個解決問題的函數必須有明確的結束條件,否則就會導致無限遞歸的情況
而在樹當中,一棵樹要麼是空樹,要麼有兩個指針,每個指針指向一棵樹。樹是一種遞歸結構,很多樹的問題可以使用遞歸來處理。
1. 樹的高度1.0 求二叉樹的最大層數(最大深度)104. Maximum Depth of Binary Tree (Easy)
Leetcode / 力扣
這道題目的解法其實很簡單
1public int maxDepth(TreeNode root) {
2 if (root == null) return 0;
3 return Math.max(maxDepth(root.left), maxDepth(root.right)) + 1;
4}
LeetCode:Minimum Depth of Binary Tree
給定一個二叉樹,找出其最小深度。
最小深度是從根節點到最近葉子節點的最短路徑上的節點數量。
解題思路
1class Solution {
2 public int minDepth(TreeNode root) {
3 if(root == null)
4 return 0;
5 int left = minDepth(root.left);
6 int right = minDepth(root.right);
7 return (left == 0 || right == 0) ? left + right + 1 : Math.min(left, right) + 1;
8 }
9}
110. Balanced Binary Tree (Easy)
1 3
2 / \
3 9 20
4 / \
5 15 7
平衡樹左右子樹高度差都小於等於 1
1private boolean result = true;
2
3public boolean isBalanced(TreeNode root) {
4 maxDepth(root);
5 return result;
6}
7
8public int maxDepth(TreeNode root) {
9 if (root == null) return 0;
10 int l = maxDepth(root.left);
11 int r = maxDepth(root.right);
12 if (Math.abs(l - r) > 1) result = false;
13 return 1 + Math.max(l, r);
14}
543. Diameter of Binary Tree (Easy)
給定一棵二叉樹,你需要計算它的直徑長度。一棵二叉樹的直徑長度是任意兩個結點路徑長度中的最大值。這條路徑可能穿過也可能不穿過根結點。
解題思路:
因為最長直徑不一定過根節點,所以需要分別以每一個節點為中心計算最長路徑。
用一個全局變量max來維護最長路徑(左子樹的深度+右子樹的深度)。
為了計算每個子樹的深度,使用深度優先遍歷計算樹中每一個節點的深度(max(左子樹深度,右子樹深度))
1Input:
2
3 1
4 / \
5 2 3
6 / \
7 4 5
8
9Return 3, which is the length of the path [4,2,1,3] or [5,2,1,3].
1private int max = 0;
2
3public int diameterOfBinaryTree(TreeNode root) {
4 depth(root);
5 return max;
6}
7
8private int depth(TreeNode root) {
9 if (root == null) return 0;
10 int leftDepth = depth(root.left);
11 int rightDepth = depth(root.right);
12 max = Math.max(max, leftDepth + rightDepth);
13 return Math.max(leftDepth, rightDepth) + 1;
14}
226. Invert Binary Tree (Easy)
翻轉一棵二叉樹
思路與算法
這是一道很經典的二叉樹問題。顯然,我們從根節點開始,遞歸地對樹進行遍歷,並從葉子結點先開始翻轉。
如果當前遍歷到的節點 root 的左右兩棵子樹都已經翻轉,那麼我們只需要交換兩棵子樹的位置,即可完成以 root 為根節點的整棵子樹的翻轉。
1public TreeNode invertTree(TreeNode root) {
2 if (root == null) return null;
3 TreeNode left = root.left; // 後面的操作會改變 left 指針,因此先保存下來
4 root.left = invertTree(root.right);
5 root.right = invertTree(left);
6 return root;
7}
617. Merge Two Binary Trees (Easy)
Leetcode / 力扣
給定兩個二叉樹,想像當你將它們中的一個覆蓋到另一個上時,兩個二叉樹的一些節點便會重疊。
你需要將他們合併為一個新的二叉樹。合併的規則是如果兩個節點重疊,那麼將他們的值相加作為節點合併後的新值,否則不為 NULL 的節點將直接作為新二叉樹的節點。
解題思路
1Input:
2 Tree 1 Tree 2
3 1 2
4 / \ / \
5 3 2 1 3
6 / \ \
7 5 4 7
8
9Output:
10 3
11 / \
12 4 5
13 / \ \
14 5 4 7
1public TreeNode mergeTrees(TreeNode t1, TreeNode t2) {
2 if (t1 == null && t2 == null) return null;
3 if (t1 == null) return t2;
4 if (t2 == null) return t1;
5 TreeNode root = new TreeNode(t1.val + t2.val);
6 root.left = mergeTrees(t1.left, t2.left);
7 root.right = mergeTrees(t1.right, t2.right);
8 return root;
9}
這篇文章主要講解了二叉樹的一些基本概念,以及一些常見的遞歸算法題目,看起來挺簡單的,但是讓你寫,你寫得出來嘛?算法這種東西,要多練,動手實踐,印象才深刻。所有的算法題目以及面試相關的,我都放在這個目錄下,有空可以幫我 start 一下,謝謝。https://github.com/gdutxiaoxu/Android_interview