本文將覆蓋 二分
+ 哈希表
+ 堆
+ 優先隊列
方面的面試算法題,文中我將給出:html
<br>java
如今就讓咱們開始吧!python
<br>android
概念: 二分查找也稱折半查找
(Binary Search),它是一種效率較高的查找方法。可是,折半查找要求線性表必須採用順序存儲結構
,並且表中元素按關鍵字有序
排列。git
基本思路:github
<br> <br>面試
給定一個 n 個元素有序的(升序)整型數組 nums 和一個目標值 target ,寫一個函數搜索 nums 中的 target,若是目標值存在返回下標,不然返回 -1。算法
示例 1:編程
輸入: nums = [-1,0,3,5,9,12], target = 9 輸出: 4 解釋: 9 出如今 nums 中而且下標爲 4數組
分析二分查找的一個技巧是:
if
/ else if
寫清楚這裏咱們以遞歸
和非遞歸
方式,解決面試中的二分搜索題
思路很簡單:
nums[mid]
與目標值大小nums[mid]
大,說明目標值 target 在前面nums[mid]
小,說明目標值 target 在前面後面當前位置
class Solution { public int search(int[] nums, int target) { return binarySearch(nums, 0, nums.length - 1, target); } private int binarySearch(int[] nums, int start, int end, int target) { if(start > end) { return -1; } int mid = (end + start) / 2; if(nums[mid] < target) { return binarySearch(nums, mid + 1, end, target); } if(nums[mid] > target) { return binarySearch(nums, start, mid - 1, target); } return mid; } }
這個場景是最簡單的:
int binarySearch(int[] nums, int target) { int left = 0; // 注意減 1 int right = nums.length - 1; while(left <= right) { int mid = (right + left) / 2; if(nums[mid] == target) return mid; else if (nums[mid] < target) left = mid + 1; // 注意 else if (nums[mid] > target) right = mid - 1; // 注意 } return -1; }
<br> <br>
計算並返回 x 的平方根,其中 x 是非負整數。
因爲返回類型是整數,結果只保留整數的部分,小數部分將被捨去。
示例 2:
輸入: 8 輸出: 2 說明: 8 的平方根是 2.82842..., 因爲返回類型是整數,小數部分將被捨去。
使用二分法搜索平方根的思想很簡單:
注:一個數的平方根最多不會超過它的一半,例如 8 的平方根,8 的一半是 4,若是這個數越大越是如此
對於判斷條件:
mid == x / mid
和 mid * mid == x
是等價的,實際卻否則mid == x / mid
就是:2 == 2 返回 truemid * mid == x
就是:4 == 5 返回 false對於邊界條件有個坑:
start
== mid
== end
則會死循環。取整操做的偏差爲1,故而在這裏限制條件改爲包含1在內的範圍start + 1 < end
; 這裏減一很精髓
public int sqrt(int x) { if (x < 0) { throw new IllegalArgumentException(); } else if (x <= 1) { return x; } int start = 1, end = x; // 直接對答案可能存在的區間進行二分 => 二分答案 while (start + 1 < end) { int mid = start + (end - start) / 2; if (mid == x / mid) { return mid; } else if (mid < x / mid) { start = mid; } else { end = mid; } } if (end > x / end) { return start; } return end; }
<br> <br>
概念 散列表(Hash table,也叫哈希表),是根據關鍵碼值(Key value)而直接進行訪問的數據結構。也就是說,它經過把關鍵碼值映射到表中一個位置來訪問記錄,以加快查找的速度。這個映射函數叫作散列函數,存放記錄的數組叫作散列表。
數據結構 給定表M
,存在函數f(key)
,對任意給定的關鍵字值key
,代入函數後若能獲得包含該關鍵字的記錄在表中的地址,則稱表M爲哈希(Hash)表
,函數f(key)爲哈希(Hash) 函數。
給一個整數數組,找到兩個數使得他們的和等於一個給定的數 target。須要實現的函數 twoSum 須要返回這兩個數的下標。
示例:
給定
nums = [2, 7, 11, 15], target = 9
由於
nums[0] + nums[1] = 2 + 7 = 9
因此返回[0, 1]
hashmap
來記錄key
記錄target - numbers[i]
的值,value
記錄numbers[i]
的i
的值numbers[j]
在hashmap
中存在numbers[i]
和numbers[j]
的和爲target
i
和j
即爲答案public int[] twoSum(int[] numbers, int target) { HashMap<Integer,Integer> map = new HashMap<>(); for (int i = 0; i < numbers.length; i++) { // 判斷 map 中是否有須要該值的項 if (map.containsKey(numbers[i])) { return new int[]{map.get(numbers[i]), i}; } // 意思可理解爲第 i 項,須要 target - numbers[i] map.put(target - numbers[i], i); } return new int[]{}; }
<br> <br>
給一個二進制
數組,找到 0 和 1 數量相等
的子數組的最大長度
示例 2:
輸入: [0,1,0] 輸出: 2 說明: [0, 1] (或 [1, 0]) 是具備相同數量0和1的最長連續子數組。
使用一個數字sum
維護到i
爲止1
的數量與0
的數量的差值
在loop i
的同時維護sum
並將其插入hashmap
中
對於某一個sum值,若hashmap中已有這個值
則當前的i
與sum
上一次出現的位置之間的序列0
的數量與1
的數量相同
public int findMaxLength(int[] nums) { Map<Integer, Integer> prefix = new HashMap<>(); int sum = 0; int max = 0; // 由於在開始時 0 、 1 的數量都爲 0 ,因此必須先存 0 // 不然第一次爲 0 的時候,<- i - prefix.get(sum) -> 找不到 prefix.get(0) prefix.put(0, -1); // 當第一個 0 1 數量相等的狀況出現時,數組下標減去-1獲得正確的長度 for (int i = 0; i < nums.length; i++) { int num = nums[i]; if (num == 0) { sum--; } else { sum++; } // 判斷是否已存在 sum 值 // 存在則說明以前存過 if (prefix.containsKey(sum)) { // 只作判斷,不作存儲 max = Math.max(max, i - prefix.get(sum)); } else { prefix.put(sum, i); } } return max; }
<br> <br>
給定一個字符串,請你找出其中不含有重複字符的 最長子串 的長度。
輸入: "abcabcbb" 輸出: 3 解釋: 由於無重複字符的最長子串是 "abc",因此其長度爲 3。
用HashMap
記錄每個字母出現的位置:
左邊界
,到當前枚舉到的位置之間的字符串爲不含重複字符的子串。public int lengthOfLongestSubstring(String s) { if (s == null || s.length() == 0) { return 0; } HashMap<Character, Integer> map = new HashMap<>(); int max = Integer.MIN_VALUE; // 計算無重複字符子串開始的位置 int start = -1; int current = 0; for (int i = 0; i < s.length(); i++) { if (map.containsKey(s.charAt(i))) { int tmp = map.get(s.charAt(i)); // 上一次的位置在左邊界右邊, 則須要向右移動左邊界 if (tmp >= start) { start = tmp; } } map.put(s.charAt(i), i); max = Math.max(max, i - start); } return max; }
<br> <br>
給出二維平面上的n個點,求最多有多少點在同一條直線上
首先點的定義以下
class Point { int x; int y; Point() { x = 0; y = 0; } Point(int a, int b) { x = a; y = b; } }示例 :
輸入: [[1,1],[3,2],[5,3],[4,1],[2,3],[1,4]] 輸出: 4 解釋: ^ | | o | o o | o | o o +-------------------> 0 1 2 3 4 5 6
提示:咱們會發現,其實只須要考慮當前點以後出現的點i + 1 .. N - 1
便可,由於經過點 i-2
的直線已經在搜索點 i-2
的過程當中考慮過了。
畫一條經過點 i 和以後
出現的點的直線,在哈希表中存儲這條邊並計數爲2
= 當前這條直線上有兩個點。
存儲時,以斜率來區分線與線之間的關係
假設如今 i
< i + k
< i + l
這三個點在同一條直線上,當畫出一條經過 i 和 i+l 的直線會發現已經記錄
過了,所以對更新
這條邊對應的計數:count++
。
經過 HashMap
記錄下兩個點之間的斜率相同出現的次數,注意考慮點重合
的狀況
public int maxPoints(int[][] points) { if (points == null) { return 0; } int max = 0; for (int i = 0; i < points.length; i++) { Map<String, Integer> map = new HashMap<>(); int maxPoints = 0; int overlap = 0; for (int j = i + 1; j < points.length; j++) { int dy = points[i][1] - points[j][1]; int dx = points[i][0] - points[j][0]; // 兩個點重合的狀況記錄下來 if (dy == 0 && dx == 0) { overlap++; continue; } // 防止 x 相同 y 不一樣,但 rate 都爲 0 // 防止 y 相同 x 不一樣,但 rate 都爲 0 // 以及超大數約等於 0 的狀況:[[0,0],[94911151,94911150],[94911152,94911151]] String rate = ""; if(dy == 0) rate = "yy"; else if (dx == 0) rate = "xx"; else rate = ((dy * 1.0) / dx) + ""; map.put(rate, map.getOrDefault(rate, 0) + 1); maxPoints = Math.max(maxPoints, map.get(rate)); } max = Math.max(max, overlap + maxPoints + 1); } return max; }
堆一般是一個能夠被看作一棵樹的數組對象。堆老是知足下列性質:
以下圖這是一個最大堆,,由於每個父節點的值都比其子節點要大。10 比 7 和 2 都大。7 比 5 和 1都大。
<br> <br>
PriorityQueue 優先隊列:Java 的優先隊列,保證了每次取最小元素
// 維護一個 PriorityQueue,以返回前K大的數 public int[] topk(int[] nums, int k) { int[] result = new int[k]; if (nums == null || nums.length < k) { return result; } Queue<Integer> pq = new PriorityQueue<>(); for (int num : nums) { pq.add(num); if (pq.size() > k) { // poll() 方法用於檢索或獲取和刪除隊列的第一個元素或隊列頭部的元素 pq.poll(); } } for (int i = k - 1; i >= 0; i--) { result[i] = pq.poll(); } return result; }
實現一個數據結構,提供下面兩個接口:
public class Solution { private int maxSize; private Queue<Integer> minheap; public Solution(int k) { minheap = new PriorityQueue<>(); maxSize = k; } public void add(int num) { if (minheap.size() < maxSize) { // add(E e)和offer(E e)的語義相同,都是向優先隊列中插入元素 // 只是Queue接口規定兩者對插入失敗時的處理不一樣 // 前者在插入失敗時拋出異常,後則則會返回false minheap.offer(num); return; } if (num > minheap.peek()) { minheap.poll(); minheap.offer(num); } } public List<Integer> topk() { // 將隊列中的數存到數組中 Iterator it = minheap.iterator(); List<Integer> result = new ArrayList<Integer>(); while (it.hasNext()) { result.add((Integer) it.next()); } // 調用數組排序法後返回 Collections.sort(result, Collections.reverseOrder()); return result; } }
在未排序的數組中找到第 k 個
最大的元素。請注意,你須要找的是數組排序後
的第 k
個最大的元素,而不是
第 k 個不一樣
的元素。
示例 2:
輸入: [3,2,3,1,2,4,5,5,6] 和 k = 4 輸出: 4
個人第一個想法:暴力法
public int findKthLargest(int[] nums, int k) { Queue<Integer> que = new PriorityQueue<>(); for(int num : nums) { if(que.size() < k) { que.offer(num); } else { if(que.peek() < num) { que.poll(); que.offer(num); } } } return que.peek(); }
使用快速排序
,思路極其簡單:
第 k 個
數便可具體實現:
public int kthLargestElement(int k, int[] nums) { if (nums == null || nums.length == 0 || k < 1 || k > nums.length){ return -1; } return partition(nums, 0, nums.length - 1, nums.length - k); } private int partition(int[] nums, int start, int end, int k) { if (start >= end) { return nums[k]; } int left = start, right = end; int pivot = nums[(start + end) / 2]; while (left <= right) { while (left <= right && nums[left] < pivot) { left++; } while (left <= right && nums[right] > pivot) { right--; } if (left <= right) { swap(nums, left, right); left++; right--; } } if (k <= right) { return partition(nums, start, right, k); } if (k >= left) { return partition(nums, left, end, k); } return nums[k]; } private void swap(int[] nums, int i, int j) { int tmp = nums[i]; nums[i] = nums[j]; nums[j] = tmp; }
<br> <br>
爲了提升文章質量,防止冗長乏味
本片文章篇幅總結越長。我一直以爲,一片過長的文章,就像一場超長的 會議/課堂,體驗很很差,因此打算再開一篇文章來總結其他的考點
在後續文章中,我將繼續針對鏈表
棧
隊列
堆
動態規劃
矩陣
位運算
等近百種,面試高頻算法題,及其圖文解析 + 教學視頻 + 範例代碼
,進行深刻剖析有興趣能夠繼續關注 _yuanhao 的編程世界
🔥 面試必備:高頻算法題彙總「圖文解析 + 教學視頻 + 範例代碼」必問之 鏈表 + 棧 + 隊列 部分!🔥 🔥面試必備:高頻算法題彙總「圖文解析 + 教學視頻 + 範例代碼」必知必會 排序 + 二叉樹 部分!🔥 每一個人都要學的圖片壓縮終極奧義,有效解決 Android 程序 OOM Android 讓你的 Room 搭上 RxJava 的順風車 從重複的代碼中解脫出來 ViewModel 和 ViewModelProvider.Factory:ViewModel 的建立者 單例模式-全局可用的 context 對象,這一篇就夠了 縮放手勢 ScaleGestureDetector 源碼解析,這一篇就夠了 Android 屬性動畫框架 ObjectAnimator、ValueAnimator ,這一篇就夠了 看完這篇再不會 View 的動畫框架,我跪搓衣板 看完這篇還不會 GestureDetector 手勢檢測,我跪搓衣板!
<br> <br>
倉庫地址:超級乾貨!精心概括視頻、歸類、總結
,各位路過的老鐵支持一下!給個 Star !
原文出處:https://www.cnblogs.com/yuanhao-1999/p/11677320.html