C语言快速排序算法以及代码

news2024/9/30 17:32:13

接下来我们将用图像模拟来一步步演示快速排序的过程,这样我们将会通过视觉和大脑一起来梳理快速排序的思路。

后文示例的C语言代码将实现图像模拟的过程。

一、图像模拟 快速排序 过程

我们选取十个数字0~9当做我们的排序数字,并将其打乱。然后我们将按照升序进行排列。如下图:

1、选取基准数

首先要在这个序列中随便找一个基准数,在此我们选取第一个数字5作为基准数字。(选取基准数有多种方式,此方式不是唯一)如下图:

接下来我们要将这个基准数字5挪动到其应该呆的位置。

那么它应该呆在哪个位置呢,其实这个位置要满足两个条件:位置的左边都是小于5的数字,位置的右边都是大于5的数字(升序)。我们将这个位置叫作数字在排序中的正确位置,简称“ 正确位置”。

现在我们知道了要将5挪动到其正确位置,那么就产生了一个问题,怎么寻找正确位置呢?

这里就要引入快速排序的核心思想了,即“ 两端查找比较交换”。这个思想我们就不从文字予以解释了,下文直接上图。需要说明的是,在通过“两端查找比较交换”查找“正确位置”的时候会顺便将小于基准数5和大于基准数5的数字归位。

2、用指针指向无序序列的两端

我们在两端引入两个指针,一个指向左端,一个指向右端,分别用left和right进行标识。(两端指针)

3、查找比较交换(此过程是循环过程,圈号为重复的步骤)

①、右指针查找比较 我们先让右指针right进行查找(因为是升序,right先查找,后文还将提到)比基准数字5小的数字。如果指针下的数字不比基准数小,就一直向前查找。(查找比较) 此时right指针走到数字4时,和基准数字5比较后,显然小于。此时right指针应该停下。

②左指针查找比较 接下来让left指针开始查找大于基准数字5的数字,走到数字8时,和基准数字5比较显然大于,left指针停下。(查找比较)

③交换 现在left指针和right指针都指向了一个数字,接下来拿出这两个数字进行位置交换。(交换)

交换后

①右指针查找比较 接下来重新让right指针向前寻找比基准数5小的数。走到3时,与基准数5比较后小于成立,停下。(查找)

②左指针查找比较 再让left指针继续向后寻找比5大的数字。走到6时与5比较后大于成立,停下。(查找)

③交换 此时再次拿出两指针下数值进行位置交换。(交换)

①右指针查找比较 重新让right指针向前查找小于基准数5的数字,走到2时与基准数比较后小于成立,停下。(查找)

②左指针查找比较(查找) 让left指针继续向后查找大于基准数5的数字,此时我们可以看到两指针重合,这个时候就要停前进了,因为两指针重合表示基准数5的“正确位置”已经找到了。

③交换 找到基准数5的正确位置后,就要将基准数字挪动到正确位置,这时就要交换两位置的数字。(交换)

交换后。

到此“两端查找比较交换”的第一轮正式结束,你发现了吗?现在基准数5的左边都是小于5的数字,而右边都是大于5的数字。

需要说明:上文提到right指针先走,在这里将进行解释。

我们观察上面交换的两幅图像可以看到,与基准数字5交换位置的数字2是小于基准数字5的,交换位置后也正好让2挪动到了比5小的那边了。大家想想为什么两指针正好停在了比5小的2上面了呢。这当然是我们故意而为之的。
▪因为right指针是寻找比基准数5小的数字,当其停止后一定是停在了小于5的数字上面。然后再让left指针移动,left指针最后停止的位置必将是和right相遇的时候,这就保证了两指针停止的位置必然是小于基准数字的。
▪最后交换位置后,小的数字也就到了基准数字的左边。
▪ 当然,如果你选取基准数的方法不一样,升序降序不一样。先让right走还是left走也就不一样了。

说完指针先后的问题后,我们继续总结这一轮结束后的一些发现

思想总结:
我们可以发现“两端查找比较交换”的思想就是为了寻找“正确位置”, 可以说通过“两端查找比较交换”的思想实现了寻找基准数的“正确位置”。

你也会发现以基准数5为界限,分为了两个(2、0、1、4、3)和(6、8、9、7)两个无序序列。

剩下的工作就是对两个无序序列重复上述过程:

1、选取基准数

2、用指针指向无序序列两端

3、两端查找比较交换

话不多说,我们继续用图像模拟演示。(红线表示本轮的“两端查找比较交换”结束)

这一轮的“两端查找比较交换”我们要单独拿出5左边的序列进行排序,我们先来看一下经过上一轮的“两端查找比较交换”排序后的前后比较:

