如何快速提高算法效率?
在某些实际问题中,我们可以考虑二分法。
1. 二分法求解问题
1.1 存在问题
题目:查找某个数在有序数组中是否存在
1.1.1 算法原理
找到数组中间位置mid,对比 arr[mid]=x 和题目所求的数 num 的大小。如果 num>x,说明所找的数一定在数组右半部分;如果 num<x,说明所找的数一定在数组左半部分。接着在右半部分/左半部分继续进行二分,直到找到 x 或二分到只剩一个数。
1.1.2 算法过程图示
假设有序数组为[2 3 4 8 16 19 23 35 39 45 56],num=45。
数组长度为N=11。
首先min=0,max=10,mid=N/2=11/2=5,找到arr[5]=19,比较19<45,因此45如果存在,那么一定是在19右侧的数组中,因为数组是有序的,19左侧的数组是全部小于19的。接着min=mid+1=6,max=10,mid=(min+max)/ 2 = 8 ,找到 arr[8]=39,比较39<45,因此45如果存在,那么一定是在39右侧的数组中。接着min=mid+1,max=10,mid=(min+max)/ 2 = 9,找到 arr[9]=45,因此找到所求数字。
时间复杂度为O(logN)。
1.1.3 算法核心代码
import java.util.Arrays;
public class BSExist {
public static boolean exist(int[] sortedArr, int num) {
if(sortedArr==null||sortedArr.length==0){
return false;
}
int min=0;
int max=sortedArr.length-1;
int mid=0;
while(min<max) {
mid=(min+max)/2;
if(sortedArr[mid]==num) {
return true;
}else if(sortedArr[mid]>num) {
max=mid-1;
}else {
min=mid+1;
}
}
return sortedArr[min]==num;
}
public static void main(String[] args) {
int[] arr = {2,3,4,8,16,19,23,35,39,45,56};
System.out.println(Arrays.toString(arr));
System.out.print(exist(arr,9));
}
}
1.2 无序数组局部最小问题
1.2.1 局部最小问题
数组长度为N,无序,任意相邻数不相等
①数组头部(下标为0、1):如果arr[0]<arr[1],则arr[0]是局部最小
②数组尾部(下标为N-2、N-1):如果arr[N-1]<arr[N-2],则arr[N-1]是局部最小
③数组中部(非头部,非尾部):如果arr[x]<arr[x-1],arr[x]<arr[x+1],则arr[x]是局部最小
1.2.2 算法原理
第一步判断①、②情况能否找到局部最小,如果不能,则局部最小一定在中间,如下图所示,趋势一开始是下降,最后是上升,说明中间部分一定存在极值,即局部最小。
接着二分数组,按照第一步继续判断局部最小,以此类推。
1.2.3 算法过程图示
例如数组[6 5 1 2 4 3 7 8]
第一步判断下标为0、1以及下标为6、7是否存在局部最小,如图可知不存在。接着对数组进行二分,min=0,max=7,mid=(0+7)/2=3,此时查看下标为3的数arr[3]=2左右两边的变化趋势,如图得到左边可能存在局部最小。再对左边部分数组进行二分,min=0,max=3,mid=(0+3)/2=1,此时查看下标为1的数arr[1]=5左右两边的变化趋势,如图得到右边可能存在局部最小,此时只有三个数,且变化趋势满足局部最小,则中间的数arr[2]=1为局部最小。
时间复杂度为O(N)。
1.2.4 算法核心代码
public class BSAwesome {
public static int getLessIndex(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; //设置最左,方便二分
int right =arr.length-2; //设置最右,方便二分
int mid = 0;
while(left<right) {
mid = (left+right)/2; //二分
if(arr[mid]>arr[mid-1]) { //二分左侧为上升趋势则说明左侧存在局部最小
right = mid -1;
}else if(arr[mid]>arr[mid+1]) { //二分右侧为下降趋势则说明右侧存在局部最小
left=mid+1;
}else {
return mid;//找到局部最小
}
}
return left;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
int[] arr= {9,5,2,9,8,6,7,1,10};
System.out.print(getLessIndex(arr));
}
}