算法-java
一、 二分查找
1. 二分-左闭右闭
public Integer erfen(int target,int arr[]){
int i = 0;
int j = arr.length-1;
int m = 0;
while(i<=j){
m=(i+j)>>>1;
if (target<arr[m]){ // 目标在中间数左侧,缩小右边界
j=m-1;
}else if (arr[m]<target){ // 目标在中间数右侧,缩小左边界
i=m+1;
}else { // 目标就是中间数
return m;
}
}
return -1;
}
注:
- 算法解释:用i和j代表序列的两边,通过不断取中间数缩小目标范围,只要i不在j右侧就一直进行。(i和j都参与比较,左闭右闭运算)
- i<=j 代表i和j指向的元素也会参与比较
- i<j 代表只有m参与比较
- java把二进制最高位看做符号位,因此,当两个足够大的数相加时,可能会最高位进一,导致得数为负数。
- ‘>>>1’ 无符号右移1位,最高位取0补位,因此,无符号右移1位替代除以2,可以避免最高位进一变负数的情况。
- 编程习惯:升序的数组,都用小于号。可以更清晰的看到数字的位置,如:
target < arr[m],arr[m] < target
2. 二分-左闭右开
public Integer newErfen(int target,int arr[]){
int i = 0;
int j = arr.length; // j取索引外的值
int m = 0;
while(i<j){ // 只要i和j不相交就一直循环
m=(i+j)>>>1;
if (target<arr[m]){
j=m; // 直接j=m
}else if (arr[m]<target){
i=m+1;
}else {
return m;
}
}
return -1;
}
注:
- 左闭右开,i参与比较,j不再参与比较
- 因为j=m,若还while(i<=j),当查找一个不在数组的数时,最后i和j相等,即m=j=(i+j)>>>2,导致m一直不变,陷入死循环。
3. 二分-改进版
public Integer newErfen2(int target,int arr[]){
int i = 0;
int j = arr.length; // j不可能指向目标
while(1<(j-i)){ // 一直循环到区间范围只剩1才结束
int m = (i+j)>>>1;
if (target < arr[m]) {
j = m; // 缩小右边界
}else {
i = m; // 缩小左边界
}
}
if (arr[i] == target) {
return i;
}else{
return -1;
}
}
注:
- 左闭右开算法
- 循环只缩小查找范围,不找目标,在循环外比较a[i]与target值
- 循环内的比较次数比改进前少了
- 时间复杂度稳定为Θ(log₂n)
二、算法性能
1.时间复杂度
定义:一个算法的执行,随着数据规模增大,而增长的时间成本。不依赖于环境因素(软件因素和硬件因素)
设:
c,c1,c2都是一个常数
f(n)实际执行的代码行数与n的行数
g(n)是经过化简,变化趋势与f(n)一致的n的函数
1.1 渐进上界
从某个常数n开始,cg(n)总是位于f(n)上方,那么记作 O(g(n)),通常表示为该算法最差的运行情况
例如:
- 设f(n)=3n+3,g(n)=n 取c=4,在n=3之后,g(n)可以作为f(n)的渐进上界,因此表示法写作O(g(n))
- 设 f(n)=5 * floor(log₂n)+9, 则g(n)=log₂n, 即渐进上界O(log₂n)
- 已知f(n),求g(n):
3.1 去除相加的常量,如2里面的+9
3.2 去除相乘的常量,如2里面的5*
3.3 去除低次项,如:f(n)=n³+n²,则n²可以去掉,因为n²增长规模远小于n³
1.2. 渐进下界
从某个常数n开始,cg(n)总是位于f(n)下方,那么记作 Ω(g(n)),通常表示为该算法最好的运行情况
1.3. 渐进紧界
从某个常数n开始,f(n)总是位于c1g(n)和c2g(n)之间,那么记作偶θ(g(n))
2. 空间复杂度
定义:一个算法执行随数据规模增大,而增长的额外空间成本,一般也用O表示
一般指该算法的所有变量共占了多少字节。