现在我们单独拿出5左边的序列(2、0、1、4、3)进行排序

选取基准数

我们还是选取第序列中的第一个数字2作为基准数。

用指针指向无序序列两端

注意,这一轮指针指向的是数字5左端(2、0、1、4、3)序列的两端。

查找比较交换

在此就不在进行文字详细描述,全程图像模拟演示。 需要记住这一轮right寻找比基准数2小的,left寻找比基准数2大的(因为是升序),两者都找到后就进行交换,两者相遇后就表示“正确位置”以找到。“正确位置”找到后就应该将基准数归位。

完成这轮排序后,你又会发现,以基准数2为界限分为了(1、0)和(4、3)两个无序序列。

到此,我们还有(1、0)序列,(4、3)序列,(6、8、9、7)序列,以及6、8、9、7序列排序完成后的(8、9、7)序列未进行排序。

我就不在一一用图像模拟,因为每一轮思路都一样,即每一轮处理就是将这一轮的基准数放在“正确位置”上。直到所有的数都放在“正确位置”上,排序就结束了。所以大家要重在理解上述思路。 理解了上述思路后,我们进行下面的代码设计。

二、程序代码设计(C语言)

理解了快速排序的思路后,就要去想如何用代码实现了。

执行每一轮的衔接问题:

这里要说一个重要的问题:问题如下▼
▲不知道大家在看上面图像模拟的时候,脑海里产生一个疑问没有,这个疑问就是,每一轮的“查找比较排序”执行完成后,程序应该怎么去执行下一轮的呢?每一轮应该怎样用程序很好的衔接呢? 关于这个问题,当然有很多种思路,下面的代码则用了递归的方法去实现每一轮的衔接。

下文,有代码叙述和C代码,大家可以结合在一起看,其中有递归的过程,需要懂得一些递归的相关知识。

代码叙述:

1、 首先,调用QkSort函数的时候需要给其传递三个值,
▷ “数组”,“左指针位置”,“右指针位置”。
2、用变量tmp作为存放本轮循环的基准数。
3、用变量i作为左指针,获取传递过来的值(变量left的值)
4、用变量j作为右指针,获取传递过来的值(变量right的值)
5、第一层while外循环的内部结构是:
▷有一个一直从后向前寻找比基准数小的右指针
▷有一个一直从前向后寻找比基准数大的左指针
▷有一个用于交换数字的if语句
6、while外循环执行完后就找到了“正确位置”
▷“基准数”归位的两个交换位置表达式:
▷arr[left] = arr[i];
▷arr[i] = tmp;
7、第一个递归
▷QkSort(arr, left, i - 1);
▷这个递归一直解决每次循环完后产生的两个序列的左序列,直到所有左序列都排序完成。
▷因此,你可以看到,第二个实际参数一直是将左指针传递给形参,
▷第三个实际参数一直是将上轮基准数的下标-1传递给形参。
▷这样就无形的将这个完整的数组虚拟的切割成了两个。
▷将参数传递过去后,函数的操作范围正好是无序的左序列。
▷如下图:

8、第二个递归 ▷QkSort(arr, i + 1, right);
▷这个递归需要等到每轮的左序列排完序后,即第一个递归都执行完后才执行。
▷其解决每次循环完后产生的两个序列的右序列,直到所有序列排序完成。
▷程序在执行完所有的第二个递归后,也表示着全部序列完成排序,序列整体已经有序,排序完成。
●说明:这个程序的难点就在于对递归的理解。

C语言代码:

#include<stdio.h>
//快速排序函数,形参列表为数组,左指针位置,右指针位置,int *arr等价于int arr[]
void QkSort(int *arr, int left, int right){
    if (left > right)  //左指针位置必须大于右指针位置
    {
        return;
    }
    //变量tmp为基准数,在此规定基准数为序列的第一个数,即左指针指向的数
    int tmp = arr[left];
    int i = left; //左指针
    int j = right;   //右指针
    //外循环,直到左指针和右指针相等时退出,表示根据当前基准数以完成当前序列排序
    while (i != j)
    {   //内循环1,寻找到比基准数小的数时退出循环,此循环控制右指针    
        while (arr[j] >= tmp && j > i)
        {
            j--;
        }
        //内循环2,寻找到比基准数大的数时退出循环,此循环控制左指针
        while (arr[i] <= tmp && j > i)
        {
            i++;
        }
        //经过以上两个内循环后,此时的左指针和右指针分别指向了
        //比基准数小和比基准数大的数
        //接下来要将这两个指针的数据进行交换
        if (j > i)//交换前判断右指针是否大于左指针
        {
            int t = arr[i];
            arr[i] = arr[j];
            arr[j] = t;
        }
    }//外循环尾

    //执行完循环后,就找到了基准数的排序位置,将基准数tmp与i位置进行交换
    arr[left] = arr[i];
    arr[i] = tmp;
    //*********************************************
    //下面的程序为递归,可能存在多层递归调用
    //*********************************************
    //此时的数组分为了两部分,基准数左边都是小于基准数的,右边都是大于基准数的,
    //现在进行递归,对基准数左边的数进行排序,此时递归可能会有多层
    QkSort(arr, left, i - 1);
    //进行到这步时,基准数左边已经全部有序,而右边还未进行排序,
    //现在进行递归,对基准数右边的数据进行排序,此时递归可能有多层
    QkSort(arr, i + 1, right);
}

