【数据结构】快速排序C语言

目录

前言

一、快排思想过程

二、算法思路

三、代码实现

C语言实现:

 

C++实现:

总结


前言

排序是一个相对复杂的过程,进一步思考排序这个问题,我们可以借助分治的思想来解决这个问题,
什么叫分治呢?就是把大问题化成小问题,进而缩小问题的规模,并且大问题和小问题只有规模不同,显然,大问题的问题规模大,小问题的问题规模小。例如,我们上面的起泡排序,经过一趟排序后,得到的序列是{23,22,38,23,45,31,15,41,67},其中67已经排好,剩下的元素也是排序,只是需要排序的元素
少了一个(问题规模变小)。当然,起泡排序的这种分割太低效,每次只能切除一个元素,太费时费力了。
那么有没有更有效的方法呢?答案是有,快速排序就是其中一种经典算法。

一、快排思想过程

(1)排序思想。
快速排序是我们最常用的算法,它的基本思想是通过一趟排序,将待排序记录分割成独立的两部分,其中一部分记录的关键字均比另一部分记录的关键字小,再分别对这两部分记录进行下一趟排序, 从而达到整个序列有序。也就是说,每次都选一个基准元素,通过这个基准元素将待排序的元素分成两个部分。
(2)排序过程。

每次都选一个基准元素,通过这个基准元素将待排序的元素分成两个部分。那么怎么选这个基准元素呢?在考研中,一般选择每段的第一个元素作为基准元素。具体来说,设待排序的记录序列是L[s .. t],在记录序列中取L[s]作为参照(又称为基准或枢轴),以L[s]为基准重新排列其余的所有记录,将所有比基准小的关键字放L[s]之前,将所有比基准大的关键字放L[s]之后。以L[s]最后所在位置i作为分界,将序列L[s .. t]分割成两个子序列L[s .. i-1]和L[i+1 .. t],再分别对这两部分元素进行下一趟排序(L[s .. i-1]以L[s]为基准元素,L[i+1 .. t]以L[i+1]为基准元素)。如此重复,直到整个序列有序。
(3)一趟快速排序方法。
在快速排序中,选取基准元素L[s],将所有比基准小的关键字放L[s]之前,将所有比基准大的关键字放L[s]之后。实现上述过程的操作称为一趟排序。从序列的两端交替扫描各个记录,将关键字小于基准,关键字的记录依次放置到序列的前边; 将关键字大于基准关键字的记录从序列的最后端起,依次放置到序列的后边,直到扫描完所有的记录。这个文字描述比较难理解,我们通过一个例子来说明,结合一趟实现快速排序的代码来讲解实现过程。设有7个待排序的记录,关键字分别为29、38、22、45、23、67、31,一趟快速排序的过程如下,待排元素的数组是L。

             

二、算法思路

当执行快速排序时,可以根据以下算法思路进行步骤实现:

1. 定义一个快速排序函数,接收数组、起始索引和结束索引作为参数。
2. 如果起始索引小于结束索引,则进行以下步骤:
   a. 选择数组的第一个元素作为基准元素 pivot。
   b. 初始化两个指针 i 和 j 分别指向起始索引和结束索引。
   c. 开始循环,直到 i 不小于 j:
      - 从右向左找到第一个小于 pivot 的元素,将其索引赋给 j。
      - 从左向右找到第一个大于 pivot 的元素,将其索引赋给 i。
      - 若 i 小于 j,则交换数组中索引为 i 和 j 的元素。
   d. 当 i 大于等于 j 时,将基准元素 pivot 与索引为 j 的元素交换。
   e. 以 j 为界将数组分为两部分,递归调用快速排序函数对左右两部分进行排序。
3. 在主函数中调用快速排序函数,将数组和起始、结束索引传入。
4. 最终,数组会被分隔成许多小的部分,每个部分都是有序的,最终整个数组会被排序。

这就是快速排序算法的基本思路,通过不断地分割和交换元素,最终实现对整个数组的排序。
 

三、代码实现

C语言实现:

#include <stdio.h>

