时间复杂度为O(NlogN)的排序

本文深入探讨了递归在寻找数组最大值和排序中的应用,如归并排序和快速排序。通过Master公式分析了算法的时间复杂度,解释了为何归并排序的时间复杂度为O(N*logN)而优于其他O(N^2)的排序方法。同时,介绍了荷兰国旗问题在优化排序中的作用,以及随机化快速排序如何降低最坏情况的时间复杂度到O(N*logN)。
摘要由CSDN通过智能技术生成

认识时间复杂度为O(NlogN)的排序

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

用递归方法找一个数组中的最大值,系统上是怎么做的?

求中点的时候一般不使用(L+R)/2,防止溢出。

public class Code08_GetMax {

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

    public static 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); //N/2 规模
        int rightMax = process(arr, mid + 1, R); //N/2 规模
        return Math.max(leftMax,rightMax);
    }
}

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)

母问题的数据规模为N,子问题的规模每一次都是 N/b, a表示被调用的次数,后面一项是除了子问题之外剩下的过程的时间复杂度。

上面例子的时间复杂度如何计算呢?

T(N) = 2*T(N/2) + O(1) a=2, b=2, d=0; log(2,2)=1 > 0,所以时间复杂度为O(N)

子问题规模一致,就可以使用master公式。

归并排序

public class Code04_MergeSort {

    public static void mergeSort(int[] arr){
        if (arr == null || arr.length < 2){
            return;
        }
        process(arr, 0, arr.length -1);
    }
    public static 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);
    }

    public static 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];
        }
    }
}

这个也可以是有master公式

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

a= 2, b= 2, d = 1; 复杂度为O(N*logN)

时间复杂度降低了

这个排序方法为什么时间复杂度更低呢?

因为使用了更少的比较行为。比较了N次才进行了一个数

总结

  • 整体就是一个简单递归,左边排好序,右边排好序,让整体有序

  • 让整体有序的过程里用了排外序方法

  • 利用master公式来求解时间复杂度

  • 归并排序的实质

例题1
  • 小和问题

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

【做到不是O(N2)时间复杂度的解法】

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

相当于计算右边有多少个数比它大,然后加起来就是数组的小和。

可以用归并的方式去计算小和,是不重复不遗漏的。排序的操作不能省略。

相等的时候先拷贝右组,才能知道有多少个数比它大。

例题2
  • 逆序对问题

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

荷兰国旗问题

问题1

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

1)设定一个边界标签。当遇到小于num的数时,就跟标签对应的数组数交换,这个标签就加1,遇到大于num的数,就直接无操作,标签不变。

问题2

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

设置两个标签,一个是小于的右边界,一个是大于的左边界。

小于num,则进行交换,标签加1,i++

等于num的,不交换。i++

大于num的,和大于区域的前一个交换,标签减1,并且i原地不动,因为后面交换的这个数还没有看过。

当i和大于标签撞到则过程停止。

快排

  • 将最后一个数拿出来当做num,然后将左侧的数组分成小于num和大于num的区域。然后交换这个数和大于区域的第一个数,然后这个num的顺序确定了,然后小于和大于的区域进行重复这个过程。

  • 快排2.0,利用了荷兰国旗问题的方法,分成了三个区域。

时间复杂度都是O(N2),因为能找到最差的情况。因此还需要改进。因为按照num的值划分,这个结果就可能会导致很坏,比如正好是最大的数。

  • 快排3.0

随机产生一个数,跟最后一位交换,然后作为num进行划分。

然后就变成了时间复杂度为 O(N*logN)

public class Code05_QuickSort {

    public static void quickSort(int[] arr) {
        if (arr == null || arr.length < 2) {
            return;
        }
        quickSort(arr, 0, arr.length - 1);
    }

    public static 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[l..r]的函数
    //默认以arr[r]做划分, arr[r] -> p   分三个区域  <p  ==p  >p
    //返回等于区域的左边界和右边界,res[l,r]
    public static int[] partition(int[] arr, int L, int R){
        int less = L - 1;

        int more = R;
        while (L < more){
            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};
    }

    public static void swap(int[] arr, int i, int j){
        int tmp = arr[i];
        arr[i] = arr[j];
        arr[j] = tmp;
    }
}
快排的空间复杂度

o(logN) 级别的

最坏的情况是o(N), 最好的情况每次都是中点num。随机情况,趋于o(logN)

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
时间复杂度为O(nlogn)的排序算法有希尔排序,堆排序快速排序和归并排序。其中归并排序是一种稳定的排序算法,而希尔排序、堆排序快速排序是不稳定的排序算法。 <span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [python-归并排序算法.docx](https://download.csdn.net/download/qq_43934844/87893705)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [时间复杂度O(nlogn)的排序算法](https://blog.csdn.net/qq_43533956/article/details/123978524)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [时间复杂度为O(nlogn)的排序算法](https://blog.csdn.net/qq_46130027/article/details/129765856)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

写代码的信哥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值