C语言算法--快速排序法

news2024/11/20 4:24:17

C语言算法–快速排序法

1-什么是快速排序法

快速排序(Quicksort)是一种常用的排序算法,它基于分治的思想。它的核心思想是选择一个基准元素,将数组划分为两个子数组,使得左边的子数组中的所有元素都小于等于基准元素,右边的子数组中的所有元素都大于基准元素,然后对这两个子数组递归地应用快速排序算法,直到整个数组有序。

面是快速排序的基本步骤:

  1. 选择一个基准元素。通常情况下,选择数组的第一个元素作为基准元素。
  2. 将数组划分为两个子数组,左边子数组中的元素小于等于基准元素,右边子数组中的元素大于基准元素。这个过程称为分区(Partition)。
  3. 对左边子数组和右边子数组递归地应用快速排序算法,直到子数组的长度为1或0,此时子数组已经有序。
  4. 合并子数组,得到最终的有序数组。

快速排序的关键在于分区过程。常用的分区算法是Lomuto分区和Hoare分区。Lomuto分区的实现简单但效率稍低,而Hoare分区则更高效一些。

快速排序的平均时间复杂度为O(n log n),其中n是待排序数组的长度。它是一种原地排序算法,不需要额外的存储空间,因此空间复杂度为O(1)。快速排序是一种不稳定的排序算法,意味着相等元素的相对顺序在排序后可能会改变。

动画演示(来源于网络):

请添加图片描述

请添加图片描述

1.1-什么是Lomuto分区

Lomuto分区是一种用于快速排序算法的分区方法,由C.A.R. Hoare的经典分区方法之一。Lomuto分区算法的实现相对简单,但在某些情况下效率稍低。

下面是Lomuto分区的基本步骤:

  1. 选择一个基准元素。通常情况下,选择数组的最后一个元素作为基准元素。
  2. 遍历数组,将小于等于基准元素的元素放在数组的左边,并维护一个指针(称为分区指针),指向当前小于等于基准元素的位置。
  3. 遍历结束后,将基准元素放在分区指针的下一个位置,即将基准元素放在正确的位置上。
  4. 分区指针左边的元素都小于等于基准元素,右边的元素都大于基准元素。
  5. 对分区指针左边和右边的子数组递归地应用快速排序算法。

Lomuto分区的时间复杂度与快速排序相同,平均情况下为O(n log n),最坏情况下为O(n^2)(当数组已经有序或接近有序时)

1.2-什么是Hoare分区

Hoare分区是一种用于快速排序算法的分区方法,由快速排序的发明者之一、英国计算机科学家Tony Hoare在1960年提出。相对于Lomuto分区,Hoare分区在一些情况下能够更高效地进行数组的划分。

下面是Hoare分区的基本步骤:

  1. 选择一个基准元素。通常情况下,选择数组的第一个元素或随机选择一个元素作为基准元素。
  2. 定义两个指针,一个指向数组的左端,另一个指向数组的右端。
  3. 移动左指针,直到找到一个大于等于基准元素的元素。
  4. 移动右指针,直到找到一个小于等于基准元素的元素。
  5. 如果左指针仍然在右指针的左侧,则交换左指针和右指针所指向的元素。
  6. 重复步骤 3-5,直到左指针超过了右指针。
  7. 返回右指针作为分区点。

1.3-Hoare分区和Lomuto分区对比

Hoare分区和Lomuto分区是两种常用的分区方法,用于快速排序算法中的数组划分。它们在实现上有所不同,对比如下:

  1. 实现复杂度:
    • Lomuto分区的实现相对简单,容易理解和实现。
    • Hoare分区的实现稍微复杂一些,需要使用两个指针并处理边界情况。
  2. 划分方式:
    • Lomuto分区从数组的最后一个元素作为基准元素开始,通过将小于等于基准元素的元素放在数组的左边,大于基准元素的元素放在数组的右边,最终将基准元素放在正确的位置上。
    • Hoare分区从数组的第一个元素作为基准元素开始,通过两个指针分别从数组的两端向中间移动,交换逆序对,直到两个指针相遇,将基准元素放在分区点上。
  3. 效率对比:
    • 平均情况下,Hoare分区相对于Lomuto分区通常更高效,因为它在划分时更均衡地将元素分配到两个子数组中。因此,Hoare分区的快速排序通常比Lomuto分区的快速排序更快。
    • 然而,在某些情况下,Lomuto分区可能比Hoare分区更好。当数组中存在大量重复的元素时,Lomuto分区对于相等元素的处理更简单,可能更有效。

