认识O(NlogN)的排序

发布于:2022-12-19 ⋅ 阅读:(458) ⋅ 点赞:(0)

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

用递归方法找一个数组中的最大值,系统上到底是怎么做的?
master公式的使用
T(N) = a*T(N/b) + O(N^d)
1.log(b,a) > d ->复杂度为O(N^log(b,a))
2…log(b,a) = d ->复杂度为O(N^d * logN)
3…log(b,a) < d ->复杂度为O(N^d)

master公式 的理解:
T(N)=a*T(N/b)+O(N^d)
总规模是n的数据量
等量的子规模为N/b
a指调用了a次
O(N^d)指除去子规模之后剩下规模的时间复杂度

求最大值

递归方法求最大值

 int getMax(int[] arr) {
        return process(arr, 0, arr.length - 1);
    }

    int process(int[] arr, int L, int R) {
        if (L == R) {//整个数组只含有一个数值,直接返回
            return arr[L];
        }
        int mid = L + ((R - L) >> 1);//中点
        int leftMax = process(arr, L, mid);
        int rightMax = process(arr, mid + 1, R);
        return Math.max(leftMax, rightMax);
    }

归并排序

工作原理:
整体就是一个简单递归,左边排好序,右边排好序,然后使得整体有序

时间复杂度O(NlogN)

下列代码,利用master公式可得
a=2,b=2,d=1 此时log(2,2) = 1,可得时间复杂度为O(NlogN)

void process(int[] arr, int L, int R) {
        if (L == R) {//整个数组只含有一个数值,直接返回
            return;
        }
        int mid = L + ((R - L) >> 1);//中点
        process(arr, L, mid);//使得左侧有序
        process(arr, mid + 1, R);//使得右侧有序
        merge(arr, L, mid, R);
    }
/*创建一个辅助空间使得整个数组有序*/
    void merge(int[] arr, int L, int M, int R) {
        int[] help = new int[R - L + 1];//创建辅助空间
        int i = 0;
        int p1 = L;
        int p2 = M + 1;
        while (p1 <= M && p2 <= R) {
            help[i++] = arr[p1] <= arr[p2] ? arr[p1++] : arr[p2++];
        }
        while (p1 <= M) {
            help[i++] = arr[p1++];
        }
        while (p2 <= R) {
            help[i++] = arr[p2++];
        }
        for (i = 0; i < help.length; i++) {
            arr[L + i] = help[i];
        }
    }

执行过程:
在这里插入图片描述

归并排序的扩展

小和问题:

在一个数组中,每一个数左边比当前数小的数累加起来就,叫做这个数组的小和。求一个数组的小和。
[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

int smallSum(int[] arr) {
        if (arr == null || arr.length < 2) {
            return 0;
        }
        return process2(arr, 0, arr.length - 1);
    }

    int process2(int[] arr, int l, int r) {
        if (l == r) {
            return 0;
        }
        int mid = 1 + ((r - 1) >> 1);
        return process2(arr, l, mid)
                + process2(arr, mid + 1, r)
                + merge1(arr, l, mid, r);
    }

    int merge1(int[] arr, int L, int m, int r) {
        int[] help = new int[r - L + 1];//创建辅助空间
        int i = 0;
        int p1 = L;
        int p2 = m + 1;
        int res = 0;
        while (p1 <= m && p2 <= r) {
            res += arr[p1] < arr[p2] ? (r - p2 + 1) * arr[p1] : 0;
            help[i++] = arr[p1] < arr[p2] ? arr[p1++] : arr[p2++];
        }
        while (p1 <= m) {
            help[i++] = arr[p1++];
        }
        while (p2 <= r) {
            help[i++] = arr[p2++];
        }
        for (i = 0; i < help.length; i++) {
            arr[L + i] = help[i];
        }
        return res;
    }

如果不理解的话,这里建议自己画一下程序的执行过程(确实难以理解的话,可以私信

逆序对问题:

在一个数组中,左边的数如果比右边的数大,则这两个数构成一个逆序对。

荷兰国旗问题

问题一
给定一个数组arr,和一个数num,请把小于等于num的数放在数组的左边,大于num的数放在数组的右边。要求额外空间复杂度O(1),时间复杂度O(N)
问题二(荷兰国旗问题)
给定一个数组arr,和一个数num,请把小于num的数放在数组的左边,等于num的数放在数组的中间,大于num的数放在数组的右边。要求额外空间复杂度O(1),时间复杂度O(N)

思路
1.arr[i]<num,arr[i]和小于区域的下一个数做交换,小于区域右扩,i++
2.arr[i]==num ,i++
3.arr[i]>num,arr[i]和大于区域前一个做交换,大于区域左扩,i不变
理解了问题二的解决方法,问题一就自然而然解决了

快速排序1.0

时间复杂度O(N^2)
给定一个数组arr,将该数组最后一个数据假设为num,将该数组分成左右两部分,左边为小于等于num,右边为大于num,然后将num与右边第一个数做交换,然后重复执行以上步骤

快速排序2.0

时间复杂度O(N^2)
给定一个数组arr,将该数组最后一个数据假设为num,将该数组分成左中右三部分,左边为小于num,中间为等于num,右边为大于num,然后将num与右边第一个数做交换,然后重复执行以上步骤

快速排序3.0

时间复杂度O(NlogN)
给定一个数组arr,随机选取数组一个数据假设为num,,并将num与数组最后一个数据做交换,然后将该数组分成左中右三部分,左边为小于num,中间为等于num,右边为大于num,然后将num与右边第一个数做交换,然后重复执行以上步骤

void quickSort(int[] arr,int L,int R){
        if (L < R){
            swap(arr,L+(int)(Math.random()*(R - L +1)),R);
            int[] p = partition(arr,L,R);
            quickSort(arr,L,p[0]-1);//<区
            quickSort(arr,p[1]+1,R);//>区
        }
    }
    /**
     * 这是一个处理arr[1...r]的函数
     * 默认以arr[r]做划分,arr[r] ->p    <p  ==p   >p
     * 返回等于区域(左边界,右边界),所以返回一个长度为2的数组res,res[0]res[1]
     * */
    int[] partition(int[] arr,int L,int R){
        int less = L-1;//<区右边界
        int more = R;//>区左边界
        while (L < more){//L表示当前数的位置 arr[R] ->  划分值
            if (arr[L] < arr[R]){//当前数 < 划分值
                swap(arr,++less,L++);
            } else if (arr[L] > arr[R]) {//当前数 > 划分值
                swap(arr,--more,L);
            }else {//当前数 == 划分值
                L++;
            }
        }
        swap(arr,more,R);
        return new int[] {less + 1,more};
    }
    void swap(int[] arr,int i,int j){
        int tmp = arr[i];
        arr[i] = arr[j];
        arr[j] = tmp;
    }

网站公告

今日签到

点亮在社区的每一天
去签到