排序算法:插入排序、希尔排序、选择排序、冒泡排序、堆排序、快速排序

1. 插入排序

基本思想

插入排序的基本思想是将一个元素插入到已经排好序的有序表中,从而形成一个新的、记录数增加1的有序表。具体步骤如下:

  1. 将原数组分为有序数组和无序数组两部分。将数组的第一个元素视为有序数组,其余部分视为无序数组。
  2. 从第二个元素开始,遍历无序数组,将当前元素与有序数组中的元素进行比较(有序数组从后往前依次比较)。如果当前元素小于有序数组中的元素,则将有序数组中的元素向后移动一位,继续比较直到找到大于当前元素的位置。
  3. 如果当前元素大于有序数组中的最后一个元素,则直接将当前元素添加到有序数组的末尾。

代码实现

#include <stdio.h>

void insertionSort(int arr[], int n) {
    int i, key, j;
    for (i = 1; i < n; i++) {
        key = arr[i]; // 当前要排序的元素
        j = i - 1;

        // 将arr[i]与已排序好的序列arr[0...i-1]中的元素进行比较,找到合适的位置插入
        while (j >= 0 && arr[j] > key) {
            arr[j + 1] = arr[j]; // 将大于key的元素向后移动
            j = j - 1;
        }
        arr[j + 1] = key; // 将key插入到合适的位置
    }
}

int main() {
    int arr[] = {5, 2, 4, 6, 1, 3};
    int n = sizeof(arr) / sizeof(arr[0]);
    insertionSort(arr, n);
    printf("排序后的数组: \n");
    for (int i = 0; i < n; i++)
        printf("%d ", arr[i]);
    printf("\n");
    return 0;
}

示例输出

对于输入数组{5, 2, 4, 6, 1, 3},排序后的结果为1 2 3 4 5 6

时间复杂度

插入排序的时间复杂度为O(n^2),其中n是数组中的元素数量。在最坏情况下(即输入数组是逆序的),需要进行n(n-1)/2次比较和交换。在最好情况下(即输入数组已经有序),只需要进行n-1次比较,不需要交换。

2. 希尔排序

基本思想

  1. 分组:选择一个增量序列(例如n/2, n/4, n/8等),将待排序的数组分成若干个子序列,每个子序列中的元素间隔为当前的增量。
  2. 插入排序:对每个子序列分别进行直接插入排序。
  3. 缩小增量:逐步减小增量,重复上述分组和排序过程,直到增量减小到1。
  4. 最终排序:当增量为1时,整个数组被分成一个子序列,此时进行最后一次插入排序,完成整个排序过程。

代码实现

#include <stdio.h>

// 希尔排序函数
void shellSort(int arr[], int n) {
    int gap, i, j, temp;
    // 从n/2开始,每次减半,直到增量为1
    for (gap = n / 2; gap > 0; gap /= 2) {
        // 对每个子数组进行插入排序
        for (i = gap; i < n; i++) {
            temp = arr[i];
            for (j = i; j >= gap && arr[j - gap] > temp; j -= gap) {
                arr[j] = arr[j - gap];
            }
            arr[j] = temp;
        }
    }
}

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

int main() {
    int arr[] = {9, 5, 2, 7, 8, 1, 3, 5, 4, 8, 7, 6, 2, 1, 5};
    int n = sizeof(arr) / sizeof(arr[0]);
    printf("排序前的数组: \n");
    printArray(arr, n);
    shellSort(arr, n);
    printf("排序后的数组: \n");
    printArray(arr, n);
    return 0;
}

  • 在外层循环中,j是从i开始递减的,每次减去gap的值,直到arr[j - gap]不再大于temp。这意味着j最终停在了第一个不大于temp的元素的下一个位置。

  • j停止递减时,arr[j]实际上是temp应该插入的位置。此时,arr[j - gap]temp应该替换的元素,而不是之前移动的元素。因此,当我们执行arr[j] = temp;时,我们并没有覆盖之前的交换操作,而是将temp放到了正确的位置。

