常见排序算法

ops/2024/11/18 13:19:51/

目录

前言

相关概念

直接插入排序

希尔排序( 缩小增量排序 )

直接选择排序

堆排序

冒泡排序

计数排序(非比较排序)

基数排序

桶排序

​后记


前言

欢迎再次来到小鸥的博客,本系列总结了直接插入排序、希尔排序、选择排序、堆排序、冒泡排序、快速排序、归并排序、计数排序八种(基数排序和桶排序不做演示)常见排序的原理,并使用C语言再现,

快排和归并由于实现方法有多种,篇幅较长,将单独在第二篇讨论

下篇:快排和归并-CSDN博客

本期专栏:算法_海盗猫鸥的博客-CSDN博客

个人主页:海盗猫鸥-CSDN博客

本篇中代码使用C,且数据序列使用数组为例

相关概念

  • 排序:所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作。

  • 稳定性:在待排序的序列中存在相同的数据,他们在序列中的相对位置若在排序过后不变,则称这种算法>排序算法是稳定的,否则不稳定。

  • 内部排序:数据元素全部放在内存中的排序

  • 外部排序:数据元素太多不能同时放在内存中,根据排序过程的要求不断地在内外存之间移动数据的排序。

  • 排序种类

把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列 。

直接插入排序

原理解析:(升序)

当插入第 i(i>=1)个数时,前i-1个数已经有序,用arr[i]依次与arr[i-1],arr[i-2]...比较,若arr[i]小于比较数,则将次位置的数据向后挪动一位,直到arr[i]大于比较数,将arr[i]插入此位置;

每次完成一轮排序,前i-1个数都是有序的

