为什么合并排序和快速排序的平均时间复杂度都是O(nlogn)

合并排序和快速排序的平均时间复杂度都是O(nlogn)的原因:它们都采用了分治策略,将问题分解成子问题并递归地求解。合并排序算法将待排序序列不断划分成两个子序列,直到每个子序列只有一个元素;快速排序算法每次分割操作将数组分成两个长度较为相等的部分。

一、合并排序和快速排序的平均时间复杂度都是O(nlogn)的原因

合并排序和快速排序的平均时间复杂度都是 O(nlogn),是因为它们都采用了分治策略,将问题分解成子问题并递归地求解。

合并排序

当我们计算合并排序的时间复杂度时,需要考虑拆分和合并这两部分。在每一次拆分中,我们将输入数组划分成两个子数组,其中每个子数组的长度为n/2,因此我们需要做log(n)轮拆分。每一轮拆分的时间复杂度为O(n),因此平均时间复杂度为O(nlogn)。

快速排序

快速排序算法中每次分割操作将数组分成了两个长度相等的部分,因此需要做log(n)次分割。在每个分割操作中,需要扫描数组一次找到最终位置的关键值,并将其与数组中的其他值分成两个部分。因此,每个分割操作的时间复杂度为O(n),平均时间复杂度为O(nlogn)。

二、合并排序简介

合并排序是采用分治策略实现对N个元素进行排序的算法,是分治法的一个典型应用和完美体现,它是一种平衡,简单的二分分治策略,计算过程分为三步:

  1. 分解:将待排序元素分成大小大致相同的两个子序列。
  2. 求解子问题:用合并排序法分别对两个子序列递归地进行排序。
  3. 合并:将排好序的有序子序列进行合并,得到符合要求的有序序列。

算法步骤

  1. 申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列;
  2. 设定两个指针,最初位置分别为两个已经排序序列的起始位置;
  3. 比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置;
  4. 重复步骤 3 直到某一指针达到序列尾;
  5. 将另一序列剩下的所有元素直接复制到合并序列尾。

代码

int min(int x, int y) {
    return x < y ? x : y;
}
void merge_sort(int arr[], int len) {
    int* a = arr;
    int* b = (int*) malloc(len * sizeof(int));
    int seg, start;
    for (seg = 1; seg < len; seg += seg) {
        for (start = 0; start < len; start += seg + seg) {
            int low = start, mid = min(start + seg, len), high = min(start + seg + seg, len);
            int k = low;
            int start1 = low, end1 = mid;
            int start2 = mid, end2 = high;
            while (start1 < end1 && start2 < end2)
                b[k++] = a[start1] < a[start2] ? a[start1++] : a[start2++];
            while (start1 < end1)
                b[k++] = a[start1++];
            while (start2 < end2)
                b[k++] = a[start2++];
        }
        int* temp = a;
        a = b;
        b = temp;
    }
    if (a != arr) {
        int i;
        for (i = 0; i < len; i++)
            b[i] = a[i];
        b = a;
    }
    free(b);
}

三、快速排序简介

快速排序的基本思想是随机找出一个数,可以随机取,也可以取固定位置,一般是取第一个或最后一个称为基准,然后就是比基准小的放在左边,比基准大的放到右边。如何放呢?就是和基准进行交换,这样交换完左边都是比基准小的,右边都是比较基准大的,这样就将一个数组分成了两个子数组,然后再按照同样的方法把子数组再分成更小的子数组,直到不能分解为止。快速排序使用分治法(Divide and conquer)策略来把一个序列(list)分为两个子序列(sub-lists)。

算法步骤

  1. 从数列中挑出一个元素,称为 “基准”(pivot)。
  2. 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作。
  3. 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。

代码

