左神的算法

八大排序

1. 选择排序

    /***
     * 选择排序
     * @param arr 数组
     */
    public static void selectionSort(int[] arr) {
        if (arr == null || arr.length < 2) {
            return;
        }
        for (int i = 0; i < arr.length-1; i++) {
            int minIndex = i;
            for (int j = i + 1; j < arr.length; j++) {
                minIndex = arr[j] > minIndex ? minIndex : arr[j];
            }
            //如果第二部分的最小值小于arr[i]的值就交换
            //如果第二部分的最小值大于arr[i]的值,自己和自己交换
            swap(arr,i,minIndex);
        }
    }
    public static void swap(int[] arr,int i,int j){
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = arr[i];
    }

2. 冒泡排序

    /**
     * 冒泡排序
     * 第一种是正向控制次数
     *
     * @param arr
     */
    public static void bubblingSort(int[] arr) {
        if (arr == null || arr.length < 2) {
            return;
        }
        for (int i = 0; i < arr.length; i++) {
            for (int j = 0; j < arr.length - i - 1; j++) {
                swap(arr, j, j + 1);
            }
        }
    }

    /***
     * 冒泡排序
     * 第二中 反向控制次数
     * @param arr
     */
    public static void bubblingSort1(int[] arr) {
        if (arr == null || arr.length < 2) {
            return;
        }
        for (int e = arr.length - 1; e > 0; e--) {
            for (int i = 0; i < e; i++) {
                if(arr[i]>arr[i+1]){
                    swap(arr,i,i+1);
                }
            }
        }
    }
    public static void swap(int[] arr,int i,int j){
        arr[i] = arr[i]^arr[j];
        arr[j] = arr[i]^arr[j];
        arr[i] = arr[i]^arr[j];
    }
//异或^满足:
//1. 0^N=N,N^N=0
//2. 满足交换律,结合律

//交换a和b的值
int a = a^b;//a = a^b;
int b = a^b;//b = a^b = a^b^b = a^0 = a;
int a = a^b;//a = a^b = a^b^a = a^a^b = 0^b = b;


异或例题:已知在一个数组中

        (1) 只有一种数出现了奇数次,其他的都是偶数次,求这一种数

        (2) 有两种数出现了奇数次,其他都是偶数次,求这两个种数

异或^满足:
0^N=N,N^N=0
满足交换律,结合律

根据N^N=0,可以得出第一问

根据N^N=0,可以得出第二问最后的结果是e = a^b

    /***
     * 只有一种数出现了奇数次,其他的都是偶数次,求这一种数
     * @param arr 数组
     * @return 返回这个数
     */
    public static int way(int[] arr) {
        int e = 0;
        for (int i : arr) {
            e ^= i;
        }
        return e;
    }

    /***
     * 有两种数出现了奇数次,其他都是偶数次,求这两个种数
     * @param arr 数组
     */
    public static void way1(int[] arr) {
        int eor = 0;
        for (int i : arr) {
            eor ^= i;
        }
        //eor = a^b
        //eor!=0
        //eor的二进制某一位至少有一个1 ---> 要么是a的某一位,要么是b的某一位
        // 所以下一行代码是抽取eor的二进制最右边的1
        int rightOnly = eor & (~eor + 1);
        int e1 = 0;
        for (int i : arr) {
            if ((i & e1) == 0) {
                e1 ^= i;
            }
        }
        int e2 = eor ^ e1;
        System.out.println("e1--->" + e1 + "e2--->" + e2);
    }

3. 插入排序

    /***
     * 插入排序
     * @param arr
     */
    public static void insertionSort(int[] arr) {
        if (arr == null || arr.length < 2) {
            return;
        }
        //[0,0]
        //[0,1]
        //[0,2]
        // ...
        for (int i = 1; i < arr.length; i++) {
            for (int j = i - 1; j >= 0; j--) {
                if (arr[j] > arr[j + 1]) {
                    swap(arr, j, j + 1);
                }
            }
        }
    }

二分法的详解与扩展

在一个有序数组中,找某个数是否存在
在一个有序数组中,找>=某个数最左侧的位置