举例:

  • 假设我们有一个数组arr = {9, 8, 7, 6, 5, 4, 3, 2, 1}gap为3,我们正在处理索引5(即值5)。

  • i = 5temp = arr[5] = 4
  • j = 5j - gap = 2arr[2] = 7,因为7 > 4,我们执行arr[5] = arr[2] = 7
  • j = 2j - gap = -1(但我们不会执行这个比较,因为j已经小于gap了)。
  • 现在,j停在了2,arr[2]是第一个不大于temp(4)的元素。因此,我们将temp(4)放到      arr[2]的位置,覆盖了之前的7。 
  • 最终,数组变为{9, 8, 4, 6, 5, 3, 2, 1, 7},并且temp(4)被正确地插入到了数组中。

示例输出

排序前的数组: 9 5 2 7 8 1 3 5 4 8 7 6 2 1 5 排序后的数组: 1 1 2 2 3 4 5 5 5 6 7 7 8 8 9

时间复杂度

希尔排序的时间复杂度受增量序列的选择影响较大。在最坏情况下,时间复杂度可能退化为O(n^2),但在实际应用中,通过合理选择增量序列,时间复杂度可以达到O(nlogn)甚至更优。

3. 选择排序

基本实现步骤

  1. 初始化最小索引:从数组的第一个元素开始,假设该元素为当前最小值。
  2. 寻找最小值:遍历数组的未排序部分,找到比当前最小值更小的元素,并更新最小值的索引。
  3. 交换元素:将找到的最小值与当前未排序部分的第一个元素交换位置。
  4. 重复步骤:将已排序部分向右扩展一个元素,重复上述过程,直到所有元素排序完成。

代码实现

#include <stdio.h>

// 交换两个整数的值
void swap(int *xp, int*yp) {
    int temp = *xp;
    *xp =*yp;
    *yp = temp;
}

// 选择排序函数
void selectionSort(int arr[], int n) {
    int i, j, min_idx;

    // 遍历数组
    for (i = 0; i < n-1; i++) {
        // 假设当前元素为最小值
        min_idx = i;
        // 在未排序部分寻找最小值
        for (j = i+1; j < n; j++)
            if (arr[j] < arr[min_idx])
                min_idx = j;

        // 如果找到的最小值不是当前元素,则交换
        if(min_idx != i)
            swap(&arr[min_idx], &arr[i]);
    }
}

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

// 主函数
int main() {
    int arr[] = {64, 25, 12, 22, 11};
    int n = sizeof(arr)/sizeof(arr[0]);
    printf("排序前的数组: \n");
    printArray(arr, n);
    selectionSort(arr, n);
    printf("排序后的数组: \n");
    printArray(arr, n);
    return 0;
}

示例输出

排序前的数组: 64 25 12 22 11 排序后的数组: 11 12 22 25 64

时间复杂度

选择排序的时间复杂度为O(n^2),空间复杂度为O(1),是一种不稳定的排序算法。

4. 冒泡排序

基本原理

  1. 比较与交换:从数组的第一个元素开始,依次比较相邻的两个元素。如果前一个元素大于后一个元素,则交换它们的位置。
  2. 重复步骤:继续比较和交换相邻元素,直到数组最末尾的元素已排序完毕。
  3. 重复排序:重复上述步骤,直到整个数组排序完成。

代码实现

#include <stdio.h>

void bubbleSort(int arr[], int n) {
    int i, j, temp;
    int swapped;//引入swapped标志位,用于优化算法。

    for (i = 0; i < n - 1; i++) {// 外层循环控制比较次数
        swapped = 0;
        for (j = 0; j < n - i - 1; j++) {// 内层循环控制每次比较元素的数量
            if (arr[j] > arr[j + 1]) {
                temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;
                swapped = 1;
            }
        }
        if (swapped == 0) {
            break; // 如果没有发生交换,说明数组已经有序,提前退出循环
        }
    }
}