总的来说,Lomuto分区在实现上更简单,而Hoare分区在某些情况下更高效。在实际应用中,根据具体情况选择适合的分区方法可以提高快速排序算法的性能。另外,还有其他改进的分区方法,如三路快速排序(Dutch National Flag partitioning)等,可以进一步优化快速排序的性能。

2-快速排序法的优点

  1. 高效性:快速排序是一种高效的排序算法,平均情况下的时间复杂度为O(n log n)。在实际应用中,它通常比其他排序算法(如插入排序、冒泡排序等)更快速。
  2. 原地排序:快速排序是一种原地排序算法,不需要额外的存储空间来存储中间结果。只需要通过交换数组中的元素来进行排序,因此空间复杂度为O(1)。
  3. 分治思想:快速排序使用分治思想,将原问题分解为更小的子问题,通过递归地处理子问题来解决整个排序问题。这种思想使得算法的实现简单而直观。
  4. 可以并行化:由于快速排序的分治特性,可以将问题划分为多个子问题并独立地进行排序。这种特性使得快速排序可以很容易地并行化,利用多核处理器或分布式系统提高排序的效率。
  5. 适应性强:快速排序在实践中表现良好,适用于各种数据类型和规模的排序任务。它在大多数情况下都能提供高效的排序性能,并且可以通过优化的分区方法进一步提高算法的性能。

3-快速排序法的缺点

  1. 最坏情况下的性能:在最坏情况下,即待排序数组已经有序或接近有序的情况下,快速排序的性能会下降。此时,快速排序的时间复杂度将退化为O(n^2),其中n是待排序数组的长度。这是因为快速排序的分区点选择不当,导致每次划分只能减少一个元素的规模,而不是平均地减半。
  2. 不稳定性:快速排序是一种不稳定的排序算法,意味着相等元素的相对顺序在排序后可能会改变。在某些应用场景下,需要保持相等元素的相对顺序的稳定性,这时候快速排序就不适用。
  3. 需要额外空间:尽管快速排序是一种原地排序算法(不需要额外的存储空间),但其递归调用的栈空间会占用额外的内存。对于大规模的数据集,递归的深度可能较大,可能会导致栈溢出或者消耗过多的内存。
  4. 对于小规模数据效率较低:当待排序的数组规模较小时,快速排序的递归调用开销可能超过排序本身的开销。此时,其他排序算法如插入排序或者归并排序的表现可能更好。

4-快速排序法可以应用哪些场景

  1. 一般排序需求:快速排序是一种高效的排序算法,适用于对大规模数据集进行排序。它的平均时间复杂度为O(n log n),在实际应用中表现良好。
  2. 大规模数据集的排序:由于快速排序的时间复杂度较低,因此它特别适合用于对大规模数据集进行排序。快速排序的分治策略和原地排序特性使得它在处理大量数据时具有较小的内存占用。
  3. 数据库排序:快速排序在数据库系统中也被广泛应用于对查询结果进行排序。由于数据库查询返回的结果集可能非常大,快速排序可以快速且有效地对结果进行排序。
  4. 排名和统计:快速排序可以用于计算一组数据的排名或者统计特定范围内的数据。通过对数据进行快速排序,可以轻松确定某个元素的排名或者获取满足一定条件的数据。

5-举例

#include <stdio.h>

// 交换数组中两个元素的值
void swap(int* a, int* b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}

