本文將覆蓋 「字符串處理」 + 「動態規劃」 方面的面試算法題,文中我將給出:html
倉庫地址:超級乾貨!精心概括視頻、歸類、總結
,各位路過的老鐵支持一下!給個 Star !
java
如今就讓咱們開始吧!node
二叉搜索樹(Binary Search Tree),它或者是一棵空樹,或者是具備下列性質的二叉樹:android
給定一個二叉樹,判斷其是不是一個有效的二叉搜索樹。git
假設一個二叉搜索樹具備以下特徵:github
示例 :面試
輸入: 5 / \ 1 4 / \ 3 6 輸出: false 解釋: 輸入爲: [5,1,4,null,null,3,6]。 根節點的值爲 5 ,可是其右子節點值爲 4 。
乍一看,這是一道很簡單的題。只須要遍歷整棵樹,檢查 node.right.val > node.val 和
node.left.val < node.val 對每一個結點是否成立。算法
問題是,這種方法並不老是正確。不只右子結點要大於該節點,整個右子樹的元素都應該大於該節點。例如:這意味着咱們須要在遍歷樹的同時保留結點的上界與下界,在比較時不只比較子結點的值,也要與上下界比較。編程
上述思路能夠用遞歸法實現:數組
public boolean isValidBST(TreeNode root) { return isValidBST(root, Long.MIN_VALUE, Long.MAX_VALUE); } private boolean isValidBST(TreeNode root, long min, long max){ if (root == null) { return true; } if (root.val <= min || root.val >= max){ return false; } return isValidBST(root.left, min, root.val) && isValidBST(root.right, root.val, max); }
給定一個二叉搜索樹,編寫一個函數 kthSmallest 來查找其中第 k 個最小的元素。
說明:
你能夠假設 k 老是有效的,1 ≤ k ≤ 二叉搜索樹元素個數。
示例 :
輸入: root = [5,3,6,2,4,null,null,1], k = 3 5 / \ 3 6 / \ 2 4 / 1 輸出: 3
public int kthSmallest(TreeNode root, int k) { if (root == null) { return 0; } int leftCount = getCount(root.left); if (leftCount >= k) { return kthSmallest(root.left, k); } else if (leftCount + 1 == k) { return root.val; } else { //注(1) return kthSmallest(root.right, k - leftCount - 1); } } private int getCount(TreeNode root) { if (root == null) { return 0; } return getCount(root.left) + getCount(root.right) + 1; }
注:
(1)爲何是 k - leftCount - 1 而不是 k ,咱們能夠把當前的二叉樹當作左右兩部分。在執行到這個條件的時候,很明顯,左邊 leftCount 個數,加上根節點,都小於所要求的元素。接着,如今要從右子樹搜索,很明顯,搜索是往下的,不可能往上(原根節點的方向)搜索,故,以前 leftCount + 1 個數做廢,因此所傳入 k - leftCount - 1
所謂雙指針
指的是在遍歷對象的過程當中,不是普通的使用單個指針進行訪問,而是使用兩個相同方向或者相反方向的指針進行掃描,從而達到相應的目的。
換言之,雙指針法充分使用了數組有序這一特徵,從而在某些狀況下可以簡化一些運算。
給定一個非負數,表示一個數字數組,在該數的基礎上+1,返回一個新的數組。該數字按照數位高低進行排列,最高位的數在列表的最前面。
示例 :
輸入: [4,3,2,1] 輸出: [4,3,2,2] 解釋: 輸入數組表示數字 4321。
只須要判斷有沒有進位並模擬出它的進位方式,如十位數加 11 個位數置爲 00,如此循環直到判斷沒有再進位就退出循環返回結果。
而後還有一些特殊狀況就是當出現 999九、999999 之類的數字時,循環到最後也須要進位,出現這種狀況時須要手動將它進一位。
給定一個由整數組成的非空數組所表示的非負整數,在該數的基礎上加一
public int[] plusOne(int[] digits) { for (int i = digits.length - 1; i >= 0; i--) { digits[i]++; digits[i] = digits[i] % 10; if (digits[i] != 0) return digits; } digits = new int[digits.length + 1]; digits[0] = 1; return digits; }
給定一個數組和一個值,在原地刪除與值相同的數字,返回新數組的長度。
public int removeElement(int[] A, int elem) { if (A == null || A.length == 0) { return 0; } int index = 0; for (int i = 0; i < A.length; i++) { if (A[i] != elem) { A[index++] = A[i]; } } return index; }
在原數組中「刪除」重複出現的數字,使得每一個元素只出現一次,而且返回「新」數組的長度。
示例 :
給定 nums = [0,0,1,1,1,2,2,3,3,4], 函數應該返回新的長度 5, 而且原數組 nums 的前五個元素被修改成 0, 1, 2, 3, 4。 你不須要考慮數組中超出新長度後面的元素。
public int removeDuplicates(int[] A) { if (A == null || A.length == 0) { return 0; } int size = 0; for (int i = 0; i < A.length; i++) { if (A[i] != A[size]) { A[++size] = A[i]; } } // (1) return size + 1; }
注:由於 size 爲下標,因此返回長度要加一
實現MyCalendar類來存儲活動。若是新添加的活動沒有重複,則能夠添加。類將有方法book(int start,int end)。這表明左閉右開的間隔[start,end)有了預約,範圍內的實數x,都知足start <= x < end,返回true。 不然,返回false,而且事件不會添加到日曆中。
示例 :
MyCalendar(); MyCalendar.book(10, 20); // returns true MyCalendar.book(15, 25); // returns false MyCalendar.book(20, 30); // returns true 解釋: 第一個日程安排能夠添加到日曆中. 第二個日程安排不能添加到日曆中,由於時間 15 已經被第一個日程安排預約了。 第三個日程安排能夠添加到日曆中,由於第一個日程安排並不包含時間 20 。
floorKey(K key) 方法用於返回小於或等於給定的鍵的全部鍵中,的最大鍵,或null,若是不存在這樣的鍵
ceilingKey(K key) 方法用於返回大於或等於返回到給定的鍵中,的最小鍵,或null,若是不存在這樣的鍵
class MyCalendar { TreeMap<Integer, Integer> calendar; MyCalendar() { calendar = new TreeMap(); } public boolean book(int start, int end) { Integer previous = calendar.floorKey(start), next = calendar.ceilingKey(start); if ((previous == null || calendar.get(previous) <= start) && (next == null || end <= next)) { calendar.put(start, end); return true; } return false; } }
合併兩個排序的整數數組A和B變成一個新的數組。能夠假設A具備足夠的空間去添加B中的元素。
說明:
初始化 A 和 B 的元素數量分別爲 m 和 n。
你能夠假設 A 有足夠的空間(空間大小大於或等於 m + n)來保存 B 中的元素。
示例:
輸入: nums1 = [1,2,3,0,0,0], m = 3 nums2 = [2,5,6], n = 3 輸出: [1,2,2,3,5,6]
public void mergeSortedArray(int[] A, int m, int[] B, int n) { int i = m - 1, j = n - 1, index = m + n - 1; while (i >= 0 && j >= 0) { if (A[i] > B[j]) { A[index--] = A[i--]; } else { A[index--] = B[j--]; } } while (i >= 0) { A[index--] = A[i--]; } while (j >= 0) { A[index--] = B[j--]; } }
顧名思義,貪心算法老是做出在當前看來最好的選擇。也就是說貪心算法並不從總體最優考慮,它所做出的選擇只是在某種意義上的局部最優選擇。固然,但願貪心算法獲得的最終結果也是總體最優的。雖然貪心算法不能對全部問題都獲得總體最優解,但對許多問題它能產生總體最優解。如單源最短路經問題,最小生成樹問題等。在一些狀況下,即便貪心算法不能獲得總體最優解,其最終結果倒是最優解的很好近似。
視頻
假設有一個數組,它的第i個元素是一支給定的股票在第i天的價格。若是你最多隻容許完成一次交易(例如,一次買賣股票),設計一個算法來找出最大利潤。
注意:
示例 :
輸入: [7,1,5,3,6,4] 輸出: 5 解釋: 在第 2 天(股票價格 = 1)的時候買入,在第 5 天(股票價格 = 6)的時候賣出,最大利潤 = 6-1 = 5 。 注意利潤不能是 7-1 = 6, 由於賣出價格須要早於買入價格。
若是將測試範例 [7, 1, 5, 3, 6, 4]
繪製成圖,咱們發現:
public int maxProfit(int[] prices) { if (prices == null || prices.length == 0) { return 0; } int min = Integer.MAX_VALUE; //記錄最低的價格 int profit = 0; for (int price : prices) { min = Math.min(price, min); profit = Math.max(price - min, profit); } return profit; }
給定一個數組 prices 表示一支股票天天的價格。能夠完成任意次數的交易, 不過不能同時參與多個交易,設計一個算法求出最大的利潤。
貪心:
public int maxProfit(int[] prices) { int profit = 0; for(int i = 0 ; i < prices.length -1; i++) { if(prices[i + 1] > prices[i]) { profit += prices[i + 1] - prices[i]; } } return profit; }
給定一個整數數組,找到一個具備最大和的子數組,返回其最大和。
示例:
輸入: [-2,1,-3,4,-1,2,1,-5,4], 輸出: 6 解釋: 連續子數組 [4,-1,2,1] 的和最大,爲 6。
public int maxSubArray(int[] nums) { if(nums == null || nums.length == 0) { return 0; } int max = Integer.MIN_VALUE; int sum = 0; for (int num : nums) { sum += num; max = Math.max(sum, max); sum = Math.max(sum, 0); } return max; }
給定一個整型數組,找出主元素,它在數組中的出現次數嚴格大於數組元素個數的二分之一(能夠假設數組非空,且數組中老是存在主元素)。
public int majorityNumber(List<Integer> nums) { int currentMajor = 0; int count = 0; for(Integer num : nums) { if(count == 0) { currentMajor = num; } if(num == currentMajor) { count++; } else { count--; } } return currentMajor; }
本片文章篇幅總結越長。我一直以爲,一片過長的文章,就像一堂超長的 會議/課堂,體驗很很差,因此我打算再開一篇文章
在後續文章中,我將繼續針對鏈表
棧
隊列
堆
動態規劃
矩陣
位運算
等近百種,面試高頻算法題,及其圖文解析 + 教學視頻 + 範例代碼
,進行深刻剖析有興趣能夠繼續關注 _yuanhao 的編程世界
不求快,只求優質,每篇文章將以 2 ~ 3 天的週期進行更新,力求保持高質量輸出
面試高頻算法題彙總「圖文解析 + 教學視頻 + 範例代碼」之 二分 + 哈希表 + 堆 + 優先隊列 合集
🔥面試必備:高頻算法題彙總「圖文解析 + 教學視頻 + 範例代碼」必知必會 排序 + 二叉樹 部分!🔥
每一個人都要學的圖片壓縮終極奧義,有效解決 Android 程序 OOM
Android 讓你的 Room 搭上 RxJava 的順風車 從重複的代碼中解脫出來
ViewModel 和 ViewModelProvider.Factory:ViewModel 的建立者
單例模式-全局可用的 context 對象,這一篇就夠了
縮放手勢 ScaleGestureDetector 源碼解析,這一篇就夠了
Android 屬性動畫框架 ObjectAnimator、ValueAnimator ,這一篇就夠了
看完這篇再不會 View 的動畫框架,我跪搓衣板
看完這篇還不會 GestureDetector 手勢檢測,我跪搓衣板!