int main()
{
    int arr[] = { 0, 4, 3, 5, 65, 2, 64, 68, 34, 94, 53, 74, 13 };
    int len = sizeof(arr)/sizeof(int);
    printf("待排序数值:");
    for (int i = 0; i <=len-1; i++)
    {
        printf("%d ",arr[i]);
    }
    printf("\n");
    printf("排序后的数值:");
    QkSort(arr,0,len-1);//调用快速排序函数
    for (int i = 0; i <=len-1; i++)
    {
        printf("%d ", arr[i]);
    }
}

代码运行结果:

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

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

相关文章

Axure的交互以及情形的介绍

一. 交互 1.1 交互概述 通俗来讲就是&#xff0c;谁用了什么方法做了什么事情&#xff0c;主体"谁"对应的就是axure中的元件&#xff0c;"什么方法"对应的就是交互事件&#xff0c;比如单击事件、双击事件&#xff0c;"什么事情"对应的就是交互…

银河麒麟v10 安装mysql 8.35

银河麒麟v10 安装mysql 8.35 1、下载Mysql安装包2、安装Mysql 8.352.1、安装依赖包2.2、安装Mysql2.3、安装后配置 1、下载Mysql安装包 访问官网下载链接 链接: https://dev.mysql.com/downloads/mysql/ 选择如下 点击下载按钮 下载安装包 2、安装Mysql 8.35 官方安装文档…

挑战52天学小猪佩奇笔记--day25

52天学完小猪佩奇--day25 ​【本文说明】 本文内容来源于对B站UP 脑洞部长 的系列视频 挑战52天背完小猪佩奇----day25 的视频内容总结&#xff0c;方便复习。强烈建议大家去关注一波UP&#xff0c;配合UP视频学习。 day25的主题&#xff1a;生病了 猜台词&#xff1a; Daddy: …

我勒个豆,怎么没人告诉我这个偷懒神器啊

OMG&#xff0c;还有行政人不知道它的吗&#xff1f;&#xff1f;再不用真的亏大了啊&#xff01;&#xff01; 这东西写啥都可以&#xff0c;只有输入需求马上就写好了啊&#xff0c;什么工作总结&#xff0c;活动策划方案&#xff0c;会议纪要啥啥都可以&#xff0c;全能写啊…

持续集成交付CICD:K8S 通过模板文件自动化完成前端项目应用发布

目录 一、实验 1.环境 2.GitLab 更新deployment文件 3.GitLab更新共享库前端项目CI与CD流水线 4.K8S查看前端项目版本 5.Jenkins 构建前端项目 6.Jenkins 再次构建前端项目 二、问题 1. Jenkins 构建CI 流水线报错 2. Jenkins 构建CI 流水线弹出脚本报错 3. Jenkins…

长短期记忆(LSTM)神经网络-多输入分类

目录 一、程序及算法内容介绍&#xff1a; 基本内容&#xff1a; 亮点与优势&#xff1a; 二、实际运行效果&#xff1a; 三、部分程序&#xff1a; 四、完整程序下载&#xff1a; 一、程序及算法内容介绍&#xff1a; 基本内容&#xff1a; 本代码基于Matlab平台编译&am…

Linux-----17、软件包管理

# 软件包管理 # 1、软件包分类 # ㈠ 软件包类型 二进制包源码包 # ① 二进制包 什么是二进制包&#xff1f;有什么特点&#xff1f; 二进制包&#xff0c;指的是已经 1 好了的软件包&#xff0c;只需要直接安装就可以使用。二进制包&#xff0c;不需要编译&#xff0c;直接…

【人工智能革命】:AIGC时代的到来 | 探索AI生成内容的未来

&#x1f3a5; 屿小夏 &#xff1a; 个人主页 &#x1f525;个人专栏 &#xff1a; IT杂谈 &#x1f304; 莫道桑榆晚&#xff0c;为霞尚满天&#xff01; 文章目录 &#x1f4d1;前言一. AIGC 技术的概述和发展趋势1.1 AIGC 技术的概述1.2 AIGC 技术的发展趋势 二. AIGC 与元宇…

