二分、复杂度、动态数组、哈希表

1.二分法 不一定一定有序,比如找局部最小值就可以不有序

有序数组中找到num

对数器生成随机数组来校验find()方法是否正确

public class Code01_BSExist {
    //有序数组中找到num
    //arr保证有序
    public static boolean find(int[] arr, int num) {
        if (arr == null || arr.length == 0) {
            return false;
        }
        int L = 0;
        int R = arr.length - 1;
        //arr[0,N-1]  arr[L,R]
        while (L <= R) {
            int mid = (L + R) / 2;
            if (arr[mid] == num){
                return true;
            }else if(arr[mid]<num){//如果arr[mid]
                L = mid + 1;
            }else {
                R = mid - 1; //如果arr[mid]>num则arr[mid]右边的数都不符合,都排除掉R = mid - 1
            }
        }
        return false;
    }
    // for test 暴力测试
    public static boolean test(int[] sortedArr, int num) {
        for (int cur : sortedArr) { //增强for循环遍历
            if (cur == num) {
                return true;  //找到num返回true
            }
        }
        return false; //找不到返回false
    }

    // for test
    public static int[] generateRandomArray(int maxSize, int maxValue) {
        //生成随机大小的随机数组
        int[] arr = new int[(int) ((maxSize + 1) * Math.random())];
        for (int i = 0; i < arr.length; i++) {//给数组每一个位置生成随机数
            arr[i] = (int) ((maxValue + 1) * Math.random()) - (int) (maxValue * Math.random());
        }
        return arr;
    }

    public static void main(String[] args) {
        int testTime = 500000;
        int maxSize = 10;
        int maxValue = 100;
        boolean succeed = true;
        for (int i = 0; i < testTime; i++) {
            int[] arr = generateRandomArray(maxSize, maxValue);
            Arrays.sort(arr);//Arrays.sort默认从小到大排序
            int value = (int) ((maxValue + 1) * Math.random()) - (int) (maxValue * Math.random());//生成一个随机num
            if (test(arr, value) != find(arr, value)) {
                System.out.println("出错了!");
                succeed = false;
                break;
            }
        }
        System.out.println(succeed ? "Nice!" : "Fucking fucked!");
    }
}

二分、复杂度、动态数组、哈希表_第1张图片

有序数组中找到>=num最左的位置

package class03;

import java.util.Arrays;

public class Code02_BSNearLeft {
    //有序数组中找到>=num最左的位置  最左不小于num的索引位置
    public static int mostLeftNoLessNumIndex(int[] arr, int num) {
        if (arr == null || arr.length == 0) {
            return -1;
        }
        int L = 0;
        int R = arr.length - 1;
        int ans = -1;
        //1.有序数组中找到>=num最左的位置
        /*while (L <= R) {
            int mid = (L + R) / 2;
            if (arr[mid] >= num){
                ans = mid;//不断更新ans值,也就是索引位置,如果arr[mid] = num成立,也就是找到了num值,最后一次更新的位置就是要找的位置
                R = mid - 1;
            }else {
                L = mid + 1;
            }
        }*/
        
        //2.有序数组中找到<=num最右的位置
        while (L <= R) {
            int mid = (L + R) / 2;
            if (arr[mid] > num) {
//不断更新ans值,也就是索引位置,如果arr[mid] == num成立,也就是找到了num、mid值,最后一次更新的位置就是要找的位置mid,
//如果在这个过程中始终没有找到或者arr[mid] > num就不更新ans值,比如如果数组{1,2,3,4}我们找100,
// 一直都找不到arr[mid]>100的值ans值就一直不更新,但是我们这里是要找100在最右侧出现,所以当arr[mid] <= 100时就不断更新ans值,不断去找寻这个arr[mid]==100的位置,所以条件一不更新,条件二不断的更新ans值,直到找到合适的mid值,这个mid就是我们要找的位置
//如果这个数组中的全部数都大于num,就一直执行arr[mid] > num这个条件,ans一直不更新,最后ans的返回值就是-1;
                R = mid - 1;
            } else {
                ans = mid;
                L = mid + 1;
            }
        }
        return ans;
    }
//==============================================================================================
    // 1.for test 有序数组中找到>=num最左的位置
/*    public static int test(int[] arr, int value) {
        for (int i = 0; i < arr.length; i++) {
            if (arr[i] >= value) {
                return i;
            }
        }
        return -1;
    }*/
    
