一,算法评估
1,指标
(1)时间复杂度(流程决定)
时间复杂度的意义在于,当需要处理的样本很大时,真正重要的是最高阶项是什么。这就是时间复杂度的意义,它是衡量算法流程的复杂程序的一种指标,该指标只与数据量有关,与过程之外的优化无关。
(2)额外空间复杂度(流程决定)
作为输入参数的空间,不算额外空间,作为输出结果的空间,也不算额外空间,因为这些都是必要的,与实现目标有关。但除此之外,还需要开辟空间才能让流程继续下去,那这部分空间就是额外空间。
(3)常数项时间(实现细节决定)
同样算法复杂度,执行时间也很可能不相同,因为常数项花费时间不同。
2.时间复杂度
2.1 常数时间的操作:一个操作的执行时间不以具体样本量为转移,每次执行时间都是固定时间,称这样的操作为常数时间操作。
常见的常数时间操作
(1)常见的算术操作(+,-,*,/,%等)
(2)常见的位运算(>>, >>>, <<,|,&,^等)
“>>”:带符号右移动,符号位是什么,就用符号位补,正数为0,负数为1
“>>>”:不带符号右移,不管最高完成什么,都用0来补位
(3)赋值,比较,自增,自检操作等
(4)数组寻址操作
对于链表,linkedList.get(i),需要遍历,不是常数
总之,执行时间固定的操作都是常数时间的操作
反之,执行时间不固定的操作,都不是常数时间的操作
2.2 时间复杂度举例
时间复杂度为N^2的排序算法
选择排序,冒泡排序,插入排序
2.3 如何确定算法流程的总操作与样本数量之间的表达式关系
(1)想象该算法流程所处理的数据状况,要按照最差情况来。
(2)把整个流程彻底拆分为一个个基本动作,保证每个动作都是常数时间的操作。
(3) 如果数据量为N,看看基本动作的数量和N是什么关系。
注意:算法过程与具体语言无关,分析算法复杂度前提,是对该流程非常熟悉。在拆分算法流程时,拆分出来的所有行为都是常数时间操作,意味着对调用的API要熟悉,否则影响对时间复杂度的估算。
2.4 算法流程的常数项比拼方式
放弃理论分析,生成随机数据直接测。
因为不同常数时间的操作,虽然都是固定时间,但还是有快慢之分。比如位运算的常数时间远小于算术运算的常数时间,而这两个的常数时间又远小于数组寻址的时间。
2.5 问题的最优解
一般情况下,认为解决一个问题的算法流程,在时间复杂度的指标上,一定要尽可能的低,先满足了时间复杂度最低这个指标之后,使用最少的空间的算法流程,叫这个问题的最优解。
不卡常数项时间,不同语言,底层运行速度也不同。
2.6 常见的时间复杂度
排名从好到差
O(1)
O(logN)
O(N)
O(NlogN)
O(N^2) 2,3,…K
O(2^N) 2,3,…K
O(N!)
二,认识对数器
1.你想要测的方法a
2.实现复杂度不好但是容易实现的方法b
3.实现一个随机样本产生器
4.把方法a和方法b跑相同的随机样本,看看得到的结果是否一样
5.如果有一个随机样本使得对比结果不一致,打印样本进行人工干预,改对方法a和方法b
6.当样本数量很多时比对测试依然正确,可以确定方法a已经正确。
Math.random(); [0,1)
Math.random()*N; [0,N)
(int)(Math.random()*N); [0,N-1]
三,认识二分法
时间复杂度O(logN),一般使用mid = L +((R-L)>>1)
来防止溢出,位运算比除运算快很多。
注意:N2可以表达为N<<1
, N2+1表达为(N<<1)|1
N左移,低位补0,右移,无符号补0,有符号补符号位(正数补0,负数补1)。
1. 在一个有序数组中,找某个数是否存在
public static boolean binary(int[] arr, int tmp) {
boolean result = false;
if (arr != null || arr.length >= 2) {
int left = 0, right = arr.length - 1, mid;
while (left <= right) {
mid = left + ((right - left) >> 1);
if (tmp == arr[mid]) {
result = true;
break;
} else if (tmp < arr[mid]) {
right = mid - 1;
} else {
left = mid + 1;
}
}
}
return result;
}
2. 在一个有序数组中,找>=某个数最左侧的位置
满足>=某个数的条件之后,需要再看看左边是否还有满足条件的数。
public static int binary(int[] arr, int tmp) {
int result = -1;
if (arr != null || arr.length >= 2) {
int left = 0, right = arr.length - 1, mid;
while (left <= right) {
mid = left + ((right - left) >> 1);
if (arr[mid] >= tmp) {
result = mid;
right = mid - 1;
} else {
left = mid + 1;
}
}
}
return result;
}
3. 在一个有序数组中,找<=某个数最右侧的位置
当满足<=某个数之后,需要再向右看,是否有新的满足条件的数。
public static int binary(int[] arr, int tmp) {
int result = -1;
if (arr != null || arr.length >= 2) {
int left = 0, right = arr.length - 1, mid;
while (left <= right) {
mid = left + ((right - left) >> 1);
if (arr[mid] <= tmp) {
result = mid;
left = mid + 1;
} else {
right = mid - 1;
}
}
}
return result;
}
4. 局部最小值问题
什么是局部最小
(1) 0位置比1位置小,0位置就是局部最小
(2)N位置比N-1位置小,N位置就是局部最小
(3)中间位置i,既比i-1小,也比i+1小,就是局部最小。
给定一个无序数组,相邻数不等,返回一个局部最小的数
public static int smallest(int[] arr) {
if(arr==null||arr.length==0){
return -1;
}
if(arr.length==1 || arr[0]<arr[1]){
return 0;
}
if(arr[arr.length-1]<arr[arr.length-2]){
return arr.length-1;
}
int left = 1,right = arr.length-2,mid;
while (left<right){
mid=left+((right-left)>>1);
if(arr[mid]>arr[mid-1]){
right=mid-1;
}else if(arr[mid]>arr[mid+1]){
left=mid+1;
}else{
return mid;
}
}
return left;
}
四,异或操作
异或运算:相同为0,不同为1
同或操作:相同为1,不同为0
异或运算为无进位相加!
1,异或性质
(1)0^N == N, N^N==0
(2)异或运算满足交换律和结合律
2.用法
(1)不用额外变量,就交换两个数的值(必须保证两数不是同一个内存)
public static void swapTwoNumber(int a,int b){
a = a^b;
b = a^b;
a = a^b;
}
(2)数组中只有一个数出现了奇数次,其他数都出现了偶数次,求这个数
public static int findOddNumber(int[] nums) {
int eor = nums[0];
for (int i = 1; i < nums.length; i++) {
eor ^= nums[i];
}
return eor;
}
(3)怎么把一个int类型的数,提取出最右侧的1来
N&((~N)+1)
(4)一个数组中,有两种数出现了奇数次,其余数出现了偶数次,求这两种数
设这两种数分别为a,b。先遍历数组,将数组里的数异或一遍,得出eor,eor=a^b。然后找出eor最右侧的1,此时a或者b在最右侧1的位置,必定一个是1,一个是0,否则该位置值为0,提取出最右侧的1为value值。再次遍历数组,将满足最右侧1位置为1的数异或,此时就能得到eor’,此时eor’为a或者b。再将eor’与eor异或,就能得到另一个数。
public static void findOddNumber(int[] nums){
if(nums!=null && nums.length>1){
int eor = 0;
for(int i = 0;i< nums.length;i++){
eor = eor ^ nums[i];
}
int num = eor&((~eor)+1);
int eor1=0;
for(int i =0;i<nums.length;i++){
if(((num&nums[i])!=0)){
eor1 = eor1^nums[i];
}
}
System.out.println("First: "+eor1+", Second:"+(eor^eor1));
}
}