可點選右方檢視目錄
資料結構
腦圖:naotu.baidu.com/file/0a53d3a5343bd...
一維:
基礎:陣列 array (string), 連結串列 linked list
高階:棧 stack, 佇列 queue, 雙端佇列 deque, 集合 set, 對映 map (hash or map), etc
二維:
基礎:樹 tree, 圖 graph
高階:二叉搜尋樹 binary search tree (red-black tree, AVL), 堆 heap, 並查集 disjoint set, 字典樹 Trie, etc
特殊:
位運算 Bitwise, 布隆過濾器 BloomFilter
LRU Cache
時間複雜度
演算法
If-else, switch —> branch
for, while loop —> Iteration
遞迴 Recursion (Divide & Conquer, Backtrace)
搜尋 Search: 深度優先搜尋 Depth first search, 廣度優先搜尋 Breadth first search, A*, etc
動態規劃 Dynamic Programming
二分查詢 Binary Search
貪心 Greedy
數學 Math , 幾何 Geometry
刷題路線分享
基礎
- 兩數之和(簡單)
- 有效的括號(簡單)
- 字串解碼(中等)
- LRU 快取機制(困難)
- 實現 Trie(字首樹)(中等)
- 新增與搜尋單詞 - 資料結構設計(中等)
- 單詞搜尋 II (困難)
- 找不同(簡單)
- 單詞規律(簡單)
- 字串中的第一個唯一字元(簡單)
- 無重複字元的最長子串(中等)
- 最小覆蓋子串(困難)
- 合併兩個有序連結串列(簡單)
- 環形連結串列(簡單)
- 環形連結串列 II (中等)
- 反轉連結串列(簡單)
- 反轉連結串列 II (中等)
- 旋轉連結串列(中等)
- 排序連結串列
- 連結串列中倒數第 k 個節點
- 兩兩交換連結串列中的節點(中等)
- 按奇偶排序陣列(簡單)
- 按奇偶排序陣列 II (簡單)
- 有序陣列的平方(簡單)
- 山脈陣列的峰頂索引(簡單)
- 搜尋旋轉排序陣列(困難)
- 搜尋旋轉排序陣列 II (中等)
- 尋找旋轉排序陣列中的最小值(中等)
- 尋找旋轉排序陣列中的最小值 II (困難)
- 搜尋二維矩陣(中等)
- 等式方程的可滿足性(中等)
- 朋友圈(中等)
- 賬戶合併(中等)
深度優先搜尋
回溯
分治
動態規劃
- 使用最小花費爬樓梯(簡單)
- 爬樓梯(簡單)
- 不同路徑(簡單)
- 最小路徑和 (中等)
- 最大子序和 (簡單)
- 乘積最大子陣列(中等)
- 買賣股票的最佳時機(簡單)
- 買賣股票的最佳時機 II (簡單)
- 買賣股票的最佳時機 III (困難)
- 買賣股票的最佳時機 IV (困難)
- 最佳買賣股票時機含冷凍期(中等)
- 買賣股票的最佳時機含手續費(中等)
- 零錢兌換 (中等)
- 零錢兌換 II (中等)
- 編輯距離(困難)
- 不同的子序列(困難)
- 柱狀圖中最大的矩形(困難)
- 最大矩形(困難)
- 最大正方形(中等)
- 最低票價(中等)
- 區域和檢索 - 陣列不可變(簡單)
- 二維區域和檢索 - 矩陣不可變(中等)
- 最長上升子序列 (中等)
- 雞蛋掉落(困難)
部分程式碼模板 (僅供參考)
並不是題目答案,只是說明大部分題目有套路可循;主要是java、python的版本,或者虛擬碼,應該能看懂。
遞迴
// java
public void recur(int level, int param) {
// terminator
if (level > MAX_LEVEL) {
// process result
return;
}
// process current logic
process(level, param);
// drill down
recur( level: level + 1, newParam);
// restore current status
}
分治、回溯
// java
private static int divide_conquer(Problem problem, ) {
if (problem == NULL) {
int res = process_last_result();
return res;
}
subProblems = split_problem(problem)
res0 = divide_conquer(subProblems[0])
res1 = divide_conquer(subProblems[1])
result = process_result(res0, res1);
return result;
}
DFS
遞迴寫法
// java
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> allResults = new ArrayList<>();
if(root==null){
return allResults;
}
travel(root,0,allResults);
return allResults;
}
private void travel(TreeNode root,int level,List<List<Integer>> results){
if(results.size()==level){
results.add(new ArrayList<>());
}
results.get(level).add(root.val);
if(root.left!=null){
travel(root.left,level+1,results);
}
if(root.right!=null){
travel(root.right,level+1,results);
}
}
非遞迴寫法
# Python
def DFS(self, tree):
if tree.root is None:
return []
visited, stack = [], [tree.root]
while stack:
node = stack.pop()
visited.add(node)
process (node)
nodes = generate_related_nodes(node)
stack.push(nodes)
# other processing work
...
BFS
# Python
def BFS(graph, start, end):
visited = set()
queue = []
queue.append([start])
while queue:
node = queue.pop()
visited.add(node)
process(node)
nodes = generate_related_nodes(node)
queue.push(nodes)
# other processing work
...
貪心演算法
貪心演算法是在當前情況下做出的最優決定,它只考慮眼前,獲得的是區域性的最優解,並且,希望透過每次獲得區域性最優解最後找到全域性的最優解。
二分查詢
// Java
public int binarySearch(int[] array, int target) {
int left = 0, right = array.length - 1, mid;
while (left <= right) {
mid = (right - left) / 2 + left;
if (array[mid] == target) {
return mid;
} else if (array[mid] > target) {
right = mid - 1;
} else {
left = mid + 1;
}
}
return -1;
}
動態規劃
動態規劃 和 遞迴或者分治 沒有根本上的區別(關鍵看有無最優的子結構)
共性:找到重複子問題
差異性:最優子結構、中途可以淘汰次優解
DP順推模板
# 虛擬碼
function DP():
dp = [][] # 二維情況
for i = 0 .. M {
for j = 0 .. N {
dp[i][j] = _Function(dp[i’][j’]...)
}
}
return dp[M][N];
Trie 樹(字典樹)
// java
class Trie {
private boolean isEnd;
private Trie[] next;
/** Initialize your data structure here. */
public Trie() {
isEnd = false;
next = new Trie[26];
}
/** Inserts a word into the trie. */
public void insert(String word) {
if (word == null || word.length() == 0) return;
Trie curr = this;
char[] words = word.toCharArray();
for (int i = 0;i < words.length;i++) {
int n = words[i] - 'a';
if (curr.next[n] == null) curr.next[n] = new Trie();
curr = curr.next[n];
}
curr.isEnd = true;
}
/** Returns if the word is in the trie. */
public boolean search(String word) {
Trie node = searchPrefix(word);
return node != null && node.isEnd;
}
/** Returns if there is any word in the trie that starts with the given prefix. */
public boolean startsWith(String prefix) {
Trie node = searchPrefix(prefix);
return node != null;
}
private Trie searchPrefix(String word) {
Trie node = this;
char[] words = word.toCharArray();
for (int i = 0;i < words.length;i++) {
node = node.next[words[i] - 'a'];
if (node == null) return null;
}
return node;
}
}
並查集
// java
class UnionFind {
private int count = 0;
private int[] parent;
public UnionFind(int n) {
count = n;
parent = new int[n];
for (int i = 0; i < n; i++) {
parent[i] = i;
}
}
public int find(int p) {
while (p != parent[p]) {
parent[p] = parent[parent[p]];
p = parent[p];
}
return p;
}
public void union(int p, int q) {
int rootP = find(p);
int rootQ = find(q);
if (rootP == rootQ) return;
parent[rootP] = rootQ;
count--;
}
}
布隆過濾器
// java
public class BloomFilter {
private static final int DEFAULT_SIZE = 2 << 24;
private static final int[] seeds = new int[] { 5, 7, 11, 13, 31, 37, 61 };
private BitSet bits = new BitSet(DEFAULT_SIZE);
private SimpleHash[] func = new SimpleHash[seeds.length];
public BloomFilter() {
for (int i = 0; i < seeds.length; i++) {
func[i] = new SimpleHash(DEFAULT_SIZE, seeds[i]);
}
}
public void add(String value) {
for (SimpleHash f : func) {
bits.set(f.hash(value), true);
}
}
public boolean contains(String value) {
if (value == null) {
return false;
}
boolean ret = true;
for (SimpleHash f : func) {
ret = ret && bits.get(f.hash(value));
}
return ret;
}
// 內部類,simpleHash
public static class SimpleHash {
private int cap;
private int seed;
public SimpleHash(int cap, int seed) {
this.cap = cap;
this.seed = seed;
}
public int hash(String value) {
int result = 0;
int len = value.length();
for (int i = 0; i < len; i++) {
result = seed * result + value.charAt(i);
}
return (cap - 1) & result;
}
}
}
LRU Cache
// java 雜湊表 + 雙連結串列
class LRUCache {
/**
* 快取對映表
*/
private Map<Integer, DLinkNode> cache = new HashMap<>();
/**
* 快取大小
*/
private int size;
/**
* 快取容量
*/
private int capacity;
/**
* 連結串列頭部和尾部
*/
private DLinkNode head, tail;
public LRUCache(int capacity) {
//初始化快取大小,容量和頭尾節點
this.size = 0;
this.capacity = capacity;
head = new DLinkNode();
tail = new DLinkNode();
head.next = tail;
tail.prev = head;
}
/**
* 獲取節點
* @param key 節點的鍵
* @return 返回節點的值
*/
public int get(int key) {
DLinkNode node = cache.get(key);
if (node == null) {
return -1;
}
//移動到連結串列頭部
(node);
return node.value;
}
/**
* 新增節點
* @param key 節點的鍵
* @param value 節點的值
*/
public void put(int key, int value) {
DLinkNode node = cache.get(key);
if (node == null) {
DLinkNode newNode = new DLinkNode(key, value);
cache.put(key, newNode);
//新增到連結串列頭部
addToHead(newNode);
++size;
//如果快取已滿,需要清理尾部節點
if (size > capacity) {
DLinkNode tail = removeTail();
cache.remove(tail.key);
--size;
}
} else {
node.value = value;
//移動到連結串列頭部
moveToHead(node);
}
}
/**
* 刪除尾結點
*
* @return 返回刪除的節點
*/
private DLinkNode removeTail() {
DLinkNode node = tail.prev;
removeNode(node);
return node;
}
/**
* 刪除節點
* @param node 需要刪除的節點
*/
private void removeNode(DLinkNode node) {
node.next.prev = node.prev;
node.prev.next = node.next;
}
/**
* 把節點新增到連結串列頭部
*
* @param node 要新增的節點
*/
private void addToHead(DLinkNode node) {
node.prev = head;
node.next = head.next;
head.next.prev = node;
head.next = node;
}
/**
* 把節點移動到頭部
* @param node 需要移動的節點
*/
private void moveToHead(DLinkNode node) {
removeNode(node);
addToHead(node);
}
/**
* 連結串列節點類
*/
private static class DLinkNode {
Integer key;
Integer value;
DLinkNode prev;
DLinkNode next;
DLinkNode() {
}
DLinkNode(Integer key, Integer value) {
this.key = key;
this.value = value;
}
}
}
十大經典排序演算法
閱讀:www.cnblogs.com/onepixel/p/7674659...
圖片來源於上文
快速排序
// java
public static void quickSort(int[] array, int begin, int end) {
if (end <= begin) return;
int pivot = partition(array, begin, end);
quickSort(array, begin, pivot - 1);
quickSort(array, pivot + 1, end);
}
static int partition(int[] a, int begin, int end) {
// pivot: 標杆位置,counter: 小於pivot的元素的個數
int pivot = end, counter = begin;
for (int i = begin; i < end; i++) {
if (a[i] < a[pivot]) {
int temp = a[counter]; a[counter] = a[i]; a[i] = temp;
counter++;
}
}
int temp = a[pivot]; a[pivot] = a[counter]; a[counter] = temp;
return counter;
}
歸併排序
// java
public static void mergeSort(int[] array, int left, int right) {
if (right <= left) return;
int mid = (left + right) >> 1; // (left + right) / 2
mergeSort(array, left, mid);
mergeSort(array, mid + 1, right);
merge(array, left, mid, right);
}
public static void merge(int[] arr, int left, int mid, int right) {
int[] temp = new int[right - left + 1]; // 中間陣列
int i = left, j = mid + 1, k = 0;
while (i <= mid && j <= right) {
temp[k++] = arr[i] <= arr[j] ? arr[i++] : arr[j++];
}
while (i <= mid) temp[k++] = arr[i++];
while (j <= right) temp[k++] = arr[j++];
for (int p = 0; p < temp.length; p++) {
arr[left + p] = temp[p];
}
// 也可以用 System.arraycopy(a, start1, b, start2, length)
}
堆排序
// java
public static void heapSort(int[] array) {
if (array.length == 0) return;
int length = array.length;
for (int i = length / 2-1; i >= 0; i-)
heapify(array, length, i);
for (int i = length - 1; i >= 0; i--) {
int temp = array[0]; array[0] = array[i]; array[i] = temp;
heapify(array, i, 0);
}
}
static void heapify(int[] array, int length, int i) {
int left = 2 * i + 1, right = 2 * i + 2;
int largest = i;
if (left < length && array[left] > array[largest]) {
largest = left;
}
if (right < length && array[right] > array[largest]) {
largest = right;
}
if (largest != i) {
int temp = array[i]; array[i] = array[largest]; array[largest] = temp;
heapify(array, length, largest);
}
}
結語
光說不練假把式,重要的是動手做題。
本作品採用《CC 協議》,轉載必須註明作者和本文連結