【C语言八大排序思想及代码实现】

article/2025/9/30 14:00:34

文章目录

  • 系列文章目录
  • 前言
  • 一、冒泡排序
  • 二、选择排序
  • 三、直接插入排序
  • 四、希尔排序
  • 五、归并排序
  • 六、基数(桶)排序
  • 七、堆排序
  • 八、快速排序
  • 总结

一、冒泡排序

思想:

从第一个数开始依次向后进行比较(第一个和第二个比较然后第二个和第三个比较……)若前一个数大于后一个数交换两数位置。一直比较到最后第N-1个数和第N个数比较。目的是将这组数中最大的数放到最后,然后对剩下的N-1个数进行相同操作将剩余N-1个数中最大的数放到倒数第二个位置。重复这一操作N-1次。

动图演示:

 代码实现:

void Bubble_Sort(int* arr, int len)//len为数组长度
{for (int i = 1; i < len; i++)//规定总的比较轮数{bool tar = true;//标记一轮比较是否有交换for (int j = 0; j < len - i; j++)//每一轮比较{if (arr[j] > arr[j + 1])//判断前一个数是否大于后一个数若大于交换{int tmp = arr[j];arr[j] = arr[j + 1];arr[j + 1] = tmp;tar = false;}}if (tar)//若一轮比较无交换结束break;}
}

注:该代码为小优化后的代码(加了一个记录标记) 

算法分析:

 时间复杂度:O(n^2)

空间复杂度:O(1)

稳定性:稳定

二、选择排序

思想:

从第一个数据开始找出从这个数开始到最后一个数中最小的数将其与开始的那个数交换,然后从第二个数开始重复上述操作一直到倒数第二个数为止。

动图演示:

 代码实现:

void Choose_Sort(int* arr, int len)//len为数组长度
{int tmp;//交换时的中间变量for (int i = 0; i < len - 1; i++)//规定交换轮数{int min = i;//记录每轮最小值下标for (int j = i + 1; j < len; j++)//每轮对比{if (arr[j] < arr[min])min = j;}//每轮结束后的交换(将最小值交换到开头)tmp = arr[i];arr[i] = arr[min];arr[min] = tmp;}
}

算法分析:

时间复杂度:O(n^2)

空间复杂度:O(1)

稳定性:不稳定 

三、直接插入排序

思想:

从第二个数开始向前寻找第一个不大于它自身的数插入到其后面,然后从第三个数开始向前寻找……直到最后一个数为止。

动图演示:

代码实现:

void Insert_Sort(int* arr, int len)//len为数组长度
{int j;for (int i = 1; i < len ; i++)//规定交换轮数{int tmp = arr[i];//记录每轮要对比的数for (j = i-1; j >=0; j--)//每轮对比{if (arr[j] > tmp)//如果该数大于对比数该数向后挪动一位arr[j + 1] = arr[j];else//找到第一个比它小的数结束break;}//每轮结束后将对照数插入到目标位置arr[j+1] = tmp;}
}

 算法分析:

时间复杂度:O (n^2)

空间复杂度:O(n^2)

稳定性:稳定

四、希尔排序

思想:

对比较有序的数据进行排序,即先将每隔素数个数据划分为一组进行排序(进行多次素数划分(每次素数不同)并排序)使其较为有序最后在较为有序的基础上再对其进行一个一个进行排序。(是简单插入排序的优化)

动图演示:

 代码实现:

void Shell(int* arr, int len, int gap)//单次排序函数,gap为划分的素数间隔
{int j;for (int i = gap; i < len; i++)//总排序轮数{int tmp = arr[i];//每一轮对比数for (j = i - gap; j >= 0; j -= gap)//单轮对比{if (arr[j] > tmp){arr[j + gap] = arr[j];}elsebreak;}arr[j + gap] = tmp;}
}
void Shell_Sort(int* arr, int len)//len为数组长度
{int brr[3] = { 1,3,5 };//要划分数据的素数for (int i = 2; i >= 0; i--)//进行相应划分后数据的排序{Shell(arr, len, brr[i]);}
}

注:该代码为分别用【5,3,1】间隔进行排序的代码。 

算法分析:

时间复杂度:O(n^1.3)~ O(n^2)

空间复杂度:O  ( 1 ) 

稳定性:不稳定 

五、归并排序

思想:

首先将数据一个一个分为N个数组然后俩俩合并相邻数组。重复合并数组操作到最后只剩下一个数组。

图像分析:

 代码实现:

void Merge(int* arr, int len, int gap)//单次数组合并,len为数组长度,gap为划分的数组中元素数量
{int* brr = (int*)malloc(len * sizeof(int));//开辟一个等长数组用来承载数组俩俩合并后的数据int k = 0;int low1 = 0;//合并的俩个数组中第一个数组当前下标int high1 = low1 + gap - 1;//合并的俩个数组中第一个数组末尾下标int low2 = high1 + 1;合并的俩个数组中第二个数组当前下标int high2 = (low2 + gap - 1)>(len-1)?len-1:low2+gap-1;//合并的俩个数组中第二个数组末尾下标while (low2 < len)//当第二个数组不存在时结束{while (low1 <= high1 && low2 <= high2)//当二个数组都有数据时比较{if (arr[low1] > arr[low2])brr[k++] = arr[low2++];elsebrr[k++] = arr[low1++];}while (low1 <= high1)//当第一个数组还有数据时将其全部写入brr中{brr[k++] = arr[low1++];}while (low2 <= high2)//当第二个数组还有数据时将其全部写入brr中{brr[k++] = arr[low2++];}//转到后面的二组数据low1 = high2 + 1;high1 = low1 + gap - 1;low2 = high1 + 1;high2 = (low2 + gap - 1) > (len - 1) ? len - 1 : low2 + gap - 1;}while (low1 < len)//如果还剩下一组数据将其写入brr中{brr[k ++] = arr[low1++];}for (int i = 0; i < len; i++)//将brr数据写入原数组中{arr[i] = brr[i];}
}
void Merge_Sort(int* arr, int len)
{for (int i = 1; i < len; i *= 2)//俩俩合并数组{Merge(arr, len, i);}
}

算法分析: 

时间复杂度:O(n*logn)

空间复杂度:O(n)

稳定性:稳定 

 六、基数(桶)排序

思想:

(假设这组数据中最大数位数为n)将这组数据分别按个位、十位、百位一直到n位排序。

动画演示:

代码实现:

int Get_Max_Num(int* arr, int len)//获取最大值位数
{int max = arr[0];//最大值int num=0;//位数for (int i = 1; i < len; i++)//寻找最大值{if (arr[i] > max)max = arr[i];}while (max != 0)//获取最大值位数{max = max / 10;num++;}return num;
}
void Radix(int* arr, int len, int num)//单次排序,num为该次排列的位数
{int n = pow(10, num-1);//当前位数int** brr=(int**)malloc(10 * sizeof(int*));//动态开辟二维空间记录排列后的结果(较为繁琐用链队列更好)for (int i = 0; i < 10; i++){brr[i] = (int*)malloc(len * sizeof(int));}int Brr[10] = { 0 };//计数器for (int i = 0; i < len; i++)//排列数据{int tmp = arr[i] / n % 10;brr[tmp][Brr[tmp]++] = arr[i];}int k = 0;for (int i = 0; i < 10; i++)//将排列好的数据重新写入数组{for (int j = 0; j < Brr[i]; j++){arr[k++] = brr[i][j];}free(brr[i]);}free(brr);
}
void Radix_Sort(int* arr, int len)
{int num=Get_Max_Num(arr, len);//获取最大值位数for (int i = 1; i <= num; i++)//进行排列{Radix(arr, len, i);}
}

注:该代码片段所展示的为“二维数组加计数”方法构建的基数算法但“链队列方法”构建更简单(C语言中链队列代码过长不方便展示)且该代码只考虑了数据为非负数情况(考虑负数情况代码较长不方便展示)。

算法分析:

时间复杂度:不太好确定(令最大数位数为m时间复杂度为O(m*n))

空间复杂度:O(n)

稳定性:稳定

七、堆排序

 思想:

堆排序借用大(小)顶堆根节点最大(最小)的思想通过一次次构建大(小)顶堆找出最大(最小)值然后将其与所构建大(小)顶堆的最后一个元素互换然后将最后一个元素排除在下一次构建之外重新构建大(小)顶堆循环往复直到所构建大顶堆仅有一个元素为止。

动图演示:

代码实现: 