typedef struct _Range {
    int start, end;
} Range;
Range new_Range(int s, int e) {
    Range r;
    r.start = s;
    r.end = e;
    return r;
}
void swap(int *x, int *y) {
    int t = *x;
    *x = *y;
    *y = t;
}
void quick_sort(int arr[], const int len) {
    if (len <= 0)
        return; //避免len等于负值时错误
    //r[]模拟堆疊,p为数量,r[p++]为push,r[--p]为pop且取得元素
    Range r[len];
    int p = 0;
    r[p++] = new_Range(0, len - 1);
    while (p) {
        Range range = r[--p];
        if (range.start >= range.end)
            continue;
        int mid = arr[range.end];
        int left = range.start, right = range.end - 1;
        while (left < right) {
            while (arr[left] < mid && left < right)
                left++;
            while (arr[right] >= mid && left < right)
                right--;
            swap(&arr[left], &arr[right]);
        }
        if (arr[left] >= arr[range.end])
            swap(&arr[left], &arr[range.end]);
        else
            left++;
        r[p++] = new_Range(range.start, left - 1);
        r[p++] = new_Range(left + 1, range.end);
    }
}

延伸阅读

选择排序算法简介

选择排序(Select Sort)是直观的排序,通过确定一个 Key 最大或最小值,再从带排序的的数中找出最大或最小的交换到对应位置。再选择次之。双重循环时间复杂度为 O(n^2)。算法描述为:

  • 在一个长度为 N 的无序数组中,第一次遍历 n-1 个数找到最小的和第一个数交换。
  • 第二次从下一个数开始遍历 n-2 个数,找到最小的数和第二个数交换。
  • 重复以上操作直到第 n-1 次遍历最小的数和第 n-1 个数交换,排序完成。

文章标题:为什么合并排序和快速排序的平均时间复杂度都是O(nlogn),发布者:Z, ZLW,转载请注明出处:https://worktile.com/kb/p/49352

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
Z, ZLW的头像Z, ZLW
上一篇 2023年4月15日
下一篇 2023年4月15日

相关推荐

  • 开源文档协作工具:2024年10款评测

    国内外主流的10款开源文档协作平台对比:PingCode、Worktile、蚂蚁笔记(Leanote)、Wizard、Kooteam、ShowDoc、MrDoc、DooTask、语雀、WookTeam 。 在今天的数字化时代,寻找一个能够提高团队合作效率并确保信息共享流畅的解决方案,成了许多企业和个…

    2024年8月5日
    700
  • 企业如何智选知识管理工具?2024年8大精选

    本文将分享2024年8大优质企业知识管理工具:PingCode、Worktile、飞书文档、语雀、石墨文档、有道云笔记、Confluence、Document360。 很多公司都面临信息过载,难以将散落各处的知识有效整合和应用。这不仅影响决策效率,还可能导致重要信息的丢失。为了解决这一痛点,企业知识…

    2024年8月5日
    300
  • 产品经理秘籍:2024年9大主流需求管理工具

    本文将分享9款产品经理使用的主流需求管理工具:PingCode、Worktile、Tapd、禅道、Teambition、Testin、JIRA、Jama Connect、Wrike。 挑选一个能够高效精准地捕捉和管理需求的工具,对于推动项目成功至关重要,很多产品经理都面临着如何从众多选项中选择最适合…

    2024年8月5日
    400
  • 选择客户管理crm系统必看:全球15家顶级供应商综合比较

    对比的客户管理CRM系统包括:纷享销客、Zoho CRM、销售易、用友CRM、Salesforce、Microsoft Dynamics 365、销帮帮CRM、HubSpot、Oracle CRM、悟空CRM、神州云动CRM、红圈CRM、SAP CRM、Odoo、OroCRM。 一个合适的CRM系统…

    2024年8月5日
    800
  • 项目竣工资料管理软件有哪些

    项目竣工资料管理软件有许多,其中最为出色的要数PingCode和Worktile。这两款软件以其优秀的性能和功能,赢得了用户的青睐。简单来说,PingCode是一款专门为开发者设计的协作平台,强调代码质量、团队协作和敏捷开发。而Worktile则是一款面向企业的项目和任务管理工具,帮助团队更好地协作…

    2024年8月5日
    300

发表回复

登录后才能评论
注册PingCode 在线客服
站长微信
站长微信
电话联系

400-800-1024

工作日9:30-21:00在线

分享本页
返回顶部