秋招接近尾聲,我總結了 牛客
、WanAndroid
上,有關筆試面經的帖子中出現的算法題,結合往年考題寫了這一系列文章,全部文章均與 LeetCode 進行覈對、測試。歡迎食用css
本文將覆蓋 「二進制」 + 「位運算」 和 Lru 方面的面試算法題,文中我將給出:java
解析
GitHub
創建了一個倉庫倉庫地址:超級乾貨!精心概括視頻、歸類、總結
,各位路過的老鐵支持一下!給個 Star !
android
如今就讓咱們開始吧!git
給定一個包含 m x n
個要素的矩陣,(m
行, n
列),按照螺旋順序,返回該矩陣中的全部要素。github
示例 :面試
輸入:
[
[1, 2, 3, 4],
[5, 6, 7, 8],
[9,10,11,12]
]
輸出: [1,2,3,4,8,12,11,10,9,5,6,7]
複製代碼
第 1 層
,次外層元素都是第 2 層
,而後是第 3 層
的。[[1, 1, 1, 1, 1, 1, 1],
[1, 2, 2, 2, 2, 2, 1],
[1, 2, 3, 3, 3, 2, 1],
[1, 2, 2, 2, 2, 2, 1],
[1, 1, 1, 1, 1, 1, 1]]
複製代碼
(r1, c)
,按照 c = c1,...,c2
的順序。(r, c2)
,按照 r = r1+1,...,r2
的順序。r1 < r2
而且 c1 < c2
),咱們如下圖所示的方式遍歷下方的元素和左側的元素。public List<Integer> spiralOrder(int[][] matrix) {
ArrayList<Integer> rst = new ArrayList<Integer>();
if(matrix == null || matrix.length == 0) {
return rst;
}
int rows = matrix.length;
int cols = matrix[0].length;
int count = 0;
while(count * 2 < rows && count * 2 < cols){
for (int i = count; i < cols - count; i++) {
rst.add(matrix[count][i]);
}
for (int i = count + 1; i < rows - count; i++) {
rst.add(matrix[i][cols - count - 1]);
}
if (rows - 2 * count == 1 || cols - 2 * count == 1) { // 若是隻剩1行或1列
break;
}
for (int i = cols - count - 2; i >= count; i--) {
rst.add(matrix[rows - count - 1][i]);
}
for (int i = rows - count - 2; i >= count + 1; i--) {
rst.add(matrix[i][count]);
}
count++;
}
return rst;
}
複製代碼
請斷定一個數獨
是否有效。該數獨可能只填充了部分數字,其中缺乏的數字用 . 表示。算法
維護一個HashSet
用來記同一行
、同一列
、同一九宮格
是否存在相同數字數據庫
示例 :編程
輸入:
[
["8","3",".",".","7",".",".",".","."],
["6",".",".","1","9","5",".",".","."],
[".","9","8",".",".",".",".","6","."],
["8",".",".",".","6",".",".",".","3"],
["4",".",".","8",".","3",".",".","1"],
["7",".",".",".","2",".",".",".","6"],
[".","6",".",".",".",".","2","8","."],
[".",".",".","4","1","9",".",".","5"],
[".",".",".",".","8",".",".","7","9"]
]
輸出: false
解釋: 除了第一行的第一個數字從 5 改成 8 之外,空格內其餘數字均與 示例1 相同。
但因爲位於左上角的 3x3 宮內有兩個 8 存在, 所以這個數獨是無效的。
複製代碼
不必定
是可解的。有效便可
。1-9
和字符 '.'
。9x9
形式的。`一次迭代緩存
首先,讓咱們來討論下面兩個問題:
如何枚舉子數獨? 可使用 box_index = (row / 3) * 3 + columns / 3
,其中 / 是整數除法。
如何確保行 / 列 / 子數獨中沒有重複項? 能夠利用 value -> count
哈希映射來跟蹤全部已經遇到的值。
如今,咱們完成了這個算法的全部準備工做:
false
。true
。public boolean isValidSudoku(char[][] board) {
Set seen = new HashSet();
for (int i=0; i<9; ++i) {
for (int j=0; j<9; ++j) {
char number = board[i][j];
if (number != '.')
if (!seen.add(number + " in row " + i) ||
!seen.add(number + " in column " + j) ||
!seen.add(number + " in block " + i / 3 + "-" + j / 3))
return false;
}
}
return true;
}
複製代碼
給定一個N×N
的二維矩陣表示圖像,90度
順時針旋轉圖像。
示例 :
輸入: [[1,1,0,0],[1,0,0,1],[0,1,1,1],[1,0,1,0]]
輸出: [[1,1,0,0],[0,1,1,0],[0,0,0,1],[1,0,1,0]]
解釋: 首先翻轉每一行: [[0,0,1,1],[1,0,0,1],[1,1,1,0],[0,1,0,1]];
而後反轉圖片: [[1,1,0,0],[0,1,1,0],[0,0,0,1],[1,0,1,0]]
複製代碼
1 <= A.length = A[0].length <= 20
0 <= A[i][j] <= 1
複製代碼
旋轉這些矩形
的問題。移動
它們。public void rotate(int[][] matrix) {
if (matrix == null || matrix.length == 0 || matrix[0].length == 0) {
return;
}
int length = matrix.length;
for (int i = 0; i < length / 2; i++) {
for (int j = 0; j < (length + 1) / 2; j++){
int tmp = matrix[i][j];
matrix[i][j] = matrix[length - j - 1][i];
matrix[length -j - 1][i] = matrix[length - i - 1][length - j - 1];
matrix[length - i - 1][length - j - 1] = matrix[j][length - i - 1];
matrix[j][length - i - 1] = tmp;
}
}
}
複製代碼
優勢:
特定狀況下,計算方便,速度快,被支持面廣
若是用算數方法,速度慢,邏輯複雜
位運算不限於一種語言,它是計算機的基本運算方法
複製代碼
2 * n + 1
個數字,除其中一個數字以外其餘每一個數字均出現兩次,找到這個數字。異或運算具備很好的性質,相同數字異或運算後爲0,而且具備交換律和結合律,故將全部數字異或運算後便可獲得只出現一次的數字。
示例 :
輸入: [4,1,2,1,2]
輸出: 4
複製代碼
若是咱們對 0 和二進制位作 XOR 運算,獲得的仍然是這個二進制位
若是咱們對相同的二進制位作 XOR 運算,返回的結果是 0
XOR 知足交換律和結合律
因此咱們只須要將全部
的數進行 XOR 操做,獲得那個惟一的數字。
public int singleNumber(int[] A) {
if(A == null || A.length == 0) {
return -1;
}
int rst = 0;
for (int i = 0; i < A.length; i++) {
rst ^= A[i];
}
return rst;
}
複製代碼
O(n)
。咱們只須要將 O(1)
。非負整數 n
,表示該代碼中全部二進制的總數,請找出其格雷編碼順序。一個格雷編碼順序必須以 0
開始,並覆蓋全部的 2n
個整數。例子——輸入:2
;輸出:[0, 1, 3, 2];解釋: 0 - 00
,1 - 01
,3 - 11
,2 - 10
G(i) = i ^ (i >> 2)
public ArrayList<Integer> grayCode(int n) {
ArrayList<Integer> result = new ArrayList<Integer>();
for (int i = 0; i < (1 << n); i++) {
result.add(i ^ (i >> 1));
}
return result;
}
複製代碼
將一個整數中的數字進行顛倒
,當顛倒後的整數溢出時
,返回 0 (標記爲 32 位整數)。
示例 :
輸入: -123
輸出: -321
複製代碼
除 10 取餘
的方法,將最低位和最高倒序輸出
便可public int reverseInteger(int n) {
int reversed_n = 0;
while (n != 0) {
int temp = reversed_n * 10 + n % 10;
n = n / 10;
if (temp / 10 != reversed_n) {
reversed_n = 0;
break;
}
reversed_n = temp;
}
return reversed_n;
}
複製代碼
運用你所掌握的數據結構,設計和實現一個 LRU (最近最少使用) 緩存機制。它應該支持如下操做: 獲取數據 get 和 寫入數據 put 。
獲取數據 get(key)
- 若是密鑰 (key) 存在
於緩存中,則獲取密鑰的值(老是正數),不然返回 -1。
寫入數據 put(key, value)
- 若是密鑰不存在
,則寫入
其數據值。當緩存容量達到上限時,它應該在寫入新數據以前刪除
最近最少使用的數據值,從而爲新的數據值留出空間。
示例:
LRUCache cache = new LRUCache( 2 /* 緩存容量 */ );
cache.put(1, 1);
cache.put(2, 2);
cache.get(1); // 返回 1
cache.put(3, 3); // 該操做會使得密鑰 2 做廢
cache.get(2); // 返回 -1 (未找到)
cache.put(4, 4); // 該操做會使得密鑰 1 做廢
cache.get(1); // 返回 -1 (未找到)
cache.get(3); // 返回 3
cache.get(4); // 返回 4
複製代碼
解法一:
HashMap
用於記錄緩存內容public class LRUCache {
private class Node{
Node prev;
Node next;
int key;
int value;
public Node(int key, int value) {
this.key = key;
this.value = value;
this.prev = null;
this.next = null;
}
}
private int capacity;
private HashMap<Integer, Node> hs = new HashMap<Integer, Node>();
private Node head = new Node(-1, -1);// 頭
private Node tail = new Node(-1, -1);// 尾
public LRUCache(int capacity) {
this.capacity = capacity;
tail.prev = head;
head.next = tail;
}
public int get(int key) {
if( !hs.containsKey(key)) { //key找不到
return -1;
}
// remove current
Node current = hs.get(key);
current.prev.next = current.next;
current.next.prev = current.prev;
// move current to tail
move_to_tail(current); //每次get,使用次數+1,最近使用,放於尾部
return hs.get(key).value;
}
public void set(int key, int value) { //數據放入緩存
// get 這個方法會把key挪到最末端,所以,不須要再調用 move_to_tail
if (get(key) != -1) {
hs.get(key).value = value;
return;
}
if (hs.size() == capacity) { //超出緩存上限
hs.remove(head.next.key); //刪除頭部數據
head.next = head.next.next;
head.next.prev = head;
}
Node insert = new Node(key, value); //新建節點
hs.put(key, insert);
move_to_tail(insert); //放於尾部
}
private void move_to_tail(Node current) { //移動數據至尾部
current.prev = tail.prev;
tail.prev = current;
current.prev.next = current;
current.next = tail;
}
}
複製代碼
解法二:
LRU
緩存機制,須要在 O(1)
時間內完成以下操做:O(1)
時間內完成。有一種叫作有序字典
的數據結構,綜合了哈希表
和鏈表
,在 Java 中爲 LinkedHashMap
。
下面用這個數據結構來實現。
class LRUCache extends LinkedHashMap<Integer, Integer>{
private int capacity;
public LRUCache(int capacity) {
super(capacity, 0.75F, true);
this.capacity = capacity;
}
public int get(int key) {
return super.getOrDefault(key, -1);
}
public void put(int key, int value) {
super.put(key, value);
}
@Override
protected boolean removeEldestEntry(Map.Entry<Integer, Integer> eldest) {
return size() > capacity;
}
}
複製代碼
get/in/set/move_to_end/popitem(get/containsKey/put/remove)
均可以在常數時間內完成。 空間複雜度:本片文章篇幅總結越長。我一直以爲,一片過長的文章,就像一堂超長的 會議/課堂,體驗很很差,因此我打算再開一篇文章
在後續文章中,我將繼續針對鏈表
棧
隊列
堆
動態規劃
矩陣
位運算
等近百種,面試高頻算法題,及其圖文解析 + 教學視頻 + 範例代碼
,進行深刻剖析有興趣能夠繼續關注 _yuanhao 的編程世界
不求快,只求優質,每篇文章將以 2 ~ 3
天的週期進行更新,力求保持高質量輸出
倉庫地址:超級乾貨!精心概括視頻、歸類、總結
,各位路過的老鐵支持一下!給個 Star !