void swap(int *a, int *b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}

int partition(int arr[], int low, int high) {
    int pivot = arr[low];
    int i = low, j = high;

    while (i < j) {
        while (i < j && arr[j] >= pivot) {
            j--;
        }
        if (i < j) {
            swap(&arr[i], &arr[j]);
        }

        while (i < j && arr[i] <= pivot) {
            i++;
        }
        if (i < j) {
            swap(&arr[i], &arr[j]);
        }
    }

    arr[i] = pivot;
    return i;
}

void quickSort(int arr[], int low, int high) {
    if (low < high) {
        int pivotIndex = partition(arr, low, high);
        quickSort(arr, low, pivotIndex - 1);
        quickSort(arr, pivotIndex + 1, high);
    }
}

int main() {
    int arr[] = {29, 38, 22, 45, 23, 67, 31};
    int n = sizeof(arr) / sizeof(arr[0]);

    quickSort(arr, 0, n - 1);

    printf("Sorted array: ");
    for (int i = 0; i < n; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");

    return 0;
}

C语言分析:

这段代码实现了快速排序算法的逻辑,首先使用swap函数交换两个元素的值,然后定义partition函数来进行分区操作,最后实现了quickSort函数来递归进行快速排序。在main函数中初始化一个示例数组并调用quickSort函数进行排序,然后输出排序后的数组。

C++实现:

#include <iostream>
#include <vector>

void quickSort(std::vector<int>& arr, int low, int high) {
    if (low < high) {
        int pivot = arr[low]; // 选择第一个元素作为基准元素
        int i = low, j = high;

        while (i < j) {
            while (i < j && arr[j] >= pivot) {
                j--;
            }
            if (i < j) {
                std::swap(arr[i], arr[j]);
            }

            while (i < j && arr[i] <= pivot) {
                i++;
            }
            if (i < j) {
                std::swap(arr[i], arr[j]);
            }
        }

        arr[i] = pivot; // 将基准元素放置到正确的位置

        quickSort(arr, low, i - 1); // 对基准元素左边的子数组进行快速排序
        quickSort(arr, i + 1, high); // 对基准元素右边的子数组进行快速排序
    }
}

int main() {
    std::vector<int> arr = {29, 38, 22, 45, 23, 67, 31};

    quickSort(arr, 0, arr.size() - 1);

    std::cout << "Sorted array: ";
    for (int num : arr) {
        std::cout << num << " ";
    }
    std::cout << std::endl;

    return 0;
}

这段C++代码实现了快速排序算法的完整逻辑。让我们逐步分析这段代码:

1. 在代码开头`#include <iostream>`和`#include <vector>`语句用来引入所需的头文件。
2. `void quickSort(std::vector<int>& arr, int low, int high)`定义了一个名为`quickSort`的函数,接受一个整数向量`arr`以及表示排序范围的`low`和`high`参数。
3. 在`quickSort`函数中,首先检查`low`和`high`是否指向有效的排序范围,如果`low < high`,进入排序逻辑。
4. 在排序逻辑中,选择数组第一个元素作为基准元素`pivot`,然后定义两个指针`i`和`j`分别指向排序范围的两端。
5. 通过双指针的移动,将数组中的元素不断与基准元素比较和交换,直到双指针相遇。
6. 将基准元素放置到正确的位置,使得基准元素左侧的元素都小于基准元素,右侧的元素都大于基准元素。
7. 对基准元素左右两侧的子数组分别递归调用`quickSort`函数进行排序。
8. 在`main`函数中,初始化一个整数向量`arr`作为示例数组,调用`quickSort`函数对该数组进行排序。
9. 最后使用`for`循环遍历排序后的数组,并输出排序结果。
10. 程序执行结束后返回`0`表示运行成功。
这段C++代码实现了快速排序算法的逻辑,通过递归的方式对数组进行分治排序,最终实现了对数组的快速排序功能。


总结

  1. 优点

    • 平均时间复杂度为O(nlogn),效率较高。
    • 原地排序,不需要额外的空间。
    • 相对容易实现。
  2. 缺点

    • 对于小规模数据或近乎有序的数据,性能可能不如插入排序等简单排序算法。
    • 快速排序的最坏时间复杂度为O(n^2),发生在数组已经有序的情况下。
  3. 稳定性:快速排序是不稳定的排序算法,因为在交换过程中相同元素的相对位置可能发生变化。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

牛客NC166 连续子数组的最大和(二)【中等 前缀和数组+动态规划 Java/Go/PHP/C++】

题目 题目链接&#xff1a; https://www.nowcoder.com/practice/11662ff51a714bbd8de809a89c481e21 思路 前缀和数组动态规划Java代码 import java.util.*;public class Solution {/*** 代码中的类名、方法名、参数名已经指定&#xff0c;请勿修改&#xff0c;直接返回方法规…

课时138:变量进阶_变量实践_综合案例

2.1.3 综合案例 学习目标 这一节&#xff0c;我们从 免密认证、脚本实践、小结 三个方面来学习 免密认证 案例需求 A 以主机免密码认证 连接到 远程主机B我们要做主机间免密码认证需要做三个动作1、本机生成密钥对2、对端机器使用公钥文件认证3、验证手工演示 本地主机生成…

MyBatis报错:TypeException Could not set parameters for mapping问题解决

MyBatis报错&#xff1a;TypeException: Could not set parameters for mapping问题解决 问题收录 org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.type.TypeException: Could not set parameters for mapping: ParameterMapping{proper…

【详细介绍下PostgreSQL】

&#x1f308;个人主页: 程序员不想敲代码啊 &#x1f3c6;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f44d;点赞⭐评论⭐收藏 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共…

考研数学|强化跟「张宇」还是「武忠祥」?看这一篇!

考研数学强化阶段是备考过程中非常关键的一环&#xff0c;它不仅要求学生巩固和深化基础知识&#xff0c;还要求学生能够灵活运用所学知识解决复杂问题。 在选择张宇老师或武忠祥老师的高数强化课时&#xff0c;你可以考虑以下几个方面。 首先每位学生都有自己独特的学习风格…

图片数据增强-resize(不同插值)、各种模糊

各种不同的模糊处理 import os import cv2def apply_blur_to_images(input_folder_path, output_folder_path):# 遍历文件夹下的所有文件for filename in os.listdir(input_folder_path):# 检查文件类型是否为图片if filename.endswith(.jpg) or filename.endswith(.jpeg) or …

探索演进:了解IPv4和IPv6之间的区别

探索演进&#xff1a;了解IPv4和IPv6之间的区别 在广阔的互联网领域中&#xff0c;设备之间的通信依赖于一组独特的协议来促进连接。前景协议中&#xff0c;IPv4&#xff08;Internet 协议版本 4&#xff09;和 IPv6&#xff08;Internet 协议版本 6&#xff09;是数字基础设施…

ThreadLocal简介

Thread类中&#xff0c;有个ThreadLocal.ThreadLocalMap 的成员变量。 ThreadLocalMap内部维护了Entry数组&#xff0c;每个Entry代表一个完整的对象&#xff0c;key是ThreadLocal本身&#xff0c;value是ThreadLocal的泛型对象值 public void set(T value) {Thread t Thread…

【Text2SQL 论文】IncSQL:通过增量式生成 action 序列来得到 SQL

论文&#xff1a;IncSQL: Training Incremental Text-to-SQL Parsers with Non-Deterministic Oracles ⭐⭐⭐ ICLR 2019&#xff0c;arXiv:1809.05054, Microsoft Research 一、论文速读 本文提出了 IncSQL&#xff0c;一个使用 Non-Deterministic Oracles 思路的增量式 Text…

问题记录_stm32“No target connected“

问题描述&#xff1a; 基于HAL库和stm32cubeMX生成的代码&#xff0c;烧录时出现如下报错窗口&#xff1a; 问题原因&#xff1a; stm32cubeMX生成代码时关闭了SWJ调试功能 解决方法&#xff1a; 在项目中找到__HAL_AFIO_REMAP_SWJ_DISABLE();并注释掉 然后短按复位键的…

电脑技巧:一台主机两个显示器的连接设置方法

目录 一、先与电脑连接好两个显示器 二、先来看看WIN7连接两个显示器设置方法 三、再来看看WIN10连接两个显示器设置方法 在日常办公场景中&#xff0c;为了提高工作效率和增强交互体验&#xff0c;常需一台电脑同时连接两个显示器&#xff0c;正如我们在营业厅常见到的那样…

这是你要找的可视化开发平台吗?【送源码】

今天着重推荐一款高效的拖拽式低代码数据可视化开发平台 它就是 goView 它将图表或页面元素封装为基础组件&#xff0c;无需编写代码即可制作数据大屏&#xff0c;减少心智负担。 介绍 框架&#xff1a;基于 Vue3 框架编写&#xff0c;使用 hooks 写法抽离部分逻辑&#xf…

Java通过Html(ftl模板)生成PDF实战, 可支持商用

Java通过Html(freemarker模板)生成PDF实战, 可支持商用 技术架构 springboot freemarker [pdfbox] flying-saucer-pdf 生成流程&#xff1a; freemarker: 根据数据填充ftl模板文件&#xff0c;得到包含有效数据的html文件&#xff08;包含页眉页脚页码的处理&#xff0c…

服务器软件架构演进

服务器软件架构演进 背景介绍阶段一&#xff1a;单机部署阶段二&#xff1a;应用与数据分离部署阶段三&#xff1a;启用缓存优化阶段四&#xff1a;启用应用服务器集群阶段五&#xff1a;数据库读写分离阶段六&#xff1a;启用反向代理及CDN加速阶段七&#xff1a;启用分布式文…

论文阅读--GroupViT

视觉之前做无监督分割的时候&#xff0c;经常使用grouping方法&#xff1a;如果有一些聚类的中心点&#xff0c;从这写点开始发散&#xff0c;把周围相似的点逐渐扩充成一个group&#xff0c;这个group就相当是一个segmentation mask 右边是grouping block&#xff0c;左边的两…

【Java】IdentityHashMap 的使用场景

文章目录 前言1. Druid 应用场景2. IdentityHashMap 特性3. IdentityHashMap 同步化4. IdentityHashMap 处理key为空值后记 前言 最近有兴趣看一下 Druid 连接池怎么做连接管理的&#xff0c;看到一个类 IdentityHashMap &#xff0c;这里记录一下使用场景。 1. Druid 应用场…

MySQL数据库语法(二)

一、数据库的创建 创建数据库CRATE DATABASE语法&#xff1a;CREATE DATABASE [IF NOT EXISTS]数据库名;功能&#xff1a;用给定的名字创建一个数据库如果数据库已经存在&#xff0c;发生一个错误。查看创建数据库&#xff1a;SHOW CREATE DATABASE <数据库名>&#xff…

通过键值对访问字典

自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 在Python中&#xff0c;如果想将字典的内容输出也比较简单&#xff0c;可以直接使用print()函数。例如&#xff0c;要想打印dictionary字典&#xff…

【Redis】Widows 和 Linux 下使用 Redis

Redis 简述 1.缓存 缓存就是将数据存放在距离计算最近的位置以加快处理速度。缓存是改善软件性能的第一手段,现代 CPU 越来越快的一个重要因素就是使用了更多的缓存,在复杂的软件设计中,缓存几乎无处不在。大型网站架构设计在很多方面都使用了缓存设计。 2.Redis Redis …

神龙秘籍 无极神功 无极管理 真正的力量来自于自我内心。每个人都有潜力成为伟大的,只需要相信自己并发现内在的力量。

功夫熊猫中神龙秘籍的含义 在动画电影《功夫熊猫》中&#xff0c;神龙秘籍&#xff08;Dragon Scroll&#xff09;是一个具有重要象征意义的物品。影片通过神龙秘籍传达了几个深刻的主题和教训。 内在力量与自我发现&#xff1a;当阿宝&#xff08;Po&#xff09;最终打开神龙…