1. 复杂度、对数器、二分法、位运算与异或运算
1.1 排序
1.1.1 Java自带的排序函数
Arrays.sort(arr);
1.1.2 冒泡排序
- 时间复杂度O(n*n),空间复杂度O(1)
1.1.3 选择排序
- 时间复杂度O(n*n),空间复杂度O(1)
- 保留数组中最小or最大的值的下标
1.1.4 插入排序
- 时间复杂度:最坏情况O(n*n),最好情况O(n),空间复杂度O(1)
- 插入到合适的位置,内部循环从后往前看
1.2 对数器
- 有一个你想要测的方法a
- 实现复杂度不好但是容易实现的方法b
- 实现一个随机样本产生器
- 把方法a和方法b跑相同的随机样本, 看看得到的结果是否一样。
- 如果有一个随机样本使得比对结果不一致, 打印样本进行人工干预, 改对方法a或者方法b
- 当样本数量很多时比对测试依然正确, 可以确定方法a已经正确。
在我们遇到问题时候,a是我们设计出来的算法,b是暴力穷举法,这时候为了验证算法是否可靠,就可以通过产生大量的随机样本去检测算法的正确性,且我们可以通过改变输入,纠正两种算法种的错误。任何题目都可以有暴力做法。随机数组生成如下:
public static int[] GenerateRandomArray(int maxSize, int maxValue) {
// Math.random() 等概率返回 [0, 1) 之间的小数
// Math.random() * N [0, N)
// (int) (Math.random() * N) [0, N-1]
int[] arr = new int[(int) ((maxSize + 1) * Math.random())];
for (int i = 0; i < arr.length; i++) {
arr[i] = (int) ((maxValue + 1) * Math.random());
}
return arr;
}
1.3 二分查找
public boolean BinarySearch(int[] sortedArr, int num) {
if (sortedArr == null || sortedArr.length == 0) {
return false;
}
int left = 0;
int right = sortedArr.length - 1;
int mid = 0;
while(left <= right) {
// mid = (L + R) / 2; // mid = L + (R -L) / 2 万一 L与R 分别是 10亿和20亿, 会出错
mid = left + ((right - left) >> 1);
if (sortedArr[mid] == num) {
return true;
} else if (sortedArr[mid] > num) {
right = mid - 1;
} else if (sortedArr[mid] < num) {
left = mid + 1;
}
}
return sortedArr[mid] == num;
}
1.4 位运算符
位运算的实现是比加减乘除快的多的
- a左移一位,意味着a * 2
a << 1;
- a右移一位,意味着a / 2
a >> 1;
下面两个代码是等价的,但是位运算要更快
a << 1 | 1;
a * 2 + 1;
1.5 异或(^)
1.5.1 不申请额外存储空间,交换两个变量
public void Swap(int a, int b) {
a = a ^ b;
b = a ^ b;
a = a ^ b;
}
1.5.2 一个数组中有一种数出现了奇数次,其他数都出现了偶数次,找到并打印这种数
public int FindOdd(int[] arr) {
int res = 0;
for (int i = 0; i < arr.length; i++) {
res ^= arr[i];
}
return res;
}
1.5.3 一个int类型的数,提取出其二进制最右侧的1
public int ExtractRightOne(int x) {
return x & (~x + 1);
}
1.5.4 一个数组有两种数出现了奇数次,其他数都出现了偶数次,找到并打印这两种数
public int[] FindTwoOdd(int[] arr) {
int xor = 0;
for (int i = 0; i < arr.length; i++) {
xor ^= arr[i];
}
int rightOne = xor & (~xor + 1); // 提取出最右侧的1
int xor2 = 0; // xor'
for (int i = 0; i < arr.length; i++) {
if ((arr[i] & rightOne) == 0) {
xor2 ^= arr[i];
}
}
int[] res = new int[] {xor2, (xor ^ xor2)};
return res;
}
1.5.5 计算一个数,二进制格式下1的个数
public int Bit1Counts(int a) {
int count = 0;
int temp = a;
while (temp != 0) {
int rightOne = temp & (~temp + 1);
count++;
temp ^= rightOne;
}
return count;
}
1.6 递归函数的时间复杂度(master公式)
递归的子过程是等规模的,每次递归的子过程会把递归分为b个过程,调用a个过程,时间复杂度即
剩余部分则是
- 若
,则时间复杂度为
- 若
,则时间复杂度为
- 若
,则时间复杂度为