局部最小值问题:


        给定一个无序数组,相邻的数并不相等,那么就会产生很多个局部最小的值,找到任意一个局部最小的值的位置返回

(1)数组0位置:arr[0] < arr[1] --->arr[0]是局部最小值

(2)数组N-1位置:arr[N-1] < arr[N-2] --->arr[N-1]是局部最小值

(3)数组[1,N-2]范围:arr[i] < arr[i+1] && arr[i] < arr[i-1] ---> arr[i] 是局部最小值

 图解:

(1)数组0位置:arr[0] < arr[1] --->arr[0]是局部最小值

(2)数组N-1位置:arr[N-1] < arr[N-2] --->arr[N-1]是局部最小值

(3)数组[1,N-2]范围:arr[i] < arr[i+1] && arr[i] < arr[i-1] ---> arr[i] 是局部最小值

二分法求解:

    public static int way(int[] arr){
        int N = arr.length;
        if(arr==null||N == 0){
            return -1;
        }
        //arr[0] < arr[1],第一种情况
        if(arr[0] < arr[1] || arr.length==1){
            return 0;
        }
        //arr[N-1] < arr[N-2],第二种情况
        if(arr[N-1] < arr[N-2]){
            return arr[N-1];
        }
        //i = (1,N-2),第三种情况
        int left = 1;
        int right = N-2;
        while(left <= right){
            int mid = left + ((right-left) >> 1);
            if(arr[mid] > arr[mid-1]){
                right = mid-1;
            }else if(arr[mid] > arr[mid+1]){
                left = mid + 1;
            }else{
                //|      |            |    |
                //|      |      |     |    |
                //l     mid-1  mid  mid+1  r
                return mid;
            }
        }
        return -1;
    }

剖析递归行为时间复杂度的估算

  • master公式的使用:T(N) = a*T(N/b)+ O(N ^d)
  • 使用条件:子问题规模都相等
    • a:子问题等量的情况下,被调用的次数

    • O(N^d) :除去调用子问题之外,剩下过程的时间复杂度

    • T(N/b):子问题都是T(N/b)的子问题
      • \log_b{a}> d ->复杂度为0(N^\log_b{a})
      • \log_b{a} = d ->复杂度为0(N^d *logN)
      • \log_b{a} < d ->复杂度为O(N^d)

 例如:用递归方法找一个数组中的最大值

    public static int way(int[] arr){
        return process(arr,0,arr.length-1);
    }
    public static int process(int[]arr,int left, int right){
        if(left == right){
            return arr[left];
        }
        int mid = left + ((right-left)>>1);

        int leftMax = process(arr,left,mid-1);
        int rightMax = process(arr,mid+1,right);
        return Math.max(leftMax,rightMax);
    }

a = 2,b = 2,d = 0 

T(N) = 2*T(N/2) + O(1)

4. 归并排序

    public static void way(int[] arr) {
        process(arr, 0, arr.length - 1);
    }

    public static void process(int[] arr, int left, int right) {
        if (left == right) {
            return;
        }
        int mid = left + ((right - left) >> 1);
        //拆分
        //左边---中间
        process(arr, left, mid);
        //中间---右边
        process(arr, mid + 1, right);
        //排序
        merge(arr, left, mid, right);
    }

    public static void merge(int[] arr, int left, int mid, int right) {
        int[] help = new int[right - left + 1];
        int i = 0;
        int p1 = left;
        int p2 = mid + 1;
        //左右区间比较得到小的值放入help数值中
        while (p1 <= mid && p2 <= right) {
            help[i++] = arr[p1] <= arr[p2] ? arr[p1++] : arr[p2++];
        }
        //左区间可能有剩余
        while (p1 <= mid) {
            help[i++] = arr[p1++];
        }
        //右区间可能有剩余
        while (p2 <= right) {
            help[i++] = arr[p2++];
        }
        //返回给arr数组
        for (i = 0; i < help.length; i++) {
            arr[i + left] = help[i];
        }
    }

归并排序符合master公式:T(N) =2*T(\frac{N}{2}) + O(N),其中a = 2,b = 2,d = 2

