C语言分析基础排序算法——交换排序

news2024/11/18 23:47:54

目录

交换排序

冒泡排序

快速排序

Hoare版本快速排序

挖坑法快速排序

前后指针法快速排序

快速排序优化

快速排序非递归版


交换排序

冒泡排序

见C语言基础知识指针部分博客C语言指针-CSDN博客

快速排序

Hoare版本快速排序

Hoare版本快速排序的过程类似于二叉树前序遍历的过程,基本思想是:在需要排序的数据中确定一个值放入key中,接着使用左指针和右指针从数据的起始位置以及数据的末端位置向中间遍历,左指针找数值比key大的数据,右指针找数值比key小的数据,交换这两个指针的数据之后接着向中间移动,直到两个指针最后相遇时交换key所在的数值以及相遇位置的数值,完成第一趟排序,接着进行左边部分的排序,方法如同第一趟排序,左边排序完毕后进行右边部分的排序,方法如同第二趟排序,直到最后全部排序完成后为止。具体思路如下:

📌

注意key是数值的下标

//以下面的数组为例
int data[] = { 23,48,67,45,21,90,33,11 };

下面是完整过程递归图

参考代码

void swap(int* num1, int* num2)
{
    int tmp = *num1;
    *num1 = *num2;
    *num2 = tmp;
}

//一趟排序,返回key指向的位置
int PartSort(int* data, int left, int right)
{
    int key = left;//使key指向区间的第一个元素位置
    while (left < right)
    {
        //先让右侧指针先走,右指针找小
        while (left < right && data[right] >= data[key])
        {
            right--;
        }
        //再让左侧指针走,左侧指针找大
        while (left < right && data[left] <= data[key])
        {
            left++;
        }
        
        //交换右侧指针和左侧指针的数据
        swap(&data[right], &data[left]);
    }
    //执行完循环后,交换key所在位置的数据和right与left指针相遇的位置的数值
    swap(&data[key], &data[left]);
    //返回交换后的key指向的元素的位置
    return left;
}

//Hoare版本
void QuickSort_Hoare(int* data, int left, int right)
{
    //当left和right指针指向同一个位置或后一个位置结束排序
    if (left >= right)
    {
        return;
    }

    //获取到当前key的位置
    int key = PartSort(data, left, right);

    QuickSort_Hoare(data, left, key - 1);
    QuickSort_Hoare(data, key + 1, right);
}

Hoare版本问题分析

  • 在上面的过程分析中,使用第一个元素的位置作为key的位置,也可以使用最后一个元素作为key的位置,但是需要注意的是,以key指向第一个元素的位置为例,left指针一定需要指向第一个元素,而不是从第二个元素开始,例如下面的情况:当key位置的数据比其他数据都小时,right找小将一直向key所在位置移动

  • 在判断left指针或者right指针是否需要移动时,需要包括等于的情况,否则会进入死循环,例如下面的情况:当leftright指针同时指向一个等于key所在位置的元素

  • 对于递归结束的条件来说,需要出现left指针的值大于或者等于right指针的值,而不是仅仅一个大于或者等于,因为返回相遇的位置,即返回left指针或者right指针的位置而不是实际返回key所在位置,在交换过程中,只是交换key对应位置的数值和相遇位置的数值,并没有改变key指向的位置
  • 对于left指针和right指针相遇的位置的数值一定比key所在位置的数值小的问题,下面是基本分析:

分析主问题之前,先分析rightleft指针先走的原因:在初始位置时,left指针和right指针各指向第一个元素和最后一个元素但是left指针与key指针指向的位置相同,此时让right指针先走,而不是left指针先走,反之同理,具体原因如下:

接下来分析当right指针比left指针先走时,两个指针相遇时一定相遇到一个比key小的数值的问题

两个指针相遇的方式有两种情况

  • 第一种情况:left指针向right指针移动与其相遇
  • 第二种情况:right指针向left指针移动与其相遇

对于第一种情况,分析如下:

对于第二种情况,分析如下:

挖坑法快速排序