    //2.有序数组中找到<=num最右的位置
    public static int test(int[] arr, int value) {
        for (int i = arr.length-1; i > 0; i--) {
            if (arr[i] == value) {
                return i;
            }
        }
        return -1;
    }
//===================================================================================================
    // for test
    public static int[] generateRandomArray(int maxSize, int maxValue) {
        int[] arr = new int[(int) ((maxSize + 1) * Math.random())];
        for (int i = 0; i < arr.length; i++) {
            arr[i] = (int) ((maxValue + 1) * Math.random()) - (int) (maxValue * Math.random());
        }
        return arr;
    }
    
public static int test(int[] arr, int value) {
        //i >= 0这里i一定要把等于0写上,因为如果左右出现的数在0,就需要返回0索引了
        for (int i = arr.length-1; i >= 0; i--) {
            if (arr[i] <= value) {
                return i;
            }
        }
        return -1;
    }


    public static void main(String[] args) {
        int testTime = 50;
        int maxSize = 10;
        int maxValue = 100;
        boolean succeed = true;
        for (int i = 0; i < testTime; i++) {
            int[] arr = generateRandomArray(maxSize, maxValue);
//            int[] arr = {1, 2, 3, 5, 6, 6, 6, 6, 7, 8, 9};
            Arrays.sort(arr);
            int value = (int) ((maxValue + 1) * Math.random()) - (int) (maxValue * Math.random());
//            int value = 6;
            if (test(arr, value) != mostLeftNoLessNumIndex(arr, value)) {
                printArray(arr);
                System.out.println(value);
                System.out.println(test(arr, value));
                System.out.println(mostLeftNoLessNumIndex(arr, value));
                succeed = false;
                break;
            }
        }
        System.out.println(succeed ? "Nice!" : "Fucking fucked!");
    }
}

二分、复杂度、动态数组、哈希表_第2张图片

局部最小值问题 需要数与数之间相邻不等

package class03;
public class Code03_BSAwesome {
    public static int oneMinIndex(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 - 1] < arr[N - 2]) {
            return N - 1;
        }
        int L = 0;
        int R = N - 1;//L...R肯定有局部最小
        while (L < R - 1) {
            int mid = (L + R) / 2;
            if (arr[mid] < arr[mid - 1] && arr[mid] < arr[mid + 1]) {
                return mid;
            } else {
                //三种情况 1.左>mid mid>右、2.左右、3.左
                //2.3情况
                if (arr[mid] > arr[mid - 1]) {
                    R = mid - 1; //局部最小值肯定在左边,右边可能有可能没有,但是只要一个局部最小,所以把右边的值都砍掉
                    // 因为arr[mid]大于arr[mid-1]的值
                }
                //1.情况
                else {//arr[mid] >arr[mid+1]  左边的数都砍掉,局部最小肯定在右边,左边可能有可能没有
                    L = mid + 1;
                }
            }
        }
        return arr[L] < arr[R] ? L : R;
    }

    //生成随机数组,且相邻数不相等
    public static int[] randomArray(int maxLen, int maxValue) {
        int len = (int) (Math.random() * maxLen);
        int[] arr = new int[len];
        if (len > 0) {
            arr[0] = (int) (Math.random() * maxValue);
            for (int i = 1; i < len; i++) {
                //先执行一次,然后判断,如果arr[i] == arr[i - 1],就重做,如果不相等就继续执行下一次for循环,重复上述过程
                do {
                    arr[i] = (int) (Math.random() * maxValue);
                } while (arr[i] == arr[i - 1]);
            }
        }
        return arr;
    }

    public static boolean check(int[] arr, int minIndex) {
        if (arr.length == 0) {
            return minIndex == -1;
        }
        int left = minIndex - 1;
        int right = minIndex + 1;
        //先判断是否越界,如果没越界就比较一下
        boolean leftBigger = left >= 0 ? arr[left] > arr[minIndex] : true;
        boolean rightBigger = right < arr.length ? arr[right] > arr[minIndex] : true;
        return rightBigger && leftBigger;
    }

    //打印数组
    public static void printArray(int[] arr) {
        for (int num : arr) {
            System.out.print(num + " ");
        }
        System.out.println();
    }

    public static void main(String[] args) {
        int maxLen = 100;
        int maxValue = 20;
        int testTimes = 1000000;
        System.out.println("测试开始");
        for (int i = 0; i < testTimes; i++) {
            int[] arr = randomArray(maxLen, maxValue);
            int ans = oneMinIndex(arr);
            if (!check(arr, ans)) {
                printArray(arr);
                System.out.println(ans);
                break;
            }
        }
        System.out.println("测试结束");
    }
}

