今日题目
今日心得
- [局部最小]就是典型的纯算法题目,包含了很多隐藏的条件,如果不是事先准备。绝无可能在短时间内给出答案。
- 抽象算法出来,需要通过一些具体的案例,用笔和纸演算。在脑中推算效率不高,大脑需要记忆太多内容。
- 既要根据案例演算,也需要跳过细节,快速抽象出模型。然后通过对数器快速生成测试数据,根据测试结果调整算法。
算法编码
二分法查找有序数组
package find;
import math.Comp;
import sort.InsertSort;
/**
* @ClassName BsFind
* @Description 有序数组中,二分法找数字
* @Author kouryoushine
* @Date 2022/5/5 20:59
* @Version 1.0
*/
/**
* 无论查找=,>=,<=哪个结果,最终是二分法什么时候返回的差别。过程都是不断到的重复二分查找,达成条件跳出。
*/
public class BsFind {
public static void main(String[] args) {
int MaxLength=300;
int MaxValue=100;
int testTimes=10000;
for (int i = 0; i < testTimes; i++) {
int[] arr = Comp.randomIntArray(MaxLength,MaxValue);
InsertSort.InsertSort(arr);
int num= (int)(Math.random()*999);
if (firstNumIndex(arr, num)!=firstNumIndex(arr, num)){
System.out.println("wronng ");
}
}
}
public static boolean exist(int [] arr,int num){
for(int i=0;i<arr.length;i++){
if (arr[i]==num){
return true;
}
}
return false;
}
public static int firstNumIndex(int [] arr,int num){
for(int i=0;i<arr.length;i++){
if (arr[i]==num){
return i;
}
}
return -1;
}
public static boolean find(int[] arr, int num) {
if (arr == null || arr.length < 1) {
return false;
}
int L = 0;
int R = arr.length-1;
//在[L,R]范围内查找
while (L<=R){
int midIndex = (L + R) / 2;
if (num<arr[midIndex]){
R=midIndex-1;
}else if(num>arr[midIndex]){
L=midIndex+1;
}else {
return true;
}
}
return false;
}
/**
* 查询>=num的最左侧的值
* - 原理:同二分法一样,不断遍历仅仅改变条件。获取到最后一个>=num的midIndex即可。
* @param arr
* @param num
* @return
*/
public static int mostLeftNoLessNumIndex(int[] arr, int num) {
int L = 0;
int R = arr.length-1;
int midIndex=-1;
if (arr == null || arr.length < 1) {
return midIndex;
}
//在[L,R]范围内查找
while (L<=R){
midIndex = (L + R) / 2;
if (num<=arr[midIndex]){
R=midIndex-1;
}else if(num>arr[midIndex]){
L=midIndex+1;
}
}
return midIndex;
}
}
查找局部最小值
package find;
/**
* @ClassName MinLocal
* @Description TODO
* @Author kouryoushine
* @Date 2022/5/5 22:47
* @Version 1.0
*/
//tip:理解最小局部的值,并且深入理解边界条件,并抽象简化是算法的难点。如果get不到这些抽象的条件,是无论如何写不出来结果的。
// 二分法不一定只能用于有序的场景。只要任何一次有目标值,都可以使用二分法。
public class MinLocal {
public static void main(String[] args) {
int maxLength=100;
int maxValue=1000;
int testTimes=100000;
// int arr[] = {235,106,856,483,213,264,108,56,476,450,488,872};
// minLocalNum(arr);
for (int i = 0; i <testTimes; i++) {
int [] arr = randomArray(maxLength,maxValue);
int index = 0;
try {
index = minLocalNum(arr);
} catch (Exception e) {
// System.out.println(Arrays.asList(arr));
printArray(arr);
}
if(!isMinLocal(arr,index)){
System.out.println("Not MiniLocal!!! i="+index);
printArray(arr);
break;
}
}
}
//局部最小定义
// 数组无序,相邻不等
// 0<1 0是局部最小
// N-2 > N-1 N-1局部最小
// 注:0<1 && N-2<N-1时候,说明必然之间必然存在局部最小
// i<i-1 && i <i +1 ,则i是局部最小
//知识点: 斜下箭头\ ,协商箭头/之间必然存在局部最小。(如果数组任意两组相邻的数,如果左边相邻数Desc,右侧Asc,则比存在局部最小)
//找到任意一个局部最小的数字
public static int minLocalNum(int [] arr){
if(arr==null || arr.length==0){
return -1;
}
int N= arr.length;
if (N==1){
return 0;
}
if(arr[0]<arr[1]){
return 0;
}
if(arr[N-2]>arr[N-1]){
return N-1;
}
int L=0;
int R=N-1;
int ans=-1;
while (L<=R){
int mid=(L+R)/2;
if(mid==0){ //刚好剩两个数,mid取了0位置时避免报错。这种情况也可以根据L,R谁小返回谁(但不太容易理解,引入新的知识复杂化了)
mid=1;
}
if(arr[mid]<arr[mid-1] && arr[mid]<arr[mid+1]){
ans=mid;
break;
}
if(arr[mid-1]<arr[mid]){
R=mid-1;
continue;
}
if(arr[mid+1]<arr[mid]){
L=mid+1;
continue;
}
}
return ans;
}
//for test
public static boolean isMinLocal(int [] arr,int index){
if(arr==null || arr.length==0){
return -1==index;
}
int N= arr.length;
if (N==1){
return true;
}
if(arr[0]<arr[1]){
return 0==index;
}
if(arr[N-2]>arr[N-1]){
return N-1==index;
}
if(arr[index]<arr[index-1] && arr[index]<arr[index+1]){
return true;
}else {
return false;
}
}
//for test
public static int [] randomArray(int maxLength,int maxValue){
int len=(int)(Math.random()*maxLength);
int [] arr= new int[len];
for (int i = 1; i < len; i++) {
arr[0]=(int)(Math.random()*maxValue);
do {
arr[i]=(int)(Math.random()*maxValue);
}while (arr[i]==arr[i-1]);//相邻不重复
}
return arr;
}
public static void printArray(int []arr){
StringBuffer stringBuffer=new StringBuffer("{");
for (int i = 0; i < arr.length; i++) {
stringBuffer.append(","+arr[i]);
System.out.println("index="+i+" value="+arr[i]);
}
System.out.println(stringBuffer.append("}"));
}
}