挖坑法快速排序相较于Hoare版本的快速排序没有效率上的优化,但是在理解方式上相对简单,其基本思路为:在数据中随机取出一个数值放入key变量中,此时该数值的位置即为一个坑位,接下来left指针从第一个元素开始key值大的数值,right指针从最后一个元素找比key值小的数值,此时不用考虑left指针和right指针谁先走,考虑right指针先走,当right指针找到小时,将该值放置到hole所在位置,更新holeright指针的位置,接下来left指针找大,当left指针找到较key大的数值时,将该数值存入hole中,更新holeleft所在位置,如此往复,直到第一趟排序结束。接着进行左边部分的排序,方法如同第一趟排序,左边排序完毕后进行右边部分的排序,方法如同第二趟排序,直到最后全部排序完成后为止。具体思路如下:

📌

注意key是数值

//以下面的数组为例
int data[] = { 23,48,67,45,21,90,33,11 };

int PartSort_DigHole(int* data, int left, int right)
{
    int hole = left;
    int key = data[left];
    while (left < right)
    {
        while (left < right && data[right] >= key)
        {
            right--;
        }
        data[hole] = data[right];
        hole = right;
        while (left < right && data[left] <= key)
        {
            left++;
        }
        data[hole] = data[left];
        hole = left;
    }
    data[hole] = key;
    return hole;
}

//挖坑法
void QuickSort_DigHole(int* data, int left, int right)
{
    if (left >= right)
    {
        return;
    }

    int hole = PartSort_DigHole(data, left, right);

    QuickSort_DigHole(data, left, hole - 1);
    QuickSort_DigHole(data, hole + 1, right);
}

前后指针法快速排序

前后指针法相对Hoare版本和挖坑法也没有效率上的优化,但是理解相对容易,基本思路如下:在前后指针法中,有一个key指针,该指针指向一个随机的数值的下标位置,接下来是一个prev指针,指向数据的第一个元素的下标位置,以及一个cur指针指向第二个元素的下标位置,cur指针和prev指针向前遍历,当遇到比key小的数值时,prev指针先移动柜,再进行curprev进行对应位置的数值交换,接着cur指着移动,否则只让cur指针移动,当cur走到数据的结尾时结束循环,交换prevkey指针的数据,完成第一趟排序。接着进行左边部分的排序,方法如同第一趟排序,左边排序完毕后进行右边部分的排序,方法如同第二趟排序,直到最后全部排序完成后为止。具体思路如下:

📌

注意key是数值的下标,并且用leftright控制递归区间

int PartSort_Prev_postPointer(int *data, int left, int right)
{
    int key = left;
    int cur = left + 1;
    int prev = left;
    while (cur <= right)
    {
        //++prev != cur可以防止cur和自己本身交换导致多交换一次
        if (data[cur] < data[key] && ++prev != cur)
        {
            prev++;
            swap(&data[cur], &data[prev]);
        }
        cur++;
    }
    swap(&data[prev], &data[key]);
    return prev;
}

//前后指针法
void QuickSort_Prev_postPointer(int* data,int left, int right)
{
    if (left >= right)
    {
        return;
    }

    int key = PartSort_Prev_postPointer(data, left, right);

    QuickSort_Prev_postPointer(data, left, key - 1);
    QuickSort_Prev_postPointer(data, key + 1, right);
}

快速排序优化

在快速排序优化部分,采用三数取中的思路对快速排序有大量重复数据或者有序情况下进行优化,所谓三数取中,即第一个元素的位置和最后一个元素的位置加和取一半的数值在数据中的位置,但是此时需要注意的是key当前位置为mid所在位置,为了不改变原来的快速排序代码,获得中间值下标时,交换key位置的值和mid位置的值即可