void Heap_Adjust(int* arr,int start,int end)//单次构建大顶堆函数,start为本次构建大顶堆的根节点,end为本次构建的最后一个叶子
{int tmp=arr[start];//将未排序的根节点赋给tmp以方便下面的构建交换for (int i = start * 2; i <= end; i = i * 2)//构建大顶堆{if (i+1<=end && arr[i] < arr[i + 1])//若寻找到最大的次级分节点i++;if (arr[i] > tmp)//若寻找到最大的次级分节点大于当前节点进行交换{arr[start] = arr[i];start = i;//节点给下移}elsebreak;}arr[start] = tmp;//将根节点放入合适的位置
}
void Heap_Sort(int* arr, int len)
{for (int i = (len - 1) / 2; i >= 0; i--)//像构建一个整体大顶堆{Heap_Adjust(arr, i, len - 1);}for (int i = 0; i < len - 1; i++)//数据处理{//将根节点和最后一个节点交换int tmp = arr[0];arr[0] = arr[len - 1 - i];arr[len - 1 - i] = tmp;Heap_Adjust(arr, 0, len - 2 - i);//重新构建大顶堆}
}

注:该代码构建为大顶堆。 

 算法分析:

时间复杂度:O(n*logn)

空间复杂度:O(1)

稳定性:不稳定 

八、快速排序

思想:

每次以第一个数为基准数进行比较将数据中比基准数小的数全部放到基准数左边比基准数大的数放到其右边,然后分别对基准数左边和右边的数据分别重复上述操作到有一边的数小于二个为止,重复这一操作到所有的分开部分完成。

动图演示:

代码实现:

int  Partition(int* arr, int left,int right)//单次分配函数返回分配后基准值下标,left为待分配数据最左边数据下标,right为待分配数据最右边数据下标
{int tmp = arr[left];//确认基准值while (left < right)//分配过程{while (arr[right] > tmp && right > left)//从右向左找比基准值小的数{right--;}arr[left] = arr[right];//将从右边找到的比基准值小的数交换到左边while (arr[left] <= tmp && left < right )//从左向右找比基准值大的数{left++;}arr[right] = arr[left];//将从左边找到的比基准值大的数交换到右边}arr[left] = tmp;//将基准值放入空位return left;//返回基准值下标
}
void Quick_Sort(int* arr, int left,int right)
{int mid = Partition(arr, left, right);//先进行一次分配//用递归对分配好后左右边的数据分别进行再分配if (left < mid - 1)Quick_Sort(arr, left, mid - 1);if (right > mid + 1)Quick_Sort(arr, mid + 1, right);
}

注:该代码为快速排序的递归写法。 

void Get_Threemid_Num(int* arr, int left,int right)//快排优化(三数取中法)
{int tmp;//交换中间变量int mid = (left + right) / 2;//这组数据中间数据下标if (arr[left] > arr[mid])//将第一个数与中间一个数较大的数放到中间{tmp = arr[left];arr[left] = arr[mid];arr[mid] = tmp;}if (arr[mid] < arr[right])//在上一步的基础上将最大的数放到中间{tmp = arr[mid];arr[mid] = arr[right];arr[right] = tmp;}if (arr[left] < arr[right])//将三个数中第二大的数放到第一个位置,最小的数放到最后一个位置{tmp = arr[left];arr[left] = arr[right];arr[right] = tmp;}
}
int  Partition(int* arr, int left,int right)//单次分配函数返回分配后基准值下标,left为待分配数据最左边数据下标,right为待分配数据最右边数据下标
{Get_Threemid_Num(arr, left, right);int tmp = arr[left];//确认基准值while (left < right)//分配过程{while (arr[right] > tmp && right > left)//从右向左找比基准值小的数{right--;}arr[left] = arr[right];//将从右边找到的比基准值小的数交换到左边while (arr[left] <= tmp && left < right )//从左向右找比基准值大的数{left++;}arr[right] = arr[left];//将从左边找到的比基准值大的数交换到右边}arr[left] = tmp;//将基准值放入空位return left;//返回基准值下标
}
void Quick_Sort(int* arr, int left, int right)
{std::stack<int> str;//开辟一个栈用来放每一次分配好后的左右部分的left,right//先进行一次分配并将数据存入到栈中使栈不空int mid = Partition(arr, left, right);if (left < mid - 1)//将符合条件的左部分的left,rght写入栈中{str.push(left);str.push(mid - 1);}if (right > mid + 1)//将符合条件的右部分的left,rght写入栈中{str.push(mid + 1);str.push(right);}while (!str.empty())//若栈中有数据则继续执行{//每一次从栈中取出一组left,rightright = str.top();str.pop();left = str.top();str.pop();mid = Partition(arr, left, right);//分配if (left < mid - 1)//将符合条件的左部分的left,rght写入栈中{str.push(left);str.push(mid - 1);}if (right > mid + 1)//将符合条件的右部分的left,rght写入栈中{str.push(mid + 1);str.push(right);}}
}

注:该代码为快速排序栈实现的非递归写法并用“三数取中法”进行了优化(因为C语言栈代码过长这里用了C++库函数的栈)

 算法分析:

