0
点赞
收藏
分享

微信扫一扫

[每日算法220505] 二分法与复杂度

Sky飞羽 2022-05-06 阅读 73

今日题目

  • 二分法查找有序数组
  • 二分法解决局部最小值问题

今日心得

  • [局部最小]就是典型的纯算法题目,包含了很多隐藏的条件,如果不是事先准备。绝无可能在短时间内给出答案。
  • 抽象算法出来,需要通过一些具体的案例,用笔和纸演算。在脑中推算效率不高,大脑需要记忆太多内容。
  • 既要根据案例演算,也需要跳过细节,快速抽象出模型。然后通过对数器快速生成测试数据,根据测试结果调整算法。

算法编码

二分法查找有序数组

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("}"));
    }
}



举报

相关推荐

0 条评论