//三数取中
int GetMidIndex(int* data, int left, int right)
{
    int mid = (left + right) / 2;
    //获取左、中、有三个数中的中间数
    if (data[left] > data[mid])
    {
        if (data[mid] > data[right])
        {
            //left>mid>right
            return mid;
        }
        else if (data[left] > data[right])
        {
            //left>right>mid
            return right;
        }
        else
        {
            //right>left>mid
            return left;
        }
    }
    else
    {
        if (data[mid] < data[right])
        {
            //right>mid>left
            return mid;
        }
        else if (data[right] > data[left])
        {
            //mid>right>left
            return right;
        }
        else
        {
            //mid>left>right
            return left;
        }
    }
}

以前后指针版本修改为例

#define _CRT_SECURE_NO_WARNINGS 1

#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <assert.h>

void swap(int* num1, int* num2)
{
    int tmp = *num1;
    *num1 = *num2;
    *num2 = tmp;
}

//三数取中
int GetMidIndex(int* data, int left, int right)
{
    int mid = (left + right) / 2;
    //获取左、中、有三个数中的中间数
    if (data[left] > data[mid])
    {
        if (data[mid] > data[right])
        {
            //left>mid>right
            return mid;
        }
        else if (data[left] > data[right])
        {
            //left>right>mid
            return right;
        }
        else
        {
            //right>left>mid
            return left;
        }
    }
    else
    {
        if (data[mid] < data[right])
        {
            //right>mid>left
            return mid;
        }
        else if (data[right] > data[left])
        {
            //mid>right>left
            return right;
        }
        else
        {
            //mid>left>right
            return left;
        }
    }
}

int PartSort_Prev_postPointer(int *data, int left, int right)
{
    int mid = GetMidIndex(data, left, right);
    swap(&data[left], &data[mid]);
    int key = left;
    int cur = left + 1;
    int prev = left;
    while (cur <= right)
    {
        //++prev != cur可以防止cur和自己本身交换导致多交换一次
        if (data[cur] < data[key] && ++prev != cur)
        {
            swap(&data[cur], &data[prev]);
        }
        cur++;
    }
    swap(&data[prev], &data[key]);
    return prev;
}

//前后指针法
void QuickSort_Prev_postPointer(int* data,int left, int right)
{
    if (left >= right)
    {
        return;
    }

    int key = PartSort_Prev_postPointer(data, left, right);

    QuickSort_Prev_postPointer(data, left, key - 1);
    QuickSort_Prev_postPointer(data, key + 1, right);
}

int main()
{
    int data[] = { 23,48,67,45,21,90,33,11 };
    int sz = sizeof(data) / sizeof(int);
    QuickSort_Prev_postPointer(data, 0, sz - 1);
    for (int i = 0; i < sz; i++)
    {
        printf("%d ", data[i]);
    }

    return 0;
}
输出结果:
11 21 23 33 45 48 67 90

快速排序非递归版

由于递归的函数栈帧空间是在栈上创建的,而且栈上的空间较堆空间小,所以当数据量太大时,可以考虑用快速排序的非递归版,一般用栈来实现,基本思路如下:首先将数据的头和尾进行入栈操作,再在循环中通过出栈和获取栈顶元素控制左区间和右区间,可以先执行左区间或者右区间,本处以先右区间再左区间为例,通过需要拆分数据的部分出栈排序,再接着重复步骤最后排序完成,具体思路分析如下:

void QuickSort_NotRecursion(int* data, int left, int right)
{
    ST st = { 0 };
    STInit(&st);
    //压入第一个元素和最后一个元素所在位置
    STPush(&st, left);
    STPush(&st, right);

    while (!STEmpty(&st))
    {
        //获取区间
        int right = STTop(&st);
        STPop(&st);
        int left = STTop(&st);
        STPop(&st);

        //单趟排序
        int key = PartSort_Hoare(data, left, right);

        //更新区间
        //先压右侧区间
        if (key + 1 < right)
        {
            STPush(&st, key + 1);
            STPush(&st, right);
        }

        //再压左侧区间
        if (left < key - 1)
        {
            STPush(&st, left);
            STPush(&st, key - 1);
        }
    }

    STDestroy(&st);
}

快速排序的时间复杂度为O(N\times log_{2}{N}),空间复杂度为O(log_{2}{N}),属于不稳定算法

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

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

相关文章

3.6研究代码(2)

