1 评估算法优劣的核心指标是什么
时间复杂度(流程决定)
额外空间复杂度(流程决定)(实现算法所需的额外空间,传入和返回的空间不算)
常数项时间(实现细节决定)(比拼常数项时间可以通过直接运行,看运行时间来比较)
2 常数时间的操作
如果一个操作的执行时间不以具体样本量为转义,每次执行时间都是固定时间。称这样的操作为常数时间的操作。
3 常见常数时间的操作
常见的算术运算、位运算、赋值、比较、自增自减操作等、数组寻址操作。
tips:
>> 带符号右移,右移后的符号位用原来的符号位填充
>>> 不带符号右移,右移后的符号位用 0 填充
4 选择排序
假设数组长度为n,则第一次遍历数组中下标为 0~ n-1的元素,找出其中的最小值,将其与下标为0的元素交换,第二次遍历数组中下标为 1~n-1 的元素,找出其中的最小值,将其与下标为1的元素交换,以此类推,每次都找出最小的并放在前面,直到数组排序完成。
5 冒泡排序
将数组相邻的两个数比较,较大的放在后方,第一轮遍历结束以后,数组中最大的值在数组的最后,第二轮遍历结束以后数组中第二大的值在数组的倒数第二位,以此类推直到数组排序完成。
选择排序先排最小的,冒泡排序先排最大的。
等差数列为 O(n^2)
5 插入排序
插入排序是先让数组的某个区间变得有序,然后逐渐扩大这个区间,当区间扩大到和数组一样大的时候,整个数组就有序了。
第一次看下标 0到0 区间是否有序,0到0只有一个数,必定有序,第二次看下标 0到1区间是否有序,如果下标1的数比下标0的数要小,则交换它们,交换后左边没有数了,0到1 区间有序了,第三次看下标 0到2 区间是否有序,如果下标2的数比下标1的数要大,因为左边已经是有序的了,所以可以得出下标 0到2 的区间已经有序了,如果下标2的数比下标1的要小,则将它们交换,交换后再与下标0的数比较,小了就换,大了就已经有序了。以此类推直到完成排序。
6 对数器
目标方法A和能正确完成功能的方法B,实现一个随机样本产生器,让方法A和方法B跑同样的样本对比结果,当某一样本结果不一致的时候,调试并改正方法,当样本数量很多而结果仍然一致时,可以确定方法A正确了。
7 二分
以在有序数组查找某一个数为例,每次都找当前区间的中点与之比较,如果目标比中点大,因为左边的数比中点要小,所以目标只可能出现在当前中点的右边,如果目标比中点小,因为右边的数比中点要大,所以目标只可能出现在当前中点的左边。这样逐步缩小区间直到找到目标或者没找到目标。
假设找到的次数为x,每次二分区间大小会变成上一次的 1/2,所以第x次的区间大小为 N*((1/2)^x)
,最差的情况时区间大小缩小至1才中止算法,此时 N*((1/2)^x)=1
,化简得N=2^x
,解得x=logN
(以2为底),所以二分法的时间复杂度为 O(logN)
(以2为底)。
能正确构建的左右两侧淘汰逻辑就可以二分。
8 异或运算
异或运算记成无进位相加:将数分解成二进制,逐位相加。但是 1+1也不进位。
异或性质:
1.0^N=N
N^N=0
2.异或满足交换律和结合律(异或与计算顺序无关)
9 异或运算的运用
Q1:不使用额外变量交换两个数
A:
a=a^b
b=a^b
a=a^b
解析:
a=a^b
b=a^b = a^b^b = a
a=a^b = a^b^a = b
注:仅当a与b不是同一对象时适用。
Q2:一个数组中有一种数出现了奇数次,其它数都出现了偶数次,找到并打印这种数
A:
public static void printOddTimesNum(int arr[]){
int eor=0;
for(int i=0;i<arr.length;i++){
eor=eor^arr[i]
}
System.out.println(eor);
}
Q3:找出int型数二进制表示的最右侧的1,
A: N&(~N+1)
Q4:一个数组中有两种数出现了奇数次,其它数都出现了偶数次,找到并打印这种数
A:
public static void printOddTimesNum(int arr[]){
int eor=0;
for(int i=0;i<arr.length;i++){
eor=eor^arr[i]
}
int rightOne=eor&(~N+1);//找到右边的第一个1
int newEor=0;
for(int i=0;i<arr.length;i++){
if((arr[i] & rightOne) == 0){
//找到rightOne右边第一个1位置上 不为1的arr,对他们异或,得出其中一个奇数
newEor=newEor^arr[i];
}
}
System.out.println(newEor +" " +eor^newEor);
}
Q5:找出数字中的1
A:
public static int bit1Count(int N){
int count=0;
while(N!=0){
rigthOne= N & (~N+1);
count++;
N=N^rightOne;
}
return count;
}
参考链接
1.左神算法
2.二分法logn时间复杂度解释