001-算法随记
异或
-
异或^:无进位相加(同0异1)
-
0^N = N, N^N=0
-
交换律、结合律
-
一串数异或,改顺序不影响结果
-
交换两个数,不申请额外空间
- 前提:a和b指向的内存是两块东西,比如在数组中,i位置不能等于j位置,如果i位置等于j位置,这个位置会被洗成0,所以不建议这么换
// a = 甲,b = 乙 a = a ^ b; // a = 甲^乙,b = 乙 b = a ^ b; // a = 甲^乙,b = 甲^乙^乙 = 甲 a = a ^ b; // a = 甲^乙^甲 = 0^乙 = 乙,b = 甲
题目
-
一个数组中只有一种数出现奇数次,其余数出现偶数次,如何找到这个数?(要求时O(N),空O(1))
- 从头到尾异或,所有偶数次的数异或结果是0,0和剩下一个数字异或得到哪个数字
-
一个数组中只有两种数出现奇数次,其余数出现偶数次,如何找到这个数?(要求时O(N),空O(1))
-
从头异或到尾,假设两个目标数字是a和b,最终结果eor=a^b
-
又a和b不相等,所以eor不为0
-
此时a和b至少有1位不同,例如第八位不同,会使eor的第八位为1
-
实际中可以提取最右边的1
// 取最右侧的1,其余位为0 int rightOne = eor & (~eor + 1);
-
所有数可以被分为第8为是1和第8位是0的数,a和b被分别分在两类中
-
让eor2等于所有第8位是1的数异或得到的值,那这个值一定是a和b中的一个
- 代码里让数组里的数和rightOne做与运算来判断这一位是不是1,是1才会返回1
-
再让eor^eor2,得到另一个数
-
排序
插入排序
- 从0-0有序一直扩容到0-N上有序
- 每到一个数都一直往前换到它该在的位置
- 时\(O(N^2)\)
- 空间\(O(1)\)
冒泡排序
- 相邻两个数不断往右换,每次往最后一个位置放一个最值
- 时\(O(N^2)\)
- 空间\(O(1)\)
选择排序
- 每次选一个最值放最左边,然后再在这之外找一个最值放在这个右边一个位置,以此类推
二分法
-
只要问题能甩到一边中,就可以二分,不是一定要有序
-
常规二分:找一个有序数组中是否存在某个数
- 找中间的数,然后根据大小找右边的中间或左边的中间,以此类推
-
略微进阶:一个有序数组,找大于等于某个数的最左侧的位置或小于等于某个数最右侧的位置
- 一直二分到不能二分,只要满足要求就继续二分,用找到的新结果替代旧结果
-
进阶:一个无序数组,相邻数字一定不相等,求一个局部最小(可以用极小值理解:一个数小于左边且小于右边,首尾元素小于其相邻的数)的数字
- 单独看0位置和N-1位置
- 如果0到1下降,N-2到N-1上升,那么0到N-1一定有局部最小值,取中点M
- 判断M是否为局部最小
- 如果不是,则看左右的大小,来确定左右某一边有局部最小值,再接着二分
对数器
-
每次测试生成一个随机数
-
创建两个长度和值都随机的数组
-
用一个绝对正确的方法跑其中一个数组
-
用对数器方法(自己的算法)跑第二个数组
-
检查结果是否相等