【脚踢数据结构】七大排序算法(详细版)

2023-11-23 15:20

本文主要是介绍【脚踢数据结构】七大排序算法(详细版),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

  • (꒪ꇴ꒪ ),Hello我是祐言QAQ
  • 我的博客主页:C/C++语言,Linux基础,ARM开发板,软件配置等领域博主🌍
  • 快上🚘,一起学习,让我们成为一个强大的攻城狮!
  • 送给自己和读者的一句鸡汤🤔:集中起来的意志可以击穿顽石!
  • 作者水平很有限,如果发现错误,可在评论区指正,感谢🙏

        当谈到排序算法时,计算机科学中有许多经典的排序算法,每种算法都有其独特的原理和特点。下面我将为您介绍一些常见的排序算法,包括它们的原理和代码实现。

一、算法的稳定性

         算法的稳定性指的是在排序过程中,相等元素的相对位置是否保持不变。如果算法是稳定的,那么相等元素在排序后的顺序与它们在原始序列中的顺序相同。稳定性在某些应用中非常重要,例如需要按多个条件排序时。

排序方法平均情况最好情况  最坏情况辅助空间稳定性
冒泡排序O(n^2)O(n)  O(n^2)O(1)稳定
简单选择排序O(n^2)O(n^2)O(n^2)O(1)稳定
直接插入排序O(n^2)O(n)  O(n^2)O(1)稳定
希尔排序O(n log n)~O(n^2)O(n^1.3)O(n^2)O(1)不稳定
堆排序O(n log n)O(n log n)O(n log n)O(1)不稳定
归并排序O(n log n)O(n log n)O(n log n)O(n)稳定
快速排序  O(n log n)O(n log n)O(n^2)O(log n)~O(n)不稳定
        那么不稳定性又是什么呢?

        具体来说,如果在原始序列中,元素 A 和元素 B 相等,且 A 在 B 之前,如果在排序后 A 仍然在 B 之前,那么排序算法就被称为是稳定的;如果排序后 A 和 B 的相对位置发生了改变,那么排序算法就是不稳定的。

        为了更好地理解稳定性和不稳定性,让我们通过一个示例来说明,假设我们有一个待排序的数组,其中每个元素是一个包含姓名和年龄的记录,如下所示:

Name    Age
------------
Alice   25
Bob     30
Carol   25
David   22

        现在我们要按照年龄进行排序,首先考虑使用快速排序,但快速排序是不稳定的排序算法。如果我们使用快速排序对年龄进行排序,可能会得到以下结果:

Name    Age
------------
David   22
Alice   25
Carol   25
Bob     30

        在这个排序后的结果中,虽然 Alice 和 Carol 在原始序列中的相对位置是正确的,但是 Carol 和 David 的相对位置发生了改变,所以快速排序是不稳定的。

        相比之下,如果我们使用稳定的排序算法,如冒泡排序或插入排序,对年龄进行排序,那么无论排序的结果如何,Alice 和 Carol 在排序后的序列中的相对位置仍然保持不变。


二、冒泡排序(Bubble Sort)

         冒泡排序是一种简单的排序算法。它通过多次比较相邻元素的大小并交换位置,使得最大(或最小)的元素逐渐“冒泡”到数组的一端。冒泡排序的时间复杂度为O(n^2),其中n是元素的数量。

         代码实现:

#include <stdio.h>// 交换数组中的两个元素
void swap(int arr[], int i, int j)
{int tmp = arr[i];arr[i] = arr[j];arr[j] = tmp;
}// 冒泡排序
void bubbleSort(int arr[], int n)
{for(int i  = 0 ; i < n ; i ++){for (int j= 0 ; j < n - i -1; j++){if (arr[j] > arr[j+1]){swap(arr , j ,j+1);}}				}}//遍历
void display(int arr[], int n)
{for (int i = 0; i < n; i++){printf("%d ", arr[i]);}printf("\n");}int main() {int arr[] = {64, 34, 25, 12, 22, 11, 90, 13, 14, 26, 27, 115, 91, 92, 84, 96, 86, 35, 37, 56, 58, 65, 10, 38, 99 , 105 , 88, 29};int n = sizeof(arr) / sizeof(arr[0]);printf("原始数组:\n");display(arr,n);//遍历bubbleSort(arr, n);printf("排序后的数组:\n");display(arr,n);//遍历return 0;
}