归并排序的时间复杂度:O(N) = N*\log_{2}{N},空间复杂度:O(N)

小和问题

        在一个数组中,每一个数左边比当前数小的数累加起来,叫做这个数组的小和。求一个数组的小和

例子:[1,3,4,2,5]

  • 1左边比1小的数,没有
  • 3左边比3小的数,1
  • 4左边比4小的数,1、3
  • 2左边比2小的数,1
  •  5左边比5小的数,1、3、4、2

所以小和为1+1+3+1+1+3+4+2=16

换一种解法:

  • 1右边比1大的数:3,4,2,5
  • 3右边比3大的数:4,5
  • 4右边比4大的数:5
  • 2右边比2大的数:5
  • 5右边比5大的数:无

所以小和为:1*4+3*2+4*1+2*1 = 16

 

    /**
     * 小和数
     */
    public static int way1(int[] arr) {
        return process1(arr, 0, arr.length - 1);
    }

    public static int process1(int[] arr, int left, int right) {
        if (left == right) {
            return 0;
        }
        int mid = left + ((right - left) >> 1);
        //拆分
        //左边---中间
        return process1(arr, left, mid) + process1(arr, mid + 1, right) + merge1(arr, left, mid, right);
    }

    public static int merge1(int[] arr, int left, int mid, int right) {
        int[] help = new int[right - left + 1];
        int res = 0;
        int i = 0;
        int p1 = left;
        int p2 = mid + 1;
        //左右区间比较得到小的值放入help数值中
        while (p1 <= mid && p2 <= right) {
            res += arr[p1] < arr[p2] ? (right - p2 + 1) * arr[p1] : 0;
            help[i++] = arr[p1] <= arr[p2] ? arr[p1++] : arr[p2++];
        }
        //左区间可能有剩余
        while (p1 <= mid) {
            help[i++] = arr[p1++];
        }
        //右区间可能有剩余
        while (p2 <= right) {
            help[i++] = arr[p2++];
        }
        //返回给arr数组
        for (i = 0; i < help.length; i++) {
            arr[i + left] = help[i];
        }
        return res;
    }

 

逆序对问题

        在一个数组中,左边的数如果比右边的数大,则折两个数构成一个逆序对,请打印所有逆序对

eg:[1,3,4,2,5]

  • 1右边比1小的:无
  • 3右边比3小的:2
  • 4右边比4小的:2
  • 5右边比5小的:无

所有逆序对:1+1=2

 

相关推荐

  1. 贪心算法最优“与“右最优“

    2023-12-10 17:26:02       32 阅读
  2. 程序员实用

    2023-12-10 17:26:02       11 阅读
  3. DevOps与测试、方法

    2023-12-10 17:26:02       25 阅读

最近更新

  1. TCP协议是安全的吗?

    2023-12-10 17:26:02       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2023-12-10 17:26:02       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2023-12-10 17:26:02       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2023-12-10 17:26:02       18 阅读

热门阅读

  1. GO设计模式——1、简单工厂模式(创建型)

    2023-12-10 17:26:02       36 阅读
  2. 开源软件:JumpServer、DataEase、MeterSphere

    2023-12-10 17:26:02       44 阅读
  3. 【周报2023.12.09】

    2023-12-10 17:26:02       42 阅读
  4. c++ 类和对象-封装意义一

    2023-12-10 17:26:02       38 阅读
  5. 用格里高利公式求给定精度的PI值

    2023-12-10 17:26:02       41 阅读
  6. Vue笔记(一)基础

    2023-12-10 17:26:02       42 阅读
  7. ReactNative如何调用自定义的原生模块

    2023-12-10 17:26:02       44 阅读
  8. python的列表list

    2023-12-10 17:26:02       40 阅读
  9. vue中侦听器

    2023-12-10 17:26:02       38 阅读
  10. Spring中@Contorller和@ResController的区别

    2023-12-10 17:26:02       42 阅读
  11. 微信小程序页面跳转方法

    2023-12-10 17:26:02       36 阅读
  12. (Spring学习07)Spring之启动刷新过程源码解析

    2023-12-10 17:26:02       37 阅读