二分、复杂度、动态数组、哈希表_第3张图片

2.时间复杂度

等差数列中时间复杂度为O(N)

二分中时间复杂度为
O ( log ⁡ 2 N ) O (\log_2{N}) O(log2N)
常数操作时间复杂度O(1)

最差的情况来估计时间复杂度

3.动态数组

在java中arraylist是动态数组,时间复杂度是O(1),每往数组中新加数,如果下标越界,就会进行数组扩容,数组长度扩容到原本的2倍,扩容的过程是一个等差数列,扩容前的数组元素原封不动复制到扩容后的数组,然后新加的元素插入到新数组后面

o(1)0(n)

4.哈希表与有序表

哈希表

  • 哈希表的增删改查时间复杂度都为O(1),但是这个常数时间是比较大的

  • 哈希表中按值传递,不看地址是否不同

public static class Node {
        public int value;

        public Node(int v) {
            value = v;
        }
    }

    public static void main(String[] args) {
        HashMap<String, String> map = new HashMap<>();
        map.put("name", "我是佩萁");
        System.out.println(map.containsKey("name"));//true
        System.out.println(map.get("name"));//我是佩萁
        map.put("name", "小熊");
        System.out.println(map.get("name"));//小熊


        HashMap<Integer, String> map2 = new HashMap<>();
        map2.put(123456, "我是1234567");
        Integer a = 123456;
        Integer b = 123456;
        //==比较的是内存地址  如果要比较后面的内容应该写equals方法
        System.out.println(a == b);//false
        //哈希表只看后面的内容不看内存地址是否不一样
        System.out.println(map2.containsKey(a));//true
        System.out.println(map2.containsKey(b));//true

        //如果是自己写的类型的话,就不是单看值是否相等了,而是看地址是否一样
        Node node1 = new Node(1);
        Node node2 = new Node(1);
        HashMap<Node,String> map3 = new HashMap<>();
        map3.put(node1,"我是node1");
        System.out.println(map3.containsKey(node1));//true
        System.out.println(map3.containsKey(node2));//false
    }

有序表

  • 有序表的每步时间复杂度都为O(N)

  • 有序表传入的key值必须可以比较,如果不是基础类型,而是自己定义的类型,需要说明怎么比较,否则会报错

        TreeMap<Integer,String> treeMap1 = new TreeMap<>();
        treeMap1.put(3,"我是3");
        treeMap1.put(0,"我是0");
        treeMap1.put(6,"我是6");
        treeMap1.put(4,"我是4");
        treeMap1.put(5,"我是5");
        treeMap1.put(8,"我是8");
        System.out.println(treeMap1.containsKey(4));//true
        System.out.println(treeMap1.get(4));//我是4
        System.out.println(treeMap1.firstKey());//0 找到最小的key值
        System.out.println(treeMap1.lastKey());//8 找到最大的key值
        System.out.println(treeMap1.floorKey(5));//5  <=5 离5最近的key
        System.out.println(treeMap1.ceilingKey(6));//6  >=6 离6最近的key

你可能感兴趣的:(算法笔记,散列表,算法,java)