三、冒泡排序的改进:鸡尾酒排序

         鸡尾酒排序是对冒泡排序的改进。它通过交替地进行正向和反向的冒泡扫描,从而在每一轮排序中同时处理数组的两端。这样可以更快地将较大和较小的元素分别移到正确的位置。

         代码实现:

#include <stdio.h>
#include <time.h>// 交换数组中的两个元素
void swap(int arr[], int i, int j)
{int tmp = arr[i];arr[i] = arr[j];arr[j] = tmp;
}// 鸡尾酒排序
void cocktailSort(int arr[], int size)
{int left = 0;        // 数组的左边界int right = size - 1;// 数组的右边界while (left < right) // 当左边界小于右边界时继续排序{for (int i = left; i < right; i++) // 从左往右冒泡,将较大的元素往右边“冒”{if (arr[i] > arr[i + 1]){swap(arr, i, i + 1); // 如果相邻元素顺序错误,交换它们}}right--; // 右边界左移,排除已经排好序的元素for (int j = right; j > left; j--) // 从右往左冒泡,将较小的元素往左边“冒”{if (arr[j] < arr[j - 1]){swap(arr, j, j - 1); // 如果相邻元素顺序错误,交换它们}}left++; // 左边界右移,排除已经排好序的元素}
}//遍历
void display(int arr[], int n)
{for (int i = 0; i < n; i++){printf("%d ", arr[i]);}printf("\n");}int main()
{int arr[] = {64, 34, 25, 12, 22, 11, 90, 13, 14, 26, 27, 115, 91, 92, 84, 96, 86, 35, 37, 56, 58, 65, 10, 38, 99, 105, 88, 29};int n = sizeof(arr) / sizeof(arr[0]);printf("原始数组:\n");display(arr, n);// 获取开始时间clock_t start_time = clock();cocktailSort(arr, n); // 调用鸡尾酒排序函数对数组排序// 获取结束时间clock_t end_time = clock();double total_time = (double)(end_time - start_time) / CLOCKS_PER_SEC;printf("排序后的数组:\n");display(arr, n);printf("运行时间:%f 秒\n", total_time);return 0;
}

四、选择排序(Selection Sort)

         选择排序每次从未排序部分中选择最小(或最大)的元素,然后将其与未排序部分的起始位置交换。选择排序的时间复杂度为O(n^2),其中n是元素的数量。虽然选择排序简单,但在大规模数据上效率较低。

         代码实现:

#include <stdio.h>//交换数组中的两个元素
void swap(int *a, int *b) {int temp = *a;*a = *b;*b = temp;
}//选择排序
void selectSort(int arr[], int size) {for (int i = 0; i < size - 1; i++) {int minIndex = i; // 假设当前位置为最小值的索引// 在未排序部分中查找最小值的索引for (int j = i+1; j < size; j++) {if (arr[j] < arr[minIndex]) {minIndex = j; // 更新最小值的索引}}// 如果最小值的索引不等于当前位置,进行交换if (minIndex != i) {swap(&arr[i], &arr[minIndex]); // 将最小值交换到当前位置}}
}

五、插入排序(Insertion Sort)

         插入排序的核心思想是将未排序的元素逐个插入已排序的部分。它通过在已排序部分找到适当位置来插入元素,从而构建有序序列。插入排序的最好情况时间复杂度为O(n),最坏情况为O(n^2)。

        插入排序的理解可以结和玩扑克牌时顺序放牌。

         代码实现:

// 插入排序
void insert_Sort(int arr[], int size) {for (int i = 1; i < size; i++) {int key = arr[i]; // 当前要插入的元素int j = i - 1;//拿到你当前元素的前一个元素的下标// 向前查找插入位置while (j >= 0 && arr[j] > key) {arr[j + 1] = arr[j]; // 将比 key 大的元素向后移动一个下标j--;//向前移动元素下标,可以理解为此时比key大的元素已经无需再比较,因此就把它的下标减去}//将key插入到正确位置,即key在当前元素之前arr[j + 1] = key;//为什么是加1,因为此时j值为 -1 加上1 为0,即最数组前方}
}

六、插入排序的更高效改进:希尔排序(Shell Sort)

         希尔排序,也叫递减增量排序,是插入排序的一种更高效的改进版本。希尔排序是不稳定的排序算法。

  希尔排序是基于插入排序的以下两点性质而提出改进方法的:

  • 插入排序在对几乎已经排好序的数据操作时,效率高,即可以达到线性排序的效率
  • 但插入排序一般来说是低效的,因为插入排序每次只能将数据移动一位

  希尔排序通过将比较的全部元素分为几个区域来提升插入排序的性能。这样可以让一个元素可以一次性地朝最终位置前进一大步。然后算法再取越来越小的步长进行排序,算法的最后一步就是普通的插入排序,但是到了这步,需排序的数据几乎是已排好的了(此时插入排序较快)。

  假设有一个很小的数据在一个已按升序排好序的数组的末端。如果用复杂度为O(n^2)的排序(冒泡排序或直接插入排序),可能会进行n次的比较和交换才能将该数据移至正确位置。而希尔排序会用较大的步长移动数据,所以小数据只需进行少数比较和交换即可到正确位置。

        代码实现:

void ShellSort(int arr[], int n) {int h = 0;// 生成初始增量序列while (h <= n) {h = 3 * h + 1;}// 开始希尔排序while (h >= 1) {// 对每个增量间隔进行插入排序for (int i = h; i < n; i++) {int j = i - h;int get = arr[i];// 插入排序部分while (j >= 0 && arr[j] > get) {arr[j + h] = arr[j];j = j - h;}arr[j + h] = get; // 将当前元素插入正确的位置}h = (h - 1) / 3; // 递减增量序列}
}

七、快速排序(Quick Sort)

         快速排序是一种高效的分治排序算法。它选择一个元素作为“基准”,将数组分成两部分,一部分小于基准,一部分大于基准。然后递归地对两部分进行排序。快速排序的平均时间复杂度为O(n log n),但在最坏情况下可能达到O(n^2)。它通常比其他简单的排序算法更快,并且在实践中效果良好。

        代码实现:

void QuickSort(int arr[], int left, int right) {// 边界条件:如果左边界大于等于右边界,直接返回if (left >= right) {return;}// 选取基准值int i = left, j = right;int pivot = arr[right]; // 以右边值作为基准while (i < j) {// 从左往右找第一个大于等于基准的元素while (i < j && arr[i] < pivot) {i++;}if (i < j) {arr[j] = arr[i]; // 将找到的大于等于基准的元素移到右边j--;}// 从右往左找第一个小于等于基准的元素while (i < j && arr[j] > pivot) {j--;}if (i < j) {arr[i] = arr[j]; // 将找到的小于等于基准的元素移到左边i++;}}arr[j] = pivot; // 基准值归位// 对基准值左边的子数组进行快速排序QuickSort(arr, left, i - 1);// 对基准值右边的子数组进行快速排序QuickSort(arr, i + 1, right);
}

        更多C语言Linux系统ARM板实战数据结构相关文章,关注专栏:

   手撕C语言

            玩转linux

                    脚踢数据结构

                            6818(ARM)开发板实战

📢写在最后

  • 今天的分享就到这啦~
  • 觉得博主写的还不错的烦劳 一键三连喔~
  • 🎉感谢关注🎉

这篇关于【脚踢数据结构】七大排序算法(详细版)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/418849

相关文章

不懂推荐算法也能设计推荐系统

本文以商业化应用推荐为例,告诉我们不懂推荐算法的产品,也能从产品侧出发, 设计出一款不错的推荐系统。 相信很多新手产品,看到算法二字,多是懵圈的。 什么排序算法、最短路径等都是相对传统的算法(注:传统是指科班出身的产品都会接触过)。但对于推荐算法,多数产品对着网上搜到的资源,都会无从下手。特别当某些推荐算法 和 “AI”扯上关系后,更是加大了理解的难度。 但,不了解推荐算法,就无法做推荐系

康拓展开(hash算法中会用到)

康拓展开是一个全排列到一个自然数的双射(也就是某个全排列与某个自然数一一对应) 公式: X=a[n]*(n-1)!+a[n-1]*(n-2)!+...+a[i]*(i-1)!+...+a[1]*0! 其中,a[i]为整数,并且0<=a[i]<i,1<=i<=n。(a[i]在不同应用中的含义不同); 典型应用: 计算当前排列在所有由小到大全排列中的顺序,也就是说求当前排列是第

csu 1446 Problem J Modified LCS (扩展欧几里得算法的简单应用)

这是一道扩展欧几里得算法的简单应用题,这题是在湖南多校训练赛中队友ac的一道题,在比赛之后请教了队友,然后自己把它a掉 这也是自己独自做扩展欧几里得算法的题目 题意:把题意转变下就变成了:求d1*x - d2*y = f2 - f1的解,很明显用exgcd来解 下面介绍一下exgcd的一些知识点:求ax + by = c的解 一、首先求ax + by = gcd(a,b)的解 这个

综合安防管理平台LntonAIServer视频监控汇聚抖动检测算法优势

LntonAIServer视频质量诊断功能中的抖动检测是一个专门针对视频稳定性进行分析的功能。抖动通常是指视频帧之间的不必要运动,这种运动可能是由于摄像机的移动、传输中的错误或编解码问题导致的。抖动检测对于确保视频内容的平滑性和观看体验至关重要。 优势 1. 提高图像质量 - 清晰度提升:减少抖动,提高图像的清晰度和细节表现力,使得监控画面更加真实可信。 - 细节增强:在低光条件下,抖

【数据结构】——原来排序算法搞懂这些就行,轻松拿捏

前言:快速排序的实现最重要的是找基准值,下面让我们来了解如何实现找基准值 基准值的注释:在快排的过程中,每一次我们要取一个元素作为枢纽值,以这个数字来将序列划分为两部分。 在此我们采用三数取中法,也就是取左端、中间、右端三个数,然后进行排序,将中间数作为枢纽值。 快速排序实现主框架: //快速排序 void QuickSort(int* arr, int left, int rig

usaco 1.3 Mixing Milk (结构体排序 qsort) and hdu 2020(sort)

到了这题学会了结构体排序 于是回去修改了 1.2 milking cows 的算法~ 结构体排序核心: 1.结构体定义 struct Milk{int price;int milks;}milk[5000]; 2.自定义的比较函数,若返回值为正,qsort 函数判定a>b ;为负,a<b;为0,a==b; int milkcmp(const void *va,c

poj 3974 and hdu 3068 最长回文串的O(n)解法(Manacher算法)

求一段字符串中的最长回文串。 因为数据量比较大,用原来的O(n^2)会爆。 小白上的O(n^2)解法代码:TLE啦~ #include<stdio.h>#include<string.h>const int Maxn = 1000000;char s[Maxn];int main(){char e[] = {"END"};while(scanf("%s", s) != EO

秋招最新大模型算法面试,熬夜都要肝完它

💥大家在面试大模型LLM这个板块的时候,不知道面试完会不会复盘、总结,做笔记的习惯,这份大模型算法岗面试八股笔记也帮助不少人拿到过offer ✨对于面试大模型算法工程师会有一定的帮助,都附有完整答案,熬夜也要看完,祝大家一臂之力 这份《大模型算法工程师面试题》已经上传CSDN,还有完整版的大模型 AI 学习资料,朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费

hdu 1285(拓扑排序)

题意: 给各个队间的胜负关系,让排名次,名词相同按从小到大排。 解析: 拓扑排序是应用于有向无回路图(Direct Acyclic Graph,简称DAG)上的一种排序方式,对一个有向无回路图进行拓扑排序后,所有的顶点形成一个序列,对所有边(u,v),满足u 在v 的前面。该序列说明了顶点表示的事件或状态发生的整体顺序。比较经典的是在工程活动上,某些工程完成后,另一些工程才能继续,此时

6.1.数据结构-c/c++堆详解下篇(堆排序,TopK问题)

上篇:6.1.数据结构-c/c++模拟实现堆上篇(向下,上调整算法,建堆,增删数据)-CSDN博客 本章重点 1.使用堆来完成堆排序 2.使用堆解决TopK问题 目录 一.堆排序 1.1 思路 1.2 代码 1.3 简单测试 二.TopK问题 2.1 思路(求最小): 2.2 C语言代码(手写堆) 2.3 C++代码(使用优先级队列 priority_queue)