指的是微电网运行参数。 在MATLAB中&#xff0c;randi([0,1],1,48) 会生成一个包含1*48个0或1的随机整数数组。这意味着数组中的每个元素都将是0或1。 MATLAB帮助中心&#xff1a;均匀分布的伪随机整数 - MATLAB randi - MathWorks 中国https://ww2.mathworks.cn/help/matlab/r…

(二十四)Flask之flask-session组件

目录&#xff1a; 每篇前言&#xff1a;Flask-session 每篇前言&#xff1a; &#x1f3c6;&#x1f3c6;作者介绍&#xff1a;【孤寒者】—CSDN全栈领域优质创作者、HDZ核心组成员、华为云享专家Python全栈领域博主、CSDN原力计划作者 &#x1f525;&#x1f525;本文已收录于…

2024 年 AI 垂直应用迅速落地,人人都可以获得AI红利

演示站点&#xff1a; https://ai.uaai.cn 技能模块 官方论坛&#xff1a; www.jingyuai.com 京娱AI 随着人工智能技术的持续发展与突破&#xff0c;2024年AI辅助研发正成为科技界和工业界瞩目的焦点。从医药研发到汽车设计&#xff0c;从软件开发到材料科学&#xff0c;AI…

Nestjs与Vue实现多人聊天[简易版]

本项目是一个小demo,帮助各位理清一点开发思路&#xff0c;作为一个小参考&#xff0c;虽然技术栈是nodejs。但是其他语言也是相通的。 准备环境&#xff1a; Nodejs version >18.13.0Vue3Nestjssoket.io 一、初始化 打开一个路径启动cmd窗口&#xff0c;初始化前后端项…

YOLOv8独家改进:backbone改进 | 最新大卷积核CNN架构UniRepLKNet,ImageNet 88% | CVPR2024

💡💡💡本文独家改进:大核卷积一统多种模态!RepLK正统续作UniRepLKNet,代替YOLOv8 Backbone 改进结构图如下: 收录 YOLOv8原创自研 https://blog.csdn.net/m0_63774211/category_12511737.html?spm=1001.2014.3001.5482 💡💡💡全网独家首发创新(原创),适…

​​​​​​​ARCGIS API for Python进行城市区域提取

ArcGIS API for Python主要用于Web端的扩展和开发&#xff0c;提供简单易用、功能强大的Python库&#xff0c;以及大数据分析能力&#xff0c;可轻松实现实时数据、栅格数据、空间数据等多源数据的接入和GIS分析、可视化&#xff0c;同时提供对平台的空间数据管理和组织管理功能…

GaLore的全称是“Gradient Low-Rank Projection“,翻译过来就是“梯度低秩投影“

鉴于大家对GaLore比较感兴趣,我今天试着结合论文做一个更深入的解读: GaLore的全称是"Gradient Low-Rank Projection",翻译过来就是"梯度低秩投影"。它的核心思想是通过降低优化器状态的秩,来大幅减少内存占用。 在训练大模型时,我们需要存储三类数据:模型…

响应式招标投标网站模板

模板信息&#xff1a; 模板编号&#xff1a;29165 模板编码&#xff1a;UTF8 模板分类&#xff1a;博客、文章、资讯、其他 适合行业&#xff1a;招标类企业 模板介绍&#xff1a; 下载转载自&#xff1a;https://www.qnziyw.cn/cmsmb/eyoucms/wenzhangzx/4332.html 本模板自带…

arcgis栅格数据处理3——定义投影(同样适用于其他类型文件)

进行数据连接时可能出现未设置投影无法链接的情况&#xff0c;需要先定义投影 点击最右侧“目录”&#xff0c;弹出带有系统工具的面板&#xff0c;点击“data management tools”点击“投影”&#xff0c;“定义投影”

大模型快速实现python3+html内容在线渲染

需求&#xff1a; 有一份数据需要通过前端在线展示给用户&#xff0c;不需要复杂的样式交互&#xff0c;后端服务是基于Python3实现的API接口&#xff0c;对前端技术不是很了解&#xff0c;需要快速实现该需求。类似样式即可&#xff1a; 思路&#xff1a; 如果页面不复杂&am…

