- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章C语言实现各种排序算法实例代码(选择,冒泡,插入,归并,希尔,快排,堆排序,计数)由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
平时用惯了高级语言高级工具高级算法,难免对一些基础算法感到生疏。但最基础的排序算法中实则蕴含着相当丰富的优化思维,熟练运用可起到举一反三之功效.
。
选择排序几乎是最无脑的一种排序算法,通过遍历一次数组,选出其中最大(小)的值放在新数组的第一位;再从数组中剩下的数里选出最大(小)的,放到第二位,依次类推.
算法步骤 。
设数组有n个元素,{ a 0 , a 1 , … , a n } 。
由于每次遍历需要计算O ( n ) 次,且需要便利n 次,故时间复杂度为O ( n 2 ));由于只在交换的过程中需要额外的数据,所以空间复杂度为O ( n ) .
C语言实现 。
//sort.cvoid SelectionSort(double *p, int n);int main(){ double test[5] = {3,2,5,7,9}; SelectionSort(test,5); for (int i = 0; i < 5; i++) printf("%f\n", test[i]); return 0;}//交换数组中i,j处的值void mySwap(double *arr, int i, int j){ double temp = arr[i]; arr[i] = arr[j]; arr[j] = temp;}//选择排序void SelectionSort(double *arr, int n){ int pMax; double temp; for (int i = 0; i < n-1; i++){ pMax = i; for (int j = i+1; j < n; j++) if (arr[j]>arr[pMax]) pMax = j; mySwap(arr,pMax,i); }}
验证 。
>gcc sort.c >a 9.000000 7.000000 5.000000 3.000000 2.000000 。
。
冒泡排序也是一种无脑的排序方法,通过重复走访要排序的数组,比较相邻的两个元素,如果顺序不符合要求则交换两个数的位置,直到不需要交换为止.
算法步骤 。
设数组有n个元素,{ a 0 , a 1 , … , a n } 。
由于每次遍历需要计算O ( n ) 次,且需要遍历n次,故时间复杂度为O ( n 2 ) ,空间复杂度为O ( n ) .
C语言实现 。
//冒泡排序void BubbleSort(double *arr, int n){ n = n-1; double temp; for (int i = 0; i < n; i++) for (int j = 0; j < n-i; j++) if (arr[j]>arr[j+1]) mySwap(arr,i,j); /*前文定义的函数*/}
。
插入排序是算法导论中的第一个算法,说明已经不那么无脑了。其基本思路是将数组划分位前后两个部分,前面是有序数组,后面是无序数组。逐个扫描无序数组,将每次扫描的数插入到有序数组中。这样有序数组会越来越长,无序数组越来越短,直到整个数组都是有序的.
算法步骤 。
设数组有n个元素,{ a 0 , a 1 , … , a n } 。
可见,插入排序的每次插入都有O ( n )的复杂度,而需要遍历n nn次,所以时间复杂度仍为O ( n 2 ) .
C语言实现 。
//插入排序void InsertionSort(double *arr, int n){ double temp; int j; for (int i = 1; i < n; i++){ j = i-1; temp = arr[i]; while (temp<arr[j] && j>=0){ arr[j+1] = arr[j]; //第j个元素后移 j--; } arr[j+1] = temp; }}
。
归并排序是算法导论中介绍分治概念时提到的一种排序算法,其基本思路为将数组拆分成子数组,然后令子数组有序,再令数组之间有序,则可以使整个数组有序.
算法步骤 。
设数组有n个元素,{ a 0 , a 1 , … , a n } 。
可以发现,对于长度为n nn的数组,需要log 2 n次的拆分,每个拆分层级都有O ( n ) 的时间复杂度和O ( n ) 的空间复杂度,所以其时间复杂度和空间复杂度分别为O ( n log 2 n ) 和O(n) 。
C语言实现 。
首先考虑两个有序数组的合并问题 。
//sort.cvoid myMerge(double *arr, int nL, int nR);int main(){ int n = 6; double arr[6] = {2,4,5,1,3,7}; Merge(arr,3,3); for (int i = 0; i < n; i++) printf("%f\n", arr[i]); return 0;}//两个有序数组的混合,arr为输入数组//nL、nR分别为左数组和右数组的长度void Merge(double *arr, int nL, int nR){ nL = nL-1; //左侧最后一个元素的索引 int sInsert = 0; //左侧待插入起始值 double temp; for (int i = 1; i <= nR; i++) { while (arr[nL+i]>arr[sInsert]) sInsert++; if (sInsert<nL+i){ temp = arr[nL+i]; for (int j = nL+i; j > sInsert; j--) arr[j]=arr[j-1]; arr[sInsert] = temp; } else break; //如果sInsert==nL+i,说明右侧序列无需插入 }}
验证 。
> gcc sort.c > a 1.000000 2.000000 3.000000 4.000000 5.000000 7.000000 。
然后考虑归并排序的递归过程 。
void MergeSort(double *arr, int n);void myMerge(double *arr, int nL, int nR);int main(){ int n = 6; double arr[6] = {5,2,4,7,1,3}; MergeSort(arr,n); for (int i = 0; i < n; i++) printf("%f\n", arr[i]); return 0;}void MergeSort(double *arr, int n){ if (n>2) { int nL = n/2; int nR = n-nL; MergeSort(arr,nL); MergeSort(arr+nL,nR); Merge(arr,nL,nR); } else if(n==2) Merge(arr,1,1); //当n==1时说明数组中只剩下一个元素,所以什么也不用做}
验证 。
> gcc sort.c > a 1.000000 2.000000 3.000000 4.000000 5.000000 7.000000 。
至此,排序算法终于有一点算法的味道了.
。
据说是第一个突破O ( n 2 ) 的排序算法,又称为缩小增量排序,本质上也是一种分治方案.
在归并排序中,先将长度为n的数组划分为长度为nL和nR的两个数组,然后继续划分,直到每个数组的长度不大于2,再对每个不大于2的数组进行排序。这样,每个子数组内部有序而整体无序,然后将有序的数组进行回溯重组,直到重新变成长度为n的数组为止.
希尔排序的划分策略则不然,这里在将数组划分为nL和nR之后,对nR和nR进行按位排序,使得nL和nR内部无序,但整体有序。然后再将数组进行细分,当数组长度变成1的时候,内部也就谈不上无序了,而所有长度为1的数组整体有序,也就是说有这些子数组所组成的数组是有序的.
算法步骤 。
设数组有n nn个元素,{ a 0 , a 1 , … , a n } 。
C语言实现 。
//希尔排序void ShellSort(double *arr, int n){ double temp; int j; for (int nSub = n/2; nSub >0; nSub/=2) //nSub为子数组长度 for (int i = nSub; i < n; i++) { temp = arr[i]; for (j = i-nSub; j >= 0&& temp<arr[j]; j -= nSub) arr[j+nSub] = arr[j]; arr[j+nSub] = temp; }}
。
快速排序的分治策略与希尔排序类似,其核心思想都是从组内无序而组间有序出发,当子数组长度缩减至1的时候,则整个数组便是有序的.
算法步骤 。
设数组有n nn个元素,{ a 0 , a 1 , … , a n } 。
由于快速排序的过程中有一个随机选择,所以其时间复杂度可能会受到这个随机选择的影响,如果运气不好的话,快速排序可能会变成冒泡排序。当然,一般来说运气不会那么差,快速排序的平均时间复杂度为O ( n log 2 n ) .
C语言实现 。
//快速排序void QuickSort(double *arr, int n){ double pivot = arr[0]; //选取第0个点为基准 int i = 1; int j = n-1; while (i<j){ if (arr[i]<pivot) i++; else{ mySwap(arr,i,j); j--; } } if (arr[i]>pivot) i--; mySwap(arr,i,0); if (i>1) QuickSort(arr,i); //对i前的数组进行快排 i++; if (i<n-1) QuickSort(arr+i,n-i);//对i+1后的数组进行快排}
。
堆是算法导论中介绍的第一种数据结构,本质是一种二叉树,最大堆要求子节点的值不大于父节点,最小堆反之。由于堆是一种带有节点的数据结构,所以结构表示更加直观。好在二叉树父子节点的序号之间存在简单的递推关系,所以在C语言中可以用数组表示堆, 。
根据上图可知,若父节点的序号为n nn,则左子节点序号为2 n + 1 ,右子节点序号为2 n + 2 .
可以将上图理解为一个排好序的最小堆,如果1位变成15,那么此时这个节点将违反最小堆的原则,经过比对,应该调换15和3的位置。调换之后,15仍然比7和8大,则再调换15和7的位置,则这个最小堆变为 。
从而继续满足最小堆的性质,最大堆亦然,其C语言实现为 。
//如果堆中节点号为m的点不满足要求,则调整这个点//此为最大堆void adjustHeap(double *arr, int m, int n){ int left = m*2+1; //左节点 while (left<n) { if (left+1<n) //判断右节点 if (arr[left]<arr[left+1]) left++; //当右节点大于左节点时,left表示右节点 if (arr[m]>arr[left]) break; //如果父节点大于子节点,则说明复合最大堆 else{ mySwap(arr,m,left); m = left; left = m*2+1; } }}
堆的调整过程就是父节点与其左右两个子节点比较的过程,也就是说通过这种方式得到的堆能够满足父子节点的大小关系,但两个孙节点之间并不会被排序。但是,如果一个数组已经满足最大堆要求,那么只需让所有的节点都在根节点处重新参与排序,那么最终得到的最大堆一定满足任意节点间的有序关系.
//堆排序void HeapSort(double *arr, int n){ for (int i = n/2; i >= 0; i--) adjustHeap(arr,i,n); //初始化堆 for (int i = n-1; i > 0 ; i--){ mySwap(arr,0,i); //将待排序元素放到最顶端 adjustHeap(arr,0,i); //调整最顶端的值 } }
。
此前所有的排序算法均没有考虑到数组的内在分布,如果我们输入的数据为某个区间内的整数,那么我们只需建立这个区间内的整数索引,然后将每个数归类到这个索引之中即可.
这便是桶排序的思路,所谓桶排序即通过将已知数据划分为有序的几个部分,放入不同的桶中,这个分部过程即桶排序。除了计数排序,基数排序是一种更广泛的桶排序形式,其划分方式可以为数据的位数,把这个桶定义为数据最高位的值.
例如,我们有一组均匀分布在[ 0 , 100 ] 之内的数据,所谓基数排序,即先划分出十个不同的桶[ 0 , 10 ) , [ 10 , 20 ) , … , [ 90 , 100 ) ,然后再对每个桶进行单独的排序。这样划分下去,那么基数排序的复杂度则为O ( 10 ∗ n ) .
词典中的排序方式也可以理解为一种基数排序,即首先看第一个字母的顺序,然后第二个,依次类推。由于桶排序对数据做了假设,所以其最优时间复杂度可以达到O ( n + k ),k为桶的数目.
例如,我们有一个由一百个由1和2组成的数组,那么我们只需建立一个索引1 : n 1 , 2 : n 2 ,然后统计1和2分别出现的个数即可,其时间复杂度也将变成O ( n ) .
在这里只做出最简单的计数排序.
/计数排序,输入数组为整数void CountingSort(int *arr,int n){ int aMax = arr[0]; int aMin = arr[0]; for (int i = 0; i < n; i++) //查找最大值和最小值 if (arr[i]>aMax) aMax = arr[i]; else if (arr[i]<aMin) aMin = arr[i]; int m = aMax-aMin+1; //索引长度 int *arrSort = (int*)malloc(sizeof(int)*m); for (int i = 0; i < m; i++) arrSort[i]=0; //初始化索引 for (int i = 0; i < n; i++) //排序 arrSort[arr[i]-aMin] += 1; n = 0; for (int i = 0; i < m; i++) { aMax = i+aMin; //此值为真实数据 for (int j = n; j < n+arrSort[i]; j++) arrSort[j] = i+aMin; n += arrSort[i]; }}
。
到此这篇关于C语言实现各种排序算法(选择,冒泡,插入,归并,希尔,快排,堆排序,计数)的文章就介绍到这了,更多相关C语言实现各种排序算法内容请搜索我以前的文章或继续浏览下面的相关文章希望大家以后多多支持我! 。
原文链接:https://blog.csdn.net/m0_37816922/article/details/103439486 。
最后此篇关于C语言实现各种排序算法实例代码(选择,冒泡,插入,归并,希尔,快排,堆排序,计数)的文章就讲到这里了,如果你想了解更多关于C语言实现各种排序算法实例代码(选择,冒泡,插入,归并,希尔,快排,堆排序,计数)的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
滑动窗口限流 滑动窗口限流是一种常用的限流算法,通过维护一个固定大小的窗口,在单位时间内允许通过的请求次数不超过设定的阈值。具体来说,滑动窗口限流算法通常包括以下几个步骤: 初始化:设置窗口
表达式求值:一个只有+,-,*,/的表达式,没有括号 一种神奇的做法:使用数组存储数字和运算符,先把优先级别高的乘法和除法计算出来,再计算加法和减法 int GetVal(string s){
【算法】前缀和 题目 先来看一道题目:(前缀和模板题) 已知一个数组A[],现在想要求出其中一些数字的和。 输入格式: 先是整数N,M,表示一共有N个数字,有M组询问 接下来有N个数,表示A[1]..
1.前序遍历 根-左-右的顺序遍历,可以使用递归 void preOrder(Node *u){ if(u==NULL)return; printf("%d ",u->val);
先看题目 物品不能分隔,必须全部取走或者留下,因此称为01背包 (只有不取和取两种状态) 看第一个样例 我们需要把4个物品装入一个容量为10的背包 我们可以简化问题,从小到大入手分析 weightva
我最近在一次采访中遇到了这个问题: 给出以下矩阵: [[ R R R R R R], [ R B B B R R], [ B R R R B B], [ R B R R R R]] 找出是否有任
我正在尝试通过 C++ 算法从我的 outlook 帐户发送一封电子邮件,该帐户已经打开并记录,但真的不知道从哪里开始(对于 outlook-c++ 集成),谷歌也没有帮我这么多。任何提示将不胜感激。
我发现自己像这样编写了一个手工制作的 while 循环: std::list foo; // In my case, map, but list is simpler auto currentPoin
我有用于检测正方形的 opencv 代码。现在我想在检测正方形后,代码运行另一个命令。 代码如下: #include "cv.h" #include "cxcore.h" #include "high
我正在尝试模拟一个 matlab 函数“imfill”来填充二进制图像(1 和 0 的二维矩阵)。 我想在矩阵中指定一个起点,并像 imfill 的 4 连接版本那样进行洪水填充。 这是否已经存在于
我正在阅读 Robert Sedgewick 的《C++ 算法》。 Basic recurrences section it was mentioned as 这种循环出现在循环输入以消除一个项目的递
我正在思考如何在我的日历中生成代表任务的数据结构(仅供我个人使用)。我有来自 DBMS 的按日期排序的任务记录,如下所示: 买牛奶(18.1.2013) 任务日期 (2013-01-15) 任务标签(
输入一个未排序的整数数组A[1..n]只有 O(d) :(d int) 计算每个元素在单次迭代中出现在列表中的次数。 map 是balanced Binary Search Tree基于确保 O(nl
我遇到了一个问题,但我仍然不知道如何解决。我想出了如何用蛮力的方式来做到这一点,但是当有成千上万的元素时它就不起作用了。 Problem: Say you are given the followin
我有一个列表列表。 L1= [[...][...][.......].......]如果我在展平列表后获取所有元素并从中提取唯一值,那么我会得到一个列表 L2。我有另一个列表 L3,它是 L2 的某个
我们得到二维矩阵数组(假设长度为 i 和宽度为 j)和整数 k我们必须找到包含这个或更大总和的最小矩形的大小F.e k=7 4 1 1 1 1 1 4 4 Anwser是2,因为4+4=8 >= 7,
我实行 3 类倒制,每周换类。顺序为早类 (m)、晚类 (n) 和下午类 (a)。我固定的订单,即它永远不会改变,即使那个星期不工作也是如此。 我创建了一个函数来获取 ISO 周数。当我给它一个日期时
假设我们有一个输入,它是一个元素列表: {a, b, c, d, e, f} 还有不同的集合,可能包含这些元素的任意组合,也可能包含不在输入列表中的其他元素: A:{e,f} B:{d,f,a} C:
我有一个子集算法,可以找到给定集合的所有子集。原始集合的问题在于它是一个不断增长的集合,如果向其中添加元素,我需要再次重新计算它的子集。 有没有一种方法可以优化子集算法,该算法可以从最后一个计算点重新
我有一个包含 100 万个符号及其预期频率的表格。 我想通过为每个符号分配一个唯一(且前缀唯一)的可变长度位串来压缩这些符号的序列,然后将它们连接在一起以表示序列。 我想分配这些位串,以使编码序列的预
我是一名优秀的程序员,十分优秀!