// 打印数组内容
void printArray(int arr[], int size) {
    for (int i = 0; i < size; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
}

// 分区函数,将数组划分为左边小于基准元素,右边大于基准元素
int partition(int arr[], int low, int high) {
    int pivot = arr[low];  // 选择第一个元素作为基准元素
    int i = low + 1;      // 左指针
    int j = high;         // 右指针

    while (1) {
        // 左指针向右移动,直到找到大于基准元素的位置
        while (arr[i] < pivot && i <= high) {
            i++;
        }

        // 右指针向左移动,直到找到小于基准元素的位置
        while (arr[j] > pivot && j >= low + 1) {
            j--;
        }

        if (i >= j) {
            break;
        }

        // 交换左指针和右指针所指向的元素
        swap(&arr[i], &arr[j]);
        printArray(arr, high + 1);  // 打印每一步的数组状态
    }

    // 将基准元素放到正确的位置上
    swap(&arr[low], &arr[j]);
    printArray(arr, high + 1);  // 打印每一步的数组状态

    return j;  // 返回基准元素的位置
}

// 快速排序函数
void quickSort(int arr[], int low, int high) {
    if (low < high) {
        int pivot_index = partition(arr, low, high);  // 获取分区点
        quickSort(arr, low, pivot_index - 1);         // 对左子数组递归排序
        quickSort(arr, pivot_index + 1, high);        // 对右子数组递归排序
    }
}

int main() {
    int arr[] = {10, 7, 8, 9, 1, 5};
    int size = sizeof(arr) / sizeof(arr[0]);

    printf("Original Array: ");
    printArray(arr, size);
    printf("\n");

    printf("Sorting Steps:\n");
    quickSort(arr, 0, size - 1);

    printf("\nSorted Array: ");
    printArray(arr, size);

    return 0;
}

请添加图片描述

#include <stdio.h>

// 枚举类型,用于表示排序步骤
enum Step
{
     COMPARE,
     SWAP,
     PARTITION
};

// 结构体,表示数组元素
struct Element
{
     int value;
     enum Step step;
};

// 交换元素的值
void swap(struct Element *a, struct Element *b)
{
     int temp = a->value;
     a->value = b->value;
     b->value = temp;
}

// 分区函数,返回分区点的索引
int partition(struct Element arr[], int low, int high)
{
     int pivot = arr[high].value; // 选择最后一个元素作为基准元素
     int i = (low - 1);           // 分区指针,初始为低端的前一个位置

     for (int j = low; j <= high - 1; j++)
     {
          if (arr[j].value <= pivot)
          {
               i++;
               swap(&arr[i], &arr[j]); // 交换元素的值
          }
     }
     swap(&arr[i + 1], &arr[high]);

     return (i + 1);
}

// 快速排序函数
void quicksort(struct Element arr[], int low, int high)
{
     if (low < high)
     {
          int pivot_index = partition(arr, low, high); // 获取分区点的索引

          // 打印分区步骤
          for (int i = 0; i < low; i++)
          {
               printf("%d ", arr[i].value);
          }
          printf("| ");
          for (int i = low; i <= high; i++)
          {
               if (i == pivot_index)
               {
                    printf("[%d] ", arr[i].value);
               }
               else
               {
                    printf("%d ", arr[i].value);
               }
          }
          printf("| ");
          for (int i = high + 1; i < high; i++)
          {
               printf("%d ", arr[i].value);
          }
          printf("\n");

          quicksort(arr, low, pivot_index - 1);  // 递归排序左半部分
          quicksort(arr, pivot_index + 1, high); // 递归排序右半部分
     }
}

int main()
{
     struct Element arr[] = {{7, COMPARE}, {2, COMPARE}, {5, COMPARE}, {1, COMPARE}, {8, COMPARE}, {6, COMPARE}, {3, COMPARE}, {4, COMPARE}};
     int n = sizeof(arr) / sizeof(arr[0]);

     printf("原始数组: ");
     for (int i = 0; i < n; i++)
     {
          printf("%d ", arr[i].value);
     }
     printf("\n\n排序步骤:\n");

     quicksort(arr, 0, n - 1);

     printf("\n排序后的数组: ");
     for (int i = 0; i < n; i++)
     {
          printf("%d ", arr[i].value);
     }
     printf("\n");

     return 0;
}

请添加图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/541239.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Flutter开发】Navigator2.0介绍及使用

目录 Navigator1.0Navigator2.0APPRouteInformationParserRouterDelegate 问题The Navigator.pages must not be empty to use the Navigator.pages API浏览器的回退按钮 总结 Navigator1.0 我们学习flutter一开始接触的路由管理就是Navigator1.0&#xff0c;它非常方便&#…

JAVA-Activiti 7与达梦、人大金仓兼容-nacos、服务pom文件配置(2)

目录 第一步,修改nacos服务配置 >需注意< 第二步,pom.xml依赖包配置 Activiti的源码包解决之后,接下来就好做很多了 第一步,修改nacos服务配置 spring:datasource:url: jdbc:kingbase8://127.0.0.1:54321/progress?currentSchemaprogress,productNamePostgreSQL,SYS…

保密+完整+可用+安全,规避代码安全「马奇诺防线」,构建软件供应链整体安全

近日&#xff0c;在「江狐会」广州站上&#xff0c;极狐(GitLab) 高级解决方案架构师武让分享了如何通过三大阶段 四大要点&#xff0c;规避代码安全「马奇诺防线」&#xff0c;真正确保软件供应链安全。以下内容整理自本次演讲。Enjoy&#xff5e; 先跟大家分享一个故事 一战…

计算机体系结构|MIT6.175和MIT6.375学习笔记

在2023年初&#xff0c;达坦科技发起成立硬件设计学习社区&#xff0c;邀请所有有志于从事数字芯片设计的同学加入我们的学习互助自学小组&#xff0c;以理解数字芯片设计的精髓&#xff0c;强化理论知识的同时提升实操技能&#xff0c;继而整体提升设计能力。现在&#xff0c;…

Vmware虚拟机安装MacOS13-Ventura详细教程

小编亲测 前提准备 功能强大的 Windows 电脑&#xff08;不能太差&#xff0c;不然会卡&#xff09;至少8GB内存默认是80GB的存储空间VMWare Workstation&#xff08;版本应该没什么需求&#xff0c;我装的是VMware Workstation 17 Pro&#xff09;Unlocker解锁软件MacOS Ventu…

最快的 Houdini 和 V-Ray 云渲染服务

Houdini是SideFX开发的一款3D动画软件应用。Houdini 最常用于 FX 部门&#xff0c;用于在电影和游戏中创建视觉效果。它被主要的 VFX 公司使用&#xff0c;例如 Walt Disney Animation Studios、Pixar、DreamWorks Animation、Double Negative、ILM、MPC、Framestore 等。Houdi…

推荐几款音频转文字软件给你

不知道小伙伴们有没有遇到过这种情况&#xff0c;在上学时期&#xff0c;我们经常需要记录老师上课的板书内容&#xff0c;但是边听边记可能速度会跟不上&#xff0c;还会遗漏掉一些内容&#xff0c;而且效率也不高。其实这时候&#xff0c;我们可以将老师讲话的内容先录制下来…

鸿蒙Hi3861学习十三-Huawei LiteOS-M(STA模式)

一、简介 AP&#xff08;Access Point&#xff09;无线接入点 AP是无线接入点&#xff0c;是一个无线网络的创建者&#xff0c;是网络的中心节点。一般家庭或办公室使用的无线路由器就是一个AP。 STA&#xff08;Station&#xff09;站点 STA也可以理解为终端的意思&#xff…

高性能零售IT系统的建设10-一个系统日志记录搞崩了整个公司的O2O交易系统

背景 绝大多数业务系统其实都是一座屎山&#xff0c;本人接手的这座屎山目前已经成了一座金山。这其中的幸酸只有那些从0参与过并活到现在的一些“老人”们心中自知其中的滋味。 在3年半前&#xff0c;本以为买来的一套将近600万行代码、达800张表、几乎用到了所有的互联网中间…

分布式系统:高并发

目录 1.什么是高并发 2.术语 3.如何应对处理高并发 3.1.提升系统的并发能力 3.3.1.垂直扩展 3.3.2.水平扩展 3.2.流量控制 4.削峰 4.1.怎样来实现流量削峰方案 4.2.限流 5.总结 1.什么是高并发 高并发是指系统在同一时间内处理大量请求的能力。在软件开发中&a…

CRM管理系统在线用

一、CRM管理系统是什么 CRM是客户关系管理的缩写&#xff0c;是指企业通过建立客户档案、跟进客户需求、提供优质服务来维系客户关系的一种管理模式。是企业以客户关系为重点&#xff0c;通过开展系统化的客户研究&#xff0c;优化企业组织体系和业务流程&#xff0c;提高客户…

Jina AI全新Inference服务,LangChain开发体验从未如此丝滑

由于 Token 的限制&#xff0c;在开发 LangChain 问答机器人应用时&#xff0c;我们经常需要将文档切割&#xff0c;接着使用 Embedding 引擎 分别将分割后的 Document 变成 Embeddings&#xff0c;即向量表示。 同时输入的问题&#xff0c;也需要用到 Embedding 引擎 变成向量…

linux调试知识:手把手教你SSH怎么链接

在机器装机后&#xff0c;如果没有显示&#xff0c;有没有串口&#xff0c;通常很难区操作调试&#xff0c;本文总结一篇通过搭建SSH链接去为调试做服务&#xff1a; 首先第一步&#xff1a;安装必要的软件&#xff0c;CRT或者XSHELL。 下面将举实际案例&#xff0c;手把手搭…

Axure rp9 引入Echarts图表 |手动引入图表 Apache Echarts

Axure rp9 引入Echarts图表 |手动引入图表 Apache Echarts 1.拖入一个矩形lable&#xff0c;调整合适大小,并命名为test 2.给test新建交互载入时&#xff0c;打开链接&#xff0c;并将下方code贴入 如果想在无网的情况下运行&#xff0c;需要在axure软件的安装目录DefaultSet…

深度学习笔记之递归网络(三)递归神经网络

深度学习笔记之递归网络——递归神经网络 引言回顾&#xff1a;潜变量自回归模型递归神经网络思想困惑度 引言 上一节介绍了基于统计算法的语言模型。本节将介绍基于神经网络的序列模型——递归神经网络。 回顾&#xff1a;潜变量自回归模型 关于潜变量自回归模型&#xff0…

记一次 Visual Studio 2022 卡死分析

一&#xff1a;背景 1. 讲故事 最近不知道咋了&#xff0c;各种程序有问题都寻上我了&#xff0c;你说 .NET 程序有问题找我能理解&#xff0c;Windows 崩溃找我&#xff0c;我也可以试试看&#xff0c;毕竟对 Windows 内核也知道一丢丢&#xff0c;那 Visual Studio 有问题找…

揭秘市场热销的4款问卷调查工具

当谈到进行在线问卷调查时&#xff0c;选择正确的工具可以使调查过程完全不同。市场上有这么多可供选择的产品&#xff0c;要找到一款符合我们需求的工具不是一件容易的事儿。在本文中&#xff0c;小编将和大家一起讨论4款市面上好用的问卷调查工具盘点&#xff0c;并比较它们的…

基于C语言设计一个叫号系统

访问【WRITE-BUG数字空间】_[内附完整源码和文档] 这道题的重点在于怎么处理患者的治疗过程。大二上学期的理论课上&#xff0c;我们在第一节的研讨课上对于这道题的实现进行了探讨。本题的患者排队与数据结构中的队列结构完全符合&#xff0c;当患者挂号后&#xff0c;检查该…

语音工牌:从线下沟通过程入手,实现运营商上门安装流程监管

近年来&#xff0c;随着网络的飞速发展&#xff0c;宽带越来越成为人们生活中必不可少的一部分&#xff0c;相应的&#xff0c;宽带上门安装、迁机及检修服务也成为运营商业务场景里重要的一环。 随着业务需求的增加和上门服务工程师队伍的壮大&#xff0c;以及消费者对服务质…

印度也开始自研 CPU ,5nm工艺、功耗是i9好几倍

前两天的新闻估计大家都看了&#xff0c;国内又一个科技巨头公司终止「造芯」。 OPPO 子公司哲库从成立到解散用了4年时间&#xff0c;这期间做出的马里亚纳X影像芯片也小有名气。 显然其目标不只是影像这一点&#xff0c;今年年初就有消息称 OPPO 自研 Soc 已经快到流片&…