搭建拓扑图发送ARP及ICMP数据报文

文章目录 搭建拓扑图设备说明通过PC1 ping PC2小结&#xff1a;当arp表缓存过期&#xff0c;而mac学习表未过期当arp表缓存未过期&#xff0c;而mac学习表过期使用VLAN分割广播域 搭建拓扑图 设备说明 两台PC电脑 同理另外一台电脑也是同理配置 IP地址&#xff1a;192.168.1.…

【架构笔记3】做“用心”之人

凡事就怕“用心”二字&#xff0c;但是用心做事&#xff0c;其实如果没有前提和详情&#xff0c;这本就是一句正确的废话&#xff0c;在一些项目开发和落地过程中&#xff0c;我也有了一些新的体会&#xff0c;自认为不是多余。 我觉得心这个词至少包含四个含义&#xff1a;“…

H62410Y 惠海 降压恒压芯片 仪表供电芯片 24V36V100V降3.3V5V1A

降压恒压仪表供电芯片的工作原理如下&#xff1a; 输入电压传感器&#xff1a;感知电源电压的大小&#xff0c;以便后续控制电压输出。 储能元件&#xff1a;内部有储能元件&#xff08;如电容器或电感等&#xff09;&#xff0c;用于存储电荷或电能&#xff0c;以供后续转换…

ESP32S3部署Edge Impulse模型

在上一篇文章中我们介绍了如何使用edge impulse训练一个图片分类模型并导出arduino库文件。在这篇文章中我们将介绍如何在esp32s3中部署这个训练好的图片分类模型。 添加进Arduino库 有两种方法将下载的文件添加进Arduino库。 在Arduino IDE程序中&#xff0c;转到项目选项卡…

【深度学习笔记】优化算法——Adam算法

Adam算法 &#x1f3f7;sec_adam 本章我们已经学习了许多有效优化的技术。 在本节讨论之前&#xff0c;我们先详细回顾一下这些技术&#xff1a; 在 :numref:sec_sgd中&#xff0c;我们学习了&#xff1a;随机梯度下降在解决优化问题时比梯度下降更有效。在 :numref:sec_min…

【LeetCode: 151. 反转字符串中的单词 + 双指针】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

【格与代数系统】格与代数系统汇总

【格与代数系统】格与哈斯图 目录 关系 偏序关系 偏序集 可比性 全序集 最值与上下界 上下确界 格 代数系统 性质 格与代数系统的关系 分配格 有界格 有补格 布尔代数 例1 例2 对偶格 软代数 完备格 稠密性 优软代数 小结 关系 X,Y是两个非空集合, 记若…

C语言编译成库文件的要求

keil编译成库文件 在Keil中&#xff0c;将C语言源文件编译成库文件通常需要进行以下步骤&#xff1a; 创建一个新的Keil项目&#xff0c;并将所需的C语言源文件添加到该项目中。 在项目设置中配置编译选项&#xff0c;确保生成的目标文件符合库文件的标准格式。 编译项目&…

ULBF810-ASEMI新能源整流桥ULBF810

编辑&#xff1a;ll ULBF810-ASEMI新能源整流桥ULBF810 型号&#xff1a;ULBF810 品牌&#xff1a;ASEMI 封装&#xff1a;ULBF-4 最大重复峰值反向电压&#xff1a;1000V 最大正向平均整流电流(Vdss)&#xff1a;8A 功率(Pd)&#xff1a;中小功率 芯片个数&#xff1a…

无人机手持地面站软件功能详解,无人机手持地面站软件开发人员组成及成本分析

无人机手持地面站软件是专为无人机操控和任务管理设计的移动应用&#xff0c;它通常集成在智能手机、平板电脑或其他便携式设备上&#xff0c;使得用户可以在远离无人机的地方对飞行器进行实时监控与远程控制。 主要功能详解&#xff1a; 1. 飞行控制与姿态显示&#xff1a; …