int main() {
    int arr[] = {5, 4, 3, 2, 1};
    int n = sizeof(arr) / sizeof(arr[0]);
    bubbleSort(arr, n);
    printf("Sorted array: ");
    for (int i = 0; i < n; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
    return 0;
}

示例输出

排序前数组:5 7 3 2 9 4 1 8 6 0 排序后数组:0 1 2 3 4 5 6 7 8 9

时间复杂度

冒泡排序的时间复杂度取决于输入数据的初始状态:

  1. 最好情况:当输入数据已经是正序时,冒泡排序只需要进行一次遍历来确认数据是否已经有序。此时的时间复杂度为O(n),其中n为数组长度。
  2. 最坏情况:当输入数据是逆序时,冒泡排序需要进行n-1轮比较和交换,每轮比较次数逐渐减少。总的比较次数为(n-1)+(n-2)+...+1,即(n^2-n)/2,因此时间复杂度为O(n^2)。
  3. 平均情况:在一般情况下,冒泡排序的平均时间复杂度也是O(n^2)。

5. 堆排序

堆排序

6. 快速排序

基本原理

  1. 选择基准(Pivot)

    • 从数组中选择一个元素作为基准。基准的选择可以是第一个元素、最后一个元素、随机元素或中间元素。在给定的代码中,基准通常是数组的第一个元素 q[l] 或者中间元素 q[(l+r)/2]
  2. 分区(Partitioning)

    • 重新排列数组,使得所有比基准小的元素放在基准前面,所有比基准大的元素放在基准后面。在这个过程中,基准元素最终会被放置在它的正确位置上。
    • 使用两个指针 i 和 j,分别从数组的两端向中间移动。i 从左向右寻找第一个大于基准的元素,j 从右向左寻找第一个小于基准的元素。当 i 和 j 相遇时,交换这两个元素的位置,直到 i 不小于 j
  3. 递归排序

    • 对基准左边的子数组和右边的子数组分别进行快速排序。递归地重复这个过程,直到每个子数组只包含一个元素或为空,此时数组就被排序完成了。

代码实现

#include<iostream>
using namespace std;
const int N=1e6+10;
int n;
int q[N];
void quick_sort(int q[],int l,int r){if(l>=r){return ;}int x=q[l],i=l-1,j=r+1;while(i<j){do{i++;}while(q[i]<x);do{j--;}while(q[j]>x);if(i<j){swap(q[i],q[j]);}}quick_sort(q,l,j);quick_sort(q,j+1,r);
}
int main(){scanf("%d",&n);for(int i=0;i<n;i++){scanf("%d",&q[i]);}quick_sort(q,0,n-1);for(int i=0;i<n;i++){printf("%d ",q[i]);}return 0;
}

示例输出

排序前数组:

5
3 1 2 4 5

排序后数组:1 2 3 4 5

时间复杂度

  • 平均时间复杂度:O(nlogn)
  • 最坏情况下的时间复杂度:O(n²)

7. 归并排序

基本原理

  1. 分解(Divide)

    • 如果序列的长度为1,则认为该序列已经有序,直接返回。
    • 否则,找到序列的中间位置,将序列分成两个子序列。
  2. 解决(Conquer)

    • 递归地对两个子序列分别进行归并排序。
  3. 合并(Combine)

    • 将两个有序的子序列合并成一个有序的序列。

代码实现

#include<iostream>
using namespace std;
const int N=100010;
int n;
int q[N],tmp[N];
void merge_sort(int q[],int l,int r){if(l>=r){return ;}int mid=l+r>>1;merge_sort(q,l,mid);merge_sort(q,mid+1,r);int k=0,i=l,j=mid+1;while(i<=mid&&j<=r){if(q[i]<=q[j])tmp[k++]=q[i++];else tmp[k++]=q[j++];}while(i<=mid){tmp[k++]=q[i++];}while(j<=r){tmp[k++]=q[j++];}for(int i=l,j=0;i<=r;i++,j++){ q[i]=tmp[j];}
}
int main(){scanf("%d",&n);for(int i=0;i<n;i++){scanf("%d",&q[i]);}merge_sort(q,0,n-1);for(int i=0;i<n;i++){printf("%d ",q[i]);}return 0;
}

示例代码

排序前数组:
5
3 1 2 4 5

排序后数组:1 2 3 4 5 

时间复杂度

归并排序的时间复杂度为O(n log n),在最坏、平均和最佳情况下表现一致。这使得它在处理大规模数据时具有较高的效率。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Kafka中的Topic和Partition有什么关系?

大家好&#xff0c;我是锋哥。今天分享关于【Kafka中的Topic和Partition有什么关系&#xff1f;】面试题。希望对大家有帮助&#xff1b; Kafka中的Topic和Partition有什么关系&#xff1f; 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 在 Apache Kafka 中&#…

一文读懂变分自编码(VAE)

一文读懂变分自编码(VAE) 概述 变分自编码器&#xff08;Variational Autoencoder, VAE&#xff09;是一种生成模型&#xff0c;用于学习数据的潜在表示并生成与原始数据分布相似的新数据。它是一种概率模型&#xff0c;通过结合深度学习和变分推断的思想&#xff0c;解决了传…

第十七周:Fast R-CNN论文阅读

Fast R-CNN论文阅读 摘要Abstract文章简介1. 引言2. Fast R-CNN框架2.1 RoI位置信息映射2.2 RoI pooling2.3 分类器与边界框回归器2.4 以VGG16为backbone的Fast RCNN的网络结构 3. 训练细节3.1 采样3.2 多任务损失 4. 优缺点分析总结 摘要 这篇博客介绍了Fast R-CNN&#xff0…

ThinkPHP 8开发环境安装

【图书介绍】《ThinkPHP 8高效构建Web应用》-CSDN博客 《ThinkPHP 8高效构建Web应用 夏磊 编程与应用开发丛书 清华大学出版社》【摘要 书评 试读】- 京东图书 1. 安装PHP8 Windows系统用户可以前往https://windows.php.net/downloads/releases/archives/下载PHP 8.0版本&am…

VM虚拟机配置ubuntu网络

目录 桥接模式 NAT模式 桥接模式 特点&#xff1a;ubuntu的IP地址与主机IP的ip地址不同 第一部分&#xff1a;VM虚拟机给ubuntu的网络适配器&#xff0c;调为桥接模式 第二部分&#xff1a;保证所桥接的网络可以上网 第三部分&#xff1a;ubuntu使用DHCP&#xff08;默认&…

日本IT行业|分享实用的开发语言及框架

在日本IT行业中&#xff0c;开发语言与框架的选择非常多样化&#xff0c;但也有一些特定的技术和框架更为流行。以下是对日本IT行业在用的开发语言与框架的详细分享&#xff1a; 开发语言 Java&#xff1a;Java在日本是一门非常稳定且受欢迎的编程语言&#xff0c;很多日本公…

【畅购商城】校验用户名、手机号以及前置技术Redis和阿里大鱼短信验证码

搭建环境 后端web服务&#xff1a;changgou4-service-web修改pom.xml文档 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance&…

[创业之路-222]:波士顿矩阵与GE矩阵在业务组合选中作用、优缺点比较

目录 一、波士顿矩阵 1、基本原理 2、各象限产品的定义及战略对策 3、应用 4、优点与局限性 二、技术成熟度模型与产品生命周期模型的配对 1、技术成熟度模型 2、产品生命周期模型 3、技术成熟度模型与产品生命周期模型的配对 三、产品生命周期与产品类型的对应关系 …

第三方接口设计注意要点

实际工作中&#xff0c;我们会遇到与三方系统对接的情形&#xff0c;比如对接短信服务、支付服务、地图服务、以及一些外部业务系统的调用和回调等等&#xff0c;不论是我们调用第三方接口还是我们为其他系统提供接口服务&#xff0c;调用过程中会遇到一些大大小小的问题和吐槽…

折腾日记:如何让吃灰笔记本发挥余热——搭建一个相册服务

背景 之前写过&#xff0c;我在家里用了一台旧的工作站笔记本做了服务器&#xff0c;连上一个绿联的5位硬盘盒实现简单的网盘功能&#xff0c;然而&#xff0c;还是觉的不太理想&#xff0c;比如使用filebrowser虽然可以备份文件和图片&#xff0c;当使用手机使用网页&#xf…

【设计与实现】基于Bootstrap的地方旅游管理系统的设计与实现

目录 第一章 绪论 1.1 研究现状 1.2 设计原则 1.3 研究内容 第四章 系统设计 4.1系统结构设计 4.2系统顺序图设计 4.3数据库设计 第五章 系统实现 5.1登录模块的实现 第一章 绪论 1.1 研究现状 时代的发展&#xff0c;我们迎来了数字化信息时代&#xff0c;它正在渐…

人工智能与区块链的碰撞:双剑合璧的创新前景

引言 人工智能&#xff08;AI&#xff09;与区块链技术&#xff0c;这两项曾经各自独立发展的前沿科技&#xff0c;如今正逐步走向融合。人工智能通过强大的数据处理能力和智能决策能力&#xff0c;在各个领域掀起了革命性的变革&#xff1b;而区块链凭借其去中心化、不可篡改的…

HarmonyOS NEXT 实战之元服务:静态案例效果---我的热门应用服务

背景&#xff1a; 前几篇学习了元服务&#xff0c;后面几期就让我们开发简单的元服务吧&#xff0c;里面丰富的内容大家自己加&#xff0c;本期案例 仅供参考 先上本期效果图 &#xff0c;里面图片自行替换 效果图1完整代码案例如下&#xff1a; Index import { authentica…

ArcGIS Pro地形图四至角图经纬度标注与格网标注

今天来看看ArcGIS Pro 如何在地形图上设置四至角点的经纬度。方里网标注。如下图的地形图左下角经纬度标注。 如下图方里网的标注 如下为本期要介绍的例图&#xff0c;如下&#xff1a; 图片可点击放大 接下来我们来介绍一下 推荐学习&#xff1a;GIS入门模型构建器Arcpy批量…

数字图像处理

一 形态学处理 ①二值图像 PS&#xff1a;1&#xff08;255&#xff09;代表的是白 0代表的是黑&#xff08;0就是什么都看不见&#xff0c;就是黑&#xff09; ②灰度图像 ③彩色图像 ④数学形态学基础&#xff1a;是分析几何形状和结构的数学方法&#xff0c;它建立在…

linux-软硬链接

我们今天再来聊一下这个"软硬链接"的问题. 目录 1. 软硬链接长什么样?2. 软连接和硬链接的特征 和 应用2.1 软连接特征 及其 应用?①软连接是什么?②软连接的应用1: 快捷方式③软连接的应用2: 方便维护库文件 2.2 硬连接特征 及其 应用?①硬链接是什么?②引用计…

SpringCloud 系列教程:微服务的未来(三)IService接口的业务实现

本文将介绍 IService 接口的基本业务操作、复杂业务操作、Lambda 方法的使用以及批量增加操作&#xff0c;帮助开发者深入了解如何高效地利用 MyBatis-Plus 提供的功能进行数据库操作。无论是简单的单表查询&#xff0c;还是复杂的多表联动&#xff0c;甚至是大数据量的批量操作…

Linux第100步_Linux之设置LCD作为终端控制台和LCD背光调节

KMS是Kemmel Mode Setting的缩写&#xff0c;内核显示模式设置。它主要负责显示的控制&#xff0c;包括屏幕分辨率、屏幕刷新率和颜色深度等等。 CRTC是指显示控制器&#xff0c;在DRM里有多个显存&#xff0c;通过操作CRTC来控制要显示那个显存。 KMS包含了FB框架。DRM驱动默…

解决pycharm无法识别miniconda

解决pycharm无法识别miniconda 选中 conda.bat 点击 Load Enviroments

云手机群控能用来做什么?

随着云手机的发展&#xff0c;云手机群控技术逐渐从小众的游戏多开工具&#xff0c;发展为涵盖多个领域的智能操作平台。不论是手游搬砖、短视频运营&#xff0c;还是账号养成等场景&#xff0c;云手机群控都展现出了强大的应用潜力。本文将为大家详细解析云手机群控的应用场景…