void InsertSort(int* arr, int n)
{for (int i = 0; i < n - 1; i++){//[0,end]都为有序,向前插入数据;int end = i;//存储待插入数据int tmp = arr[end + 1];while (end >= 0){if (tmp < arr[end]){arr[end + 1] = arr[end];end--;}else{break;}}arr[end + 1] = tmp;}
}

直接插入排序的特性总结:

  1. 元素集合越接近有序,直接插入算法>排序算法的时间效率越高
  2. 时间复杂度:O(N^2)
  3. 空间复杂度:O(1),它是一种稳定的算法>排序算法
  4. 稳定性:稳定

希尔排序( 缩小增量排序 )

当序列中的数据越接近倒序,直接插入排序的效率就越低,直到完全为倒序时,时间复杂度降低为O(N)

由此希尔排序是对直接插入排序的优化,可以防止上述情况

原理解析:

1.预排序(让数组接近有序);

直接插入排序中,每次插入的都是紧邻着的下一个位置的数据

预排序将数组中数据,按照(i,i+gap,i+2*gap...,i+n*gap)的形式,将数组分为gap组;

分别对这些组进行插入排序

2.插入排序

预排序后,最后进行直接插入排序,

代码中gap取值不止一个,而是按照三分之一的规律减小

代码:

void ShellSort(int* arr, int n)
{int gap = n;while (gap > 1)//gap为1时就是一次直接插入排序{//gap > 1时为预排序//gap == 1是插入排序gap = gap / 3 + 1;//加1保证最后一次为直接插入排序for (int i = 0; i < n - gap; i++){int end = i;int tmp = arr[end + gap];while (end >= 0){if (tmp < arr[end]){arr[end + gap] = arr[end];end -= gap;}else{break;}}arr[end + gap] = tmp;}}
}

希尔排序的特性总结:

  1. 希尔排序是对直接插入排序的优化。
  2. 当gap > 1时都是预排序,目的是让数组更接近于有序。当gap == 1时,数组已经接近有序,这样插入排序就会很快。整体而言,可以达到优化的效果。
  3. 希尔排序的时间复杂度不好计算,因为gap的取值方法很多,导致很难去计算,记:O(N^1.3)即可;
  4. 空间复杂度:O(1)
  5. 稳定性:不稳定

直接选择排序

begin和end分别指向数组开头和末尾,使用mini和maxi来遍历寻找最大最小值,将最大最小值分别end和begin的值交换;

完成一次后最小值在数组最前面,最大值在数组最后面;begin++,end--进行下一轮循环,找到剩余数据中的最大最小值,如此循环;

代码:

void Swap(int* p1, int* p2)
{int tmp = *p1;*p1 = *p2;*p2 = tmp;
}//选择排序O(N^2)
void SelectSort(int* arr, int n)
{//最大最小值所在下标int mini;int maxi;int begin, end;mini = begin = 0;end = maxi = n - 1;//每次遍历存储找到最大最小值的位置while (begin < end){mini = begin;maxi = end;for (int i = begin + 1; i <= end; i++){if (arr[i] < arr[mini]){mini = i;}if (arr[i] > arr[maxi]){maxi = i;}}//交换begin和miniSwap(&arr[begin], &arr[mini]);//若begin本来是最大值,则maxi指向begin位置,且上述交换后,mini位置变为了最大值,begin位置变为了最小值//重新调整maxi指向if (begin == maxi)maxi = mini;Swap(&arr[end], &arr[maxi]);++begin;--end;}
}

直接选择排序的特性总结:

  1. 直接选择排序思考非常好理解,但是效率不是很好。实际中很少使用
  2. 时间复杂度:O(N^2)
  3. 空间复杂度:O(1)
  4. 稳定性:不稳定

堆排序

使用数据结构堆的向上调整和向下调整算法来模拟建堆,然后用向下调整排序

//向上调整
void AdjustUp(HPDataType* a, int child)
{assert(a);int parent = (child - 1) / 2;while (child > 0){if (a[child] < a[parent])//小堆{Swap(&a[parent], &a[child]);child = parent;parent = (child - 1) / 2;}else{break;}}
}//向下调整
void AdjustDown(HPDataType* a, int n, int parent)
{assert(a);//默认左孩子小int child = (parent * 2) + 1;//孩子元素的下标while (child < n){//让child指向左右孩子中小的一方if (child + 1 < n && a[child + 1] < a[child]){//右孩子小child++;}//判断孩子是否小于父亲节点,再交换if (a[child] < a[parent])//<是小堆,>是大堆{Swap(&a[child], &a[parent]);parent = child;child = (parent * 2) + 1;}else//孩子不小于父亲,即满足堆的结构,直接跳出调整函数{break;}}
}

堆排序

  • 降序,建小堆
    原理:小堆的top一定是最小值,将top和最后的元素交换,最小值就成为了顺序结构的最后一个元素
    再进行向下调整,将除开最后一个元素以外的最小值放到top位置,循环可得降序

  • 升序,建大堆

    原理:大堆的top一定是最大值,将top和最后的元素交换,最大值就成为了顺序结构的最后一个元素
    再进行向下调整,将除开最后一个元素以外的最大值放到top位置,循环可得升序

//堆排序
void HeapSort(HPDataType* a,int n)
{//降序,建小堆// 小堆的top一定是最小值,将top和最后的元素交换,最小值就成为了顺序结构的最后一个元素// 再进行向下调整,将除开最后一个元素以外的最小值放到top位置,循环可得降序//升序,建大堆//降序//向上调整,将数组视为完全二叉树,使其成为小堆//for (int i = 1; i < n; i++)//{//	AdjustUp(a, i);//}//向下调整,从倒数第一个非叶子节点开始(父节点),向下调整,建小堆for (int i = (n - 1 - 1) / 2; i >= 0; i--){AdjustDown(a, n, i);}int end = n - 1;while (end > 0){Swap(&a[end], &a[0]);AdjustDown(a, end, 0);end--;}
}

堆排序的特性总结:

  1. 堆排序使用堆来选数,效率就高了很多。
  2. 时间复杂度:O(N*logN)
  3. 空间复杂度:O(1)
  4. 稳定性:不稳定

冒泡排序

以升序为例:

前后两个数据比较,将大的换到后一个数位置,循环一次就将最大的数据放到了数组的位置;

循环n次,待排序的数就减少n个(最后n个数据已经排序完成);

// 冒泡排序
void BubbleSort(int* a, int n)
{//前后交换,每一轮将最大的放到最后//n个数循环n轮完成排序for (int i = 0; i < n - 1; i++)//第n轮只有第一个数,不需要交换{int flag = 0;for (int j = 1; j < n - i; j++)//j从1开始,所以结束的范围要比从0开始+1即n-i,而不是n-1-i;{if (a[j - 1] > a[j])//大的往后换{int tmp = a[j - 1];a[j - 1] = a[j];a[j] = tmp;flag = 1;}}if (flag == 0)break;}

冒泡排序的特性总结

  1. 冒泡排序是一种非常容易理解的排序(教学意义)
  2. 时间复杂度:O(N^2)
  3. 空间复杂度:O(1)
  4. 稳定性:稳定

计数排序(非比较排序)

使用额外开辟的数组count来统计每个数据出现的次数,并将数据的大小和count数组中的下标大小进行对应的映射。再将数据传回原序列即可。

图解:

代码:

//计数排序
//只适用于整数
void CountSort(int* arr, int n)
{int max = arr[0], min = arr[0];//假设最大最小值for (int i = 0; i < n; i++){if (arr[i] > max)max = arr[i];if (arr[i] < min)min = arr[i];}//范围int range = max - min + 1;//最大值到最小值的区间,代表count数组的空间大小//创建count数组记录每个数的出现次数int* count = (int*)calloc(range, sizeof(int));if (count == NULL){perror("calloc fail!");return;}//相当于遍历arr数组,计数到count数组中for (int i = 0; i < n; i++){count[arr[i]-min]++;//假设min = 100,那么当arr[i]=101时相对下标就是1,即arr[i]-min//数的大小映射count数组中的相对下标位置}//遍历count数组,将数据赋值返还给arrint j = 0;for (int i = 0; i < range; i++){//每个数有多少个,就放回arr中多少个while (count[i]--){arr[j++] = i + min;}}free(count);count = NULL;
}

计数排序的特性总结:

  1. 计数排序在数据范围集中时,效率很高,但是适用范围及场景有限。
  2. 时间复杂度:O(MAX(N,范围))
  3. 空间复杂度:O(范围)
  4. 稳定性:稳定

基数排序

按对应位数的大小排序,先排序个位,再排序十位,依次类推,完成排序

桶排序

将数据按照十位的大小,存到B数组的对应大小的下标处,每个下标处对应一个链表结构,将十位相同的数链接起来,再排序。

​后记

本篇介绍的直接插入排序、希尔排序、选择排序、堆排序、冒泡排序、计数排序六种排序就到此结束;

快排和归并请米娜桑见下一篇博客~~


http://www.ppmy.cn/ops/134707.html

相关文章

关于宝塔无法在php中安装fileinfo

关于宝塔无法在php中安装fileinfo&#xff0c;我用的是php7.3.32死活安装不了fileinfo,我还试过其他版本的php都不行&#xff0c;试过网上各种方法。。。由于之前没接触过php一面莫比&#xff0c;无奈看文件里面有没有fileinfo&#xff0c;明明php的src的exc中就有fileinfo就是…

基于Spring Boot的电子商务平台架构

2 相关技术 2.1 SpringBoot框架介绍 Spring Boot是一种不需要代码生成的一种框架&#xff0c;并且可以不需要配置任何的XML文件就可以&#xff0c;因为Spring Boot里面自带了很多接口&#xff0c;只需要配置不同的接口就会自动的应用并且识别需要的依赖&#xff0c;在配置方面非…

Java-01 深入浅出 MyBatis - MyBatis 概念 ORM映射关系 常见ORM 详细发展历史

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 大数据篇正在更新&#xff01;https://blog.csdn.net/w776341482/category_12713819.html 目前已经更新到了&#xff1a; MyBatis&#xff…

039 三种实现多线程的方式

文章目录 继承Thread类实现多线程实现Runnable接口实现多线程Callable FutureTask实现多线程 继承Thread类实现多线程 package com.xd.cubemall.juc;import lombok.extern.slf4j.Slf4j;/*** 1) 继承Thread类&#xff0c;实现多线程* extends Thread* 2) 实现Runnable接口&…

(长期更新)《零基础入门 ArcGIS(ArcMap) 》实验一(下)----空间数据的编辑与处理(超超超详细!!!)

续上篇博客&#xff08;长期更新&#xff09;《零基础入门 ArcGIS(ArcMap) 》实验一&#xff08;上&#xff09;----空间数据的编辑与处理&#xff08;超超超详细&#xff01;&#xff01;&#xff01;&#xff09;-CSDN博客 继续更新 本篇博客内容为道路拓扑检查与修正&#x…

Qt Event事件系统小探2

目录 事件过滤器 来看一个例子 拖放事件和拖放操作 Qt官方文档给出的说明 拖放 拖放类 配置 拖动 放置 覆盖建议的操作 子类化复杂窗口小部件 拖放操作 添加新的拖放类型 放置操作 放置矩形 剪贴板 其他函数的介绍 事件过滤器 我们知道&#xff0c;有的时候想…

计算机网络在线测试-概述

单项选择题 第1题 数据通信中&#xff0c;数据传输速率&#xff08;比特率&#xff0c;bps&#xff09;是指每秒钟发送的&#xff08;&#xff09;。 二进制位数 &#xff08;我的答案&#xff09; 符号数 字节数 码元数 第2题 一座大楼内的一个计算机网络系统&#xf…

【Chapter 3】Machine Learning Classification Case_Prediction of diabetes-XGBoost

文章目录 1、XGBoost Algorithm2、Comparison of algorithm implementation between Python code and Sentosa_DSML community edition(1) Data reading and statistical analysis(2)Data preprocessing(3)Model Training and Evaluation(4)Model visualization 3、summarize 1…