Java-基础部分(二)

一、抽象类 当编写一个类时&#xff0c;我们往往会为该类定义一些方法&#xff0c;这些方法是用来描述该类的行为方式&#xff0c;那么这些方法都有具体的方法体。 分析事物时&#xff0c;发现了共性内容&#xff0c;就出现向上抽取。会有这样一种特殊情况&#xff0c;就是功…

使用 React 实现自定义数据展示日历组件

目录 背景实现日历组件父组件数据 效果最后 背景 项目中需要实现一个日历组件&#xff0c;并且需要展示月&#xff0c;日所对应的数据&#xff08;因为项目需求问题&#xff0c;就不统计年数据总量&#xff09;。网上找了一堆&#xff0c;基本都不大符合项目需求&#xff0c;且…

计算机提示由于找不到vcruntime140_1.dll怎么办,那种修复方法推荐

首先&#xff0c;让我们来了解一下vcruntime140_1.dll是什么。其实&#xff0c;vcruntime140_1.dll是Visual C Redistributable Packages的一部分&#xff0c;它是由Microsoft Visual Studio编写的程序运行时库。它包含了许多用于运行Windows应用程序的函数和资源。因此&#x…

VueStu02-创建一个Vue实例

一、核心步骤 1.准备容器 准备一个盒子div。 2.引包 从官网引包&#xff0c;有开发版本和生产版本之分。 3.创建Vue实例 创建一个Vue实例&#xff0c;new Vue()。 4.指定配置项 指定配置项&#xff0c;用于渲染数据 。 el&#xff1a;指定挂载点。知道自己将来要管理的是…

Python实验作业,爬虫,中国院士信息

实验内容&#xff1a; 爬取中国工程院网页上&#xff0c;把每位院士的简介保存为本地文本文件&#xff0c;把每位院士的照片保存为本地图片&#xff0c;文本文件和图片文件都以院士的姓名为主文件名。 实验代码&#xff1a; import os.path import time from urllib.request …

干货教学!!!RHEL8中ansible中常用模块的使用

内容很长各位大老爷耐心观看 本章主要介绍ansible中最常见模块的使用 文件管理模块软件包管理模块服务管理模块磁盘管理模块用户管理模块防火墙管理模块 ansible的基本用法如下 ansible 机器名 -m 模块x -a “模块的参数” 对被管理机器执行不同的操作&#xff0c;只需要调…

git修改远程commit信息

git 修改远程commit信息 如果你已经把本地commit的信息push到远程了&#xff0c;此时需要修改远程中的commit信息 第一步&#xff1a;git log 查看提交的信息,看下提交的commit日志 如下入所示 第二步&#xff1a;然后确定你需要修改的那一次commit&#xff0c;比如&#xf…

LeetCode Hot100 51.N皇后

题目&#xff1a; 按照国际象棋的规则&#xff0c;皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。 n 皇后问题 研究的是如何将 n 个皇后放置在 nn 的棋盘上&#xff0c;并且使皇后彼此之间不能相互攻击。 给你一个整数 n &#xff0c;返回所有不同的 n 皇后问题 的…

Everything 搜索

正则表达式Regex 首先需要开启 Everything 工具在&#xff08;字符串&#xff09;查找时&#xff0c;对正则表达式功能的支持&#xff1a; 需要在【菜单栏】⇒ 【Search】⇒ 勾选【Enable Regex】 查看Everything 支持的语法:

统一大语言模型和知识图谱:如何解决医学大模型-问诊不充分、检查不准确、诊断不完整、治疗方案不全面?

统一大语言模型和知识图谱&#xff1a;如何解决医学大模型问诊不充分、检查不准确、诊断不完整、治疗方案不全面&#xff1f; 医学大模型问题如何使用知识图谱加强和补足专业能力&#xff1f;大模型结构知识图谱增强大模型的方法 医学大模型问题 问诊。偏离主诉和没抓住核心。…

强化学习--DQN

DQN 强化学习 DQN深度网络经验回放目标网络 深度网络 一个神经网络能够将输入向量映射到输出向量&#xff0c;这个映射过程可以用下式表示。 某种意义上来说&#xff0c;神经网络就是一个函数&#xff0c;只不过不同于一般的数值函数&#xff0c;它的输入输出都是向量&#x…

在vue中通过js动态绘制table,并且合并连续相同内容的行,支持点击编辑单元格内容

首先是vue代码 <template><div id"body-container"style"position: absolute"><div class"box-container"><div class"lsb-table-box" ><div class"table-container" id"lsb-table"&…