梦开始的地方
1. 二分
不多说,基础中的基础。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| int binary_search(int[] nums, int target) { int left = 0, right = nums.length - 1; while(left <= right) { int mid = left + (right - left) / 2; if (nums[mid] < target) { left = mid + 1; } else if (nums[mid] > target) { right = mid - 1; } else if(nums[mid] == target) { return mid; } } return -1; }
int left_bound(int[] nums, int target) { int left = 0, right = nums.length - 1; while (left <= right) { int mid = left + (right - left) / 2; if (nums[mid] < target) { left = mid + 1; } else { right = mid - 1; } } if (left >= nums.length || nums[left] != target) return -1; return left; }
int right_bound(int[] nums, int target) { int left = 0, right = nums.length - 1; while (left <= right) { int mid = left + (right - left) / 2; if (nums[mid] < target) { left = mid + 1; } else { left = mid + 1; } } if (right < 0 || nums[right] != target) return -1; return right; }
|
2. 位运算
获取一个数二进制的某一位:
1 2
| int getBit(int a, int b) { return (a >> b) & 1; }
|
将一个数二进制的某一位设置为 0:
1 2
| int unsetBit(int a, int b) { return a & ~(1 << b); }
|
将一个数二进制的某一位设置为 1:
1 2
| int setBit(int a, int b) { return a | (1 << b); }
|
将一个数二进制的某一位取反:
1 2
| int flapBit(int a, int b) { return a ^ (1 << b); }
|
判断一个数是不是 2 的非负整数次幂:
1
| bool isPowerOfTwo(int n) { return n > 0 && (n & (n - 1)) == 0; }
|
==lowbit==
一个数二进制表示从低往高的第一个 连同后面的零,如 1010 的
lowbit
是 0010
1 2 3
| int lowbit(int x) { return x & -x; }
|
一个数的二进制表示可以看作是一个集合( 0 表示不在集合中, 1
表示在集合中)。比如集合 {1,3,4,8} ,可以表示成(100011010)
。而对应的位运算也就可以看作是对集合进行的操作。
操作 |
集合表示 |
位运算语句 |
交集 |
a ∩ b |
a & b |
并集 |
a ∪ b |
a |
补集 |
ā |
~a (全集为二进制都是 1) |
差集 |
a \ b |
a & (~b) |
对称差 |
a△b |
a ^ b |
子集
3. 前缀和与差分
K
倍区间
P1719
最大加权矩形
这种策略的定义是令 简单性质:
- ai
的值是 bi
的前缀和,即
- 计算 ai 的前腏和 sum
它可以维护多次对序列的一个区间加上一个数,并在最后询问某一位的数或是多次询问某一位的
数。注意修改操作一定要在查询操作之前。
地毯
4. 排序
4.1. 快排
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| private static void sorted(int[] n, int low, int up) { if (low >= up) return; int randIdx = new Random().nextInt(up-low+1)+low;swap(n, up, randIdx); int pivot = n[up], idx = low-1; for (int i = low;i < up;++i){ if (n[i] <= pivot) swap(n, ++idx, i); } swap(n, ++idx, up); sorted(n, low, idx-1); sorted(n, idx+1, up); } private static void swap(int[] n, int up, int randIdx) { int tp = n[up]; n[up] = n[randIdx]; n[randIdx] = tp; }
|
1 2 3 4 5 6 7 8 9
| int k; ... private void sorted(int[] n, int low, int up) { ... if (idx == k) return; else if (idx > k) sorted(n, low, idx-1); else sorted(n, idx+1, up); }
|
数组中的第
K 个最大元素
4.2. 归并
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| private static void mergeSort(int[] n, int left, int right) { if (left >= right) return; int mid = left+(right-left)/2; mergeSort(n, left, mid); mergeSort(n, mid+1, right); merge(n, left, mid+1, right+1); } private static void merge(int[] n, int low, int mid, int up) { int loc = 0, s1 = low, s2 = mid; int[] arr = new int[up-low]; while (s1 < mid && s2 < up) { if (n[s1] < n[s2]) arr[loc++] = n[s1++]; else arr[loc++] = n[s2++]; } while (s1 < mid) arr[loc++] = n[s1++]; while (s2 < up) arr[loc++] = n[s2++]; for (int i = 0;i < up-low;i++) { n[i+low] = arr[i]; } }
|
所谓逆序对,就是对于一个数组 a ,满足 ai > aj且i < j 的数对 (i, j)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| int ans = 0; ... void merge(int[] nums, int left, int mid, int right){ ... while (i<n && j<m){ if (nums[i]<=nums[j]) tp[idx++] = nums[i++]; else{ ans += (mid-i); tp[idx++] = nums[j++]; } } ... }
|
逆序对也可以用 树状数组、线段树
等数据结构求解。这三种方法的时间复杂度都是 O(nlog n)。
5. 单调队列&&滑动窗口
有一个长为 n 的序列
aa,以及一个大小为 k
的窗口。现在这个从左边开始向右滑动,每次滑动一个单位,求出每次滑动后窗口中的最大值和最小值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| private static void max_deque() { Deque<Integer> q = new LinkedList<>(); for (int i = 0;i < n;i++){ int cur = a[i]; while (!q.isEmpty() && a[q.peekLast()] <= cur) q.pollLast(); q.offerLast(i); while (!q.isEmpty() && q.peekFirst() <= i-k) q.pollFirst(); if (i >= k-1) System.out.print(a[q.peekFirst()]+" "); } } private static void min_deque() { Deque<Integer> q = new LinkedList<>(); for (int i = 0;i < n;i++){ int cur = a[i]; while (!q.isEmpty() && a[q.peekLast()] >= cur) q.pollLast(); q.offerLast(i); while (!q.isEmpty() && q.peekFirst() <= i-k) q.pollFirst(); if (i >= k-1) System.out.print(a[q.peekFirst()]+" "); } }
|
P1638 逛画展