时间复杂度:最好情况O(n*logn),最坏情况O(n^2)

空间复杂度:O(logn)

稳定性:不稳定 


总结

   这八种基本排序中前三种排序时间复杂度较高,但空间复杂度较低,适合在处理较少数据时使用若对数据的稳定性有要求则应该从冒泡排序和简单插入排序中进行选择。

   后面五种排序较为复杂适合处理较多数据情况,当数据非常乱的情况下适合用快速排序,当数据最高位数较少时适合用基数(桶)排序,当要在较多数据中获取前几个大或小数据时适合用堆排序……

  面对复杂多变的现实情况选择适当的排序方法能够极大简化处理。目前来说快速排序是非常重要的需要着重了解。


http://chatgpt.dhexx.cn/article/mePe6x2U.shtml

相关文章

经典排序算法总结(C实现)

一 排序算法介绍 1.0 排序的概述 在计算机计算和处理加工数据时&#xff0c;经常会直接或间接地涉及到数据的排序问题。可以简单地将排序操作理解为&#xff1a;将一个按值无序的数据序列转换成为一个按值有序的数据序列的过程。例如&#xff0c;将一个无序的数组 A[5] {7, …

主元排序法c语言程序,C/C++实现八大排序算法汇总

概述排序有内部排序和外部排序&#xff0c;内部排序是数据记录在内存中进行排序&#xff0c;而外部排序是因排序的数据很大&#xff0c;一次不能容纳全部的排序记录&#xff0c;在排序过程中需要访问外存。 我们这里说说八大排序就是内部排序。 当n较大&#xff0c;则应采用时间…

内排序算法小结

针对近两天所复盘的《数据结构》中的内排序部分&#xff0c;做一个小总结。尽力在最大程度上把考点重现出来&#xff0c;以便复盘之需。 本文所有算法均使用 C语言 实现。 本博客仅从个人的考点梳理为基&#xff0c;内容相较全局还有很多缺失&#xff0c;读者请权衡参考。 目…

数据结构-考研难点代码突破(C/C++/Java排序算法,性能及其稳定性分析(内部排序))

文章目录 1. 内部排序的基本种类2. 插入排序Ⅰ直接插入排序性能与稳定性分析Ⅱ 折半插入排序性能与稳定性分析Ⅲ 希尔排序性能与稳定性分析 3. 交换排序Ⅰ 冒泡排序性能与稳定性分析Ⅱ 快速排序key值选取&#xff08;三数取中&#xff09;三种交换方法① hoare左右指针② 挖坑法…

排序算法知识点总结和Java实现

排序算法知识点总结和Java实现 前言1. 术语解释2. 排序算法2.1 选择排序2.2 冒泡排序2.3 插入排序2.4 希尔排序2.5 归并排序2.6 快速排序2.7 堆排序2.8 计数排序2.9 桶排序2.10 基数排序 参考材料 前言 文章会有一些从参考材料中转载来的动图&#xff0c;如果构成侵权&#xf…

八大排序算法总结与java实现

原文链接&#xff1a; 八大排序算法总结与java实现 - iTimeTraveler 概述 因为健忘&#xff0c;加上对各种排序算法理解不深刻&#xff0c;过段时间面对排序就蒙了。所以决定对我们常见的这几种排序算法进行统一总结。首先罗列一下常见的十大排序算法&#xff1a; 直接插入排序…

C/C++最全排序算法汇总,原理+代码

1、简介 排序是计算机内经常进行的一种操作&#xff0c;其目的是将一组“无序”的记录序列调整为“有序”的记录序列。分内部排序和外部排序。若整个排序过程不需要访问外存便能完成&#xff0c;则称此类排序问题为内部排序。反之&#xff0c;若参加排序的记录数量很大&#x…

采用回调函数的内部排序算法-插入排序,希尔排序,冒泡,快排,堆排,归并排,基数排序

采用回调函数的内部排序算法-插入排序&#xff0c;希尔排序&#xff0c;冒泡&#xff0c;快排&#xff0c;堆排&#xff0c;归并排&#xff0c;基数排序 1.回调函数(callback function) :简而言之&#xff0c;回调函数就是一个通过函数指针调用的函数。 如果你把函数的指针&…

排序算法总结---C语言

排序算法总结---建议收藏 排序算法概述一&#xff1a;冒泡排序&#xff08;Bubble Sort&#xff09; 1、冒泡排序简要概述 2、冒泡排序图解 3、代码实现 二&#xff1a;选择排序&#xff08;Select Sort&#xff09; 1、选择排序简要概述 2、选择排序图解 3、代码实现 三…

C语言实现基础排序算法

排序算法平均时间复杂度最差时间复杂度空间复杂度数据对象稳定性冒泡排序O( n 2 n^{2} n2)O( n 2 n^{2} n2)O(1)稳定快速排序O(n * l o g 2 n log_{2n} log2n​)O( n 2 n^{2} n2)O( l o g 2 n log_{2n} log2n​)不稳定插入排序O( n 2 n^{2} n2)O( n 2 n^{2} n2)O(1)稳定希尔排…

排序算法-总结(c语言)

排序算法有很多种&#xff0c;但是我看网络上大多数都是十大经典排序算法&#xff0c;分别有&#xff1a; 目录 冒泡排序 1. 算法步骤 2.代码 选择排序 1. 算法步骤 2.代码 插入排序 1. 算法步骤 2.图解&#xff08;源自我主页文章&#xff09; 3.代码 希尔排序 1.…

C语言实现八种排序算法

文章目录 排序算法选择排序冒泡排序插入排序归并排序希尔排序快速排序堆排序计数排序 排序算法 平时用惯了高级语言高级工具高级算法&#xff0c;难免对一些基础算法感到生疏。但最基础的排序算法中实则蕴含着相当丰富的优化思维&#xff0c;熟练运用可起到举一反三之功效。 …

c语言八大排序算法详细版

概述排序有内部排序和外部排序&#xff0c;内部排序是数据记录在内存中进行排序&#xff0c;而外部排序是因排序的数据很大&#xff0c;一次不能容纳全部的排序记录&#xff0c;在排序过程中需要访问外存。 我们这里说说八大排序就是内部排序。 当n较大&#xff0c;则应采用时…

C语言各大排序算法整理及动画演示

&#xff08;一&#xff09;插入排序 插入排序基本思想&#xff1a;从初始的子集合开始&#xff0c;不断地将新的元素插入到已排序好的子集合当中的合适位置。&#xff08;未排序的插入到已排序当中&#xff09;具体分为直接插入排序和希尔排序两种。 ①直接插入排序 void Ins…

浅谈排序算法:冒泡排序法和选择排序法的区别

之前学习了冒泡排序法和选择排序法&#xff0c;最近被老师问某个道题用的是什么排序法。自己居然答不出来&#xff0c;才发现自己没有真正弄懂&#xff0c;这两个算法的原理和区别&#xff0c;所以 1冒泡排序法 1.1什么是冒泡排序法&#xff1f; 顾名思义&#xff0c;冒泡排…

Java语言实现常用的十种排序算法

排序问题一直都是程序工作或面试中的重点&#xff0c;特别是对于排序算法而言&#xff0c;在一些公司的笔试中&#xff0c;手写个冒泡啥的也不足为奇&#xff0c;因此今天抽空整理一下Java语言实现的各类排序算法&#xff0c;互勉学习一下。根据排序的不同方式大概可归为一下五…

排序算法(二)—— 选择法排序算法

1、选择法排序简介 选择法排序算法是一种常用的排序算法&#xff0c;他的实现方法是遍历数组所有元素&#xff0c;找出最小的元素&#xff0c;将它与第一个元素交换&#xff1b;然后遍历剩下的元素&#xff0c;找出最小的元素并与第二个元素交换&#xff1b;接下来再遍历剩下的…

Java编程:排序算法——选择排序

基本介绍 选择式排序也属于内部排序法&#xff0c;是从欲排序的数据中&#xff0c;按指定的规则选出某一元素&#xff0c;再依规定交换位置后达到排序的目的。 选择排序思想: 选择排序&#xff08;select sorting&#xff09;也是一种简单的排序方法。它的基本思想是&#x…

Java常用排序算法/程序员必须掌握的8大排序算法

本文由网络资料整理而来&#xff0c;如有问题&#xff0c;欢迎指正&#xff01; 参考链接&#xff1a;维基百科-排序算法 // 排序原始数据 private static final int[] NUMBERS {49, 38, 65, 97, 76, 13, 27, 78, 34, 12, 64, 5, 4, 62, 99, 98, 54, 56, 17, 18, 23, 34, 15,…

排序算法:选择排序

1. 什么是选择排序&#xff1f;&#xff08;摘抄自百度百科&#xff09; 选择排序&#xff08;Selection sort&#xff09;是一种简单直观的排序算法。 它的工作原理是&#xff1a; 第一次从待排序的数据元素中选出最小&#xff08;或最大&#xff09;的一个元素&#xff0c…