1. Binary Search
二分查找法主要是解决在“一堆数中找出指定的数”这类问题。
想要应用二分查找法,这一堆数必须有以下特征:
存储在数组中
有序排列(无序的话二分用来猜答案的方法)
static int binarySearch(int[] nums, int target) {
int left = 0, right = nums.length - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] == target) return mid;
else if (nums[mid] > target) right = mid - 1;
else left = mid + 1;
}
return -1;
}
LeetCode: 278, 410, 300, 1552, 1482, 1283, 1292
2. 双指针
同向双指针:283,1,16
反向双指针:11,75
快慢指针:142
quick sort,单调队列,单调栈,扫描线
3. Sliding window
340. 至多包含 K 个不同字符的最长子串:
public int lengthOfLongestSubstringKDistinct(String s, int k) {
Map<Character, Integer> map = new HashMap();
int left = 0, res = 0;
for (int i = 0; i < s.length(); i++) {
char cur = s.charAt(i);
map.put(cur, map.getOrDefault(cur, 0) + 1);
while (map.size() > k) {
char c = s.charAt(left);
map.put(c, map.get(left) - 1);
left++;
}
res = Math.max(res, i - left + 1);
}
return res;
}
LeetCode: 209, 340
4. BFS + DFS
优缺点:
bfs: 对于解决最短或最少问题特别有效,而且寻找深度小,但缺点是内存耗费量大;
dfs: 对于解决遍历和求所有问题有效,对于问题搜索深度小的时候处理速度迅速。
bfs:
// 计算从起点start到终点target的最近距离
int BFS(Node start, Node target) {
Queue<Node> q; // 核心数据结构
Set<Node> visited; // 避免走回头路
q.offer(start); // 将起点加入队列
visited.add(start);
int step = 0; // 记录扩散的步数
while (q not empty) {
int sz = q.size();
// 将队列中的所有节点向四周扩散
for (int i = 0; i < sz; i++) {
Node cur = q.poll();
// 判断是否到达终点
if (cur is target)
return step;
// 将cur相邻的节点加入队列
for (Node x : cur.adj())
if (x not in visited) {
q.offer(x);
visited.add(x);
}
}
// 更新步数
step++;
}
}
dfs:
result = []
def backtrack(路径, 选择列表):
if 满足条件
result.add(路径)
return
for 选择 in 选择列表:
做选择
backtrack(路径, 选择列表)
撤销选择
LeetCode: 102, 200, 490, 297, 124, 339
5. backtracking
类似dfs
LeetCode: 78, 17
6. sort
6.1 quick sort
public int[] quickSort(int[] array) {
helper(array, 0, array.length - 1);
return array;
}
private void helper(int[] array, int left, int right) {
if (left >= right) return;
int pivot = partition(array, left, right);
helper(array, left, pivot - 1);
helper(array, pivot + 1, right);
}
private int partition(int[] array, int left, int right) {
int pivot = array[right], wall = left;
for(int i = 0; i < array.length; i++) {
if (array[i] < pivot) {
swap(array, i, wall);
wall++;
}
}
swap(array, wall, right);
return wall;
}
LeetCode: 215
6.2 merge sort
public int[] mergeSort(int[] array) {
return divide(array, 0, array.length - 1);
}
private int[] divide(int[] array, int start, int end) {
if (left >= right) return new int[]{array[left]};
int mid = start + (end - start) / 2;
int[] leftResult = divide(array, start, mid);
int[] rightResult = divide(array, mid + 1, end);
return merge(leftResult, rightResult);
}
private int[] merge(int[] leftResult, int[] rightResult) {
int[] res = new int[leftResult.length, right.length];
int i = 0, j = 0, k = 0;
while (i < leftResult.length && j < rightResult.length) {
if (leftResult[i] < rightResult[j]) res[k++] = leftResult[i++];
else res[k++] = rightResult[j++];
}
while (i < leftResult.length) res[k++] = leftResult[i++];
while (j < rightResult.length) res[k++] = rightResult[j++];
return res;
}
LeetCode: 23
6.3 bucket sort
void bucketSort(float[] a, int n) {
for (each floating integer 'x' in n) {
insert x into bucket[n * x];
}
for (each bucket) {
sort(bucket);
}
}
LeetCode: 451
7. 单调栈*
7.1 反向模板
public int[] nextGreaterElement1(int[] nums) {
int n = nums.length, res[] = new int[n];
Deque<Integer> stack = new ArrayDeque<Integer>();
for (int = n - 1; i >= 0; i++) {
while (!stack.isEmpty() && nums[i] >= stack.peek()) stack.pop();
res[i] = stack.isEmpty() ? -1 : stack.peek();
stack.push(nums[i]);
}
return res;
}
7.2 正向模板
public int[] nextGreaterElement2(int[] nums) {
int n = nums.length, res[] = new int[n];
Deque<Integer> stack = new ArrayDeque();
Arrays.fill(res, -1);
for (int i = 0; i < n;, i++) {
while (!stack.isEmpty() && nums[stack.peek()] < nums[i])
res[stack.peek()] = nums[i];
stack.push(i);
}
return res;
}
LeetCode: 42, 739
8. 单调队列*
队头删除不符合有效窗口的元素,队尾删除不符合最值的候选元素。
步骤:去尾,新元素入队,删头,取解
public int[] monotonicQueue(int[] nums, int k) {
int N = nums.length;
Deque<Integer> q = new ArrayDeque();
int[] res = new int[N - k + 1];
for (int i = 0; i < N; i++) {
// 左出q,保证窗口大小k-1
while (!q.isEmpty() && i - q.peekFirst() >= k) q.pollFirst();
// 右出q,保证递减队列
while (!q.isEmpty() && nums[q.peekLast()] <= nums[i]) q.pollLast();
// 进q,此时q.size() == k
q.offerLast(i);
// 使用q左边最大值处理结果,可以在不同位置
q.peekFirst();
}
return res;
}
LeetCode: 239, 862*
9. 扫描线
扫描线一般运用在图形上面,就是一条线在整个图上扫来扫去,一般用来解决图形面积,周长等问题。
public int minMeetingRooms(int[][] intervals) {
List<int[]> list = new ArrayList();
for (int[] interval : intervals) {
list.add(new int[]{interval[0], 1});
list.add(new int[]{interval[1], -1});
}
Collection.sort(list, (a, b) -> a[0] == b[0] ? a[1] - b[1] : a[0] - b[0]);
int res = 0, count = 0;
for (int[] point : list) {
count += point[1];
res = Math.max(res, count);
}
return res;
}
LeetCode: 253, 252, 218
10. Trie(字典树)
字典树、前缀树,是一种多叉树结构;
1)根节点不包含字符,除根节点外的每一个节点都仅包含一个字符;
2)从根节点到某一节点路径上所经过的字符连接起来,即为该节点对应的字符串;
3)任意节点的所有子节点所包含的字符都不相同。
LeetCode: 208
class Trie {
public Trie() {
}
TrieNode root = new TrieNode();
public void insert(String word) {
TrieNode node = root;
for (char c : word) {
if (node.children[c - 'a'] == null)
node.children[c - 'a'] = new TrieNode();
node = node.children[c - 'a'];
}
node.isWord = true;
}
public boolean search(String word) {
TrieNode node = root;
for (char c : word.toCharArray()) {
if (node.children[c - 'a'] == null) return false;
node = node.children[c - 'a'];
}
return node.isWord;
}
public boolean startWith(String prefix) {
TrieNode node = root;
for (char c : word.toCharArray()) {
if (node.children[c - 'a'] == null) return false;
node = node.children[c - 'a'];
}
return true;
}
}
class TrieNode {
TrieNode[] children = new TrieNode[26];
boolean isWord;
}
LeetCode: 208, 212, 588
11. Heap(堆)
minHeap-PriorityQueue
class MyPriorityQueue {
int q = new int[100];
int size;
public void offer(int num) {
q[size] = num;
siftUp(size++);
}
public int poll() {
int val = q[0];
q[0] = q[size - 1];
size--;
siftDown(0);
return val;
}
public int peek() {
return q[0];
}
private void siftUp(int cur) {
if (cur != 0) {
int parent = (cur - 1) / 2;
if (q[parent]> q[cur]) {
swap(parent, cur);
siftUp(parent);
}
}
}
private void siftDown(int cur) {
int min = cur;
int left = cur * 2 + 1;
int right = cur * 2 + 2;
if (left < size && q[left] < q[min]) min = left;
if (right < size && q[right] < q[min]) min = right;
if (cur != min) {
swap(cur, min);
siftDown(min);
}
}
private void swap(int i, int j) {
int tmp = q[i];
q[i] = q[j];
q[j] = tmp;
}
}
12. Union find(并查集)
并查集是一种树形的数据结构,用于处理不交集的合并及查询问题。
Find: 确定元素属于哪一个自己;
Union: 将两个子集合并成同一个集合。
class DSU {
int[] parent;
public DSU(int N) {
parent = new int[N];
for (int i = 0; i < N; i++) parent[i] = i;
}
public int find(int x) {
if (parent[x] != x) parent[x] = find(parent[x]);
return parent[x];
}
public int union(int x, int y) {
parent[find(x)] = find(y);
}
}
LeetCode: 721
13. 链表
13.1 反转链表
// Iterator
public ListNode reverseList(ListNode head) {
ListNode newHead = null;
ListNode cur = head;
while (cur != null) {
ListNode next = cur.next;
cur.next = newHead;
newHead = cur;
cur = next;
}
return newHead;
}
// Recursive
public ListNode reverseList(ListNode head) {
return reverse(head, null);
}
private ListNode reverse(ListNode head, ListNode newHead) {
if (head == null) return newHead;
ListNode next = head.next;
head.next = newHead;
return reverse(next, head);
}