八大排序算法(C语言)
目录
一.插入排序
1.1直接插入排序
1.2希尔排序
二.选择排序
2.1选择排序
2.2堆排序
三.交换排序
3.1冒泡排序
3.2快速排序
四.归并排序
4.1归并排序
五.非比较排序
5.1计数排序
一.插入排序
基本思想:(默认升序)从数组中的第二个元素开始,先使用一个变量保存这个元素的下标,开然后始跟前一个比较,如果比他小则交换位置继续跟前一个比较,直到找到一个位置比它的前一个元素大则停止(或走到数组开头也停止),然后保存的下标后移继续之前的操作,直到数组末尾.
1.1直接插入排序
基本思想:直接插入排序则是对插入排序思想的直接实现.
时间复杂度:O(N^2)
空间复杂度:O(1)
稳定性:稳定
适用情况:数组越接近有序使用直接插入排序效率越高.
运行图解:
代码:(可直接复制测试)
#include<stdio.h>
#include<stdlib.h>void InsertSort(int* a,int n){//算法实现int i,insert,temp;for(i=1;i<n;i++){//从第二个元素开始向前比较insert=i-1;//元素要插入的位置temp=a[i];//记录当前元素的数值
//开始向前比较,插入位置没到数组头部或insert位置的元素大于要插入元素,一直循环.while(insert>=0 && temp<a[insert]){a[insert+1]=a[insert];//比插入元素大的直接后移insert--;//insert向前}a[insert+1]=temp;//找到比要插入元素小的元素的位置,直接放到其后面.}
}void PrintList(int* a,int n){//打印数组int i;for(i=0;i<n;i++){printf("%d",a[i]);}printf("\n");
}int main(){//测试int a[]={3,1,6,2,7,9,8,5,4};InsertSort(a,sizeof(a)/sizeof(a[0]));PrintList(a,sizeof(a)/sizeof(a[0]));return 0;
}
1.2希尔排序
基本思想:对直接插入排序的优化,因为上面我们知道了,直接插入排序在数组接近有序的情况下排序效率越高,所以我们就想办法让数组接近有序,因此我们可以给个间隔,让所有以间隔为一组的数据全部进行直接插入排序,间隔缩小继续进行上面的操作,当间隔为1的时候这时数组已经是接近有序的数组,再次使用直接插入排序效率就会有明显提高.
时间复杂度:O(N^1.25)~O(1.6*N^1.25)之间
空间复杂度:O(1)
稳定性:不稳定
gap(间隔):我们这里的取间隔方式有很多种我在代码中采用gap=n/2,n为数组长度,使gap每次缩小一半直到为1.
适用情况:数组越接近有序效率越高.
运行图解:
代码:(可直接复制测试)
#include<stdio.h>
#include<stdlib.h>void ShellSort(int* a,int n){//算法实现int i,gap=n/2;while(gap>0){//使用gap让数组逐渐接近有序i=0;//类似直接插入排序(将直接插入排序中的1换为gap)while(i+gap<n){int insert=i;//元素插入位置int temp=a[i+gap];//记录要插入元素的值
//开始向前比较,插入位置没到数组头部或insert位置的元素大于要插入元素,一直循环.while(insert>=0 && temp<a[insert]){a[insert+gap]=a[insert];//插入位置元素大于要插入的元素,插入位置的元素后移insert=insert-gap;//插入位置前移}a[insert+gap]=temp;//插入i++;}gap=gap/2;//间隔每次缩小一半}
}void PrintArray(int* a,int n){//打印数组int i;for(i=0;i<n;i++){printf("%d",a[i]);}printf("\n");
}int main(){//测试int a[]={3,4,2,6,5,9,8,7,1};ShellSort(a,sizeof(a)/sizeof(a[0]));PrintArray(a,sizeof(a)/sizeof(a[0]));return 0;
}
二.选择排序
基本思想:(默认升序),从数组所有元素中找出最大的元素放在数组尾,然后再找出第二大的元素放在数组倒数第二的位置,依次类推当进行到最后一个元素时数组刚好是呈升序的有序数组.
2.1选择排序(优化)
基本思想:既然我们每次遍历数组都是找到最大的元素放在数组尾,那么我们可以对该算法进行优化,每次遍历数组不仅找到最大的值也找的最小的值,将最大的值放到数组尾,最小的值放到数组首,这样我们每遍历一次数组就排好了两个值,对比之前的算法提高了接近一倍的效率.
时间复杂度:O(N^2)
空间复杂度:O(1)
稳定性:不稳定
运行图解:
代码:(可直接复制测试)
#include<stdio.h>
#include<stdlib.h>void Swap(int* a,int* b){//交换数据int temp=*b;*b=*a;*a=temp;
}/*
void SelectSort(int* a,int n){//选择排序int i,j,max;for(i=0;i<n-1;i++){//查找n-1次(最后剩一个元素时不用比较)max=0;for(j=1;j<n-i;j++){//从第二个元素位置开始向n-i开始遍历寻找最大的值的下标if(a[max]<a[j]){max=j;}}Swap(&a[max],&a[n-1-i]);//把最大的值与最后一个元素交换}
}
*/void SelectSort1(int* a,int n){//选择排序优化(降序)int i,j,max,min,sum=0;for(i=0;i<n-1;i=i+2){//每次排好两个元素进行循环max=sum;min=sum;for(j=sum+1;j<n-sum;j++){//从sum+1到n-sum的遍历,查找最大元素下标和最小元素下标if(a[max]<a[j]){max=j;}if(a[min]>a[j]){min=j;}}Swap(&a[max],&a[sum]);//把最大的元素放到第一位if(sum==min){//防止最小的元素本身就在第一位.Swap(&a[max],&a[n-1-sum]);}else{Swap(&a[min],&a[n-1-sum]);}sum++;}
}void PrintArray(int* a,int n){//打印数组int i;for(i=0;i<n;i++){printf("%d ",a[i]);}printf("\n");
}int main(){//测试int a[]={3,6,5,2,9,6,8,7,1};SelectSort1(a,sizeof(a)/sizeof(a[0]));PrintArray(a,sizeof(a)/sizeof(a[0]));return 0;
}
2.2堆排序
基本思想:首先我们要了解什么是堆,堆分为大根堆和小根堆,小根堆的双亲结点永远小于其子节点,因此我们可以将数据进行堆调整化成堆,此时的根节点就是最小的元素,我们将此节点从堆中删除,(堆的删除方法就是把根节点与最后一个节点交换,堆的长度减一,然后重新进行堆调整),因此我们一直对堆进行删除操作,当堆中只有一个元素时停止,此时数组正好成降序.使用大根堆则刚好相反.
时间复杂度:O(N*logN)
空间复杂度:O(1)
稳定性:不稳定
运行图解:
代码:(可直接复制测试)
#include<stdio.h>
#include<stdlib.h>void Swap(int* a,int* b){//交换数据int temp=*b;*b=*a;*a=temp;
}void AdjustDwon(int* a,int n,int root){//向下调整(堆调整)建立大根堆int parents=root;//双亲结点位置int child=root*2+1;//孩子结点位置while(child<n){//孩子结点位置不能大于数组长度if(child+1<n && a[child]<a[child+1]){//在右孩子存在的情况下,让child处在更大的位置child++;}if(a[parents]<a[child]){//如果双亲结点小于孩子结点就进行交换Swap(&a[parents],&a[child]);parents=child;//交换之后重新设置双亲结点的位置和孩子结点的位置继续进行判断child=parents*2+1;}else{
//如果双亲结点大于孩子结点就直接退出函数(要进行向下调整的条件就是该节点的左右子树已经是堆了)return;}}
}void HeapSort(int* a,int n){//堆排序int i,j;for(i=(n-2)/2;i>=0;i--){//从最后一个双亲结点开始调用向下调整建立堆AdjustDwon(a,n,i);}for(j=n-1;j>0;j--){//使用堆删除进行堆排序Swap(&a[0],&a[j]);//将首尾结点数据交换AdjustDwon(a,j,0);//将堆的长度减一,从根开始进行向下调整,重新建立堆}
}void PrintArray(int* a,int n){//打印数组int i;for(i=0;i<n;i++){printf("%d ",a[i]);}printf("\n");
}int main(){//测试int a[]={4,5,2,3,8,9,6,7,1};HeapSort(a,sizeof(a)/sizeof(a[0]));PrintArray(a,sizeof(a)/sizeof(a[0]));return 0;
}
三.交换排序
基本思想:所谓交换,就是根据序列中两个记录键值的比较结果来对换这两个记录在序列中的位置,交换排序的特点是:将键值较大的记录向序列的尾部移动,键值较小的记录向序列的前部移动。
3.1冒泡排序
基本思想:(默认升序)令i=0,从第i个元素开始和其相邻的i+1元素数值进行比较,如果i的数值大就进行交换,反之则不用交换,然后i+1,再重复上述操作,直到i走到n-1的位置.这样就将最大的元素放在了最后一位,然后重复上面的操作,已经排序好的则不用再比较,直到数组只剩下一个元素时停止此时数组是成升序的有序数组.
时间复杂度:O(N^2)
空间复杂度:O(1)
稳定性:稳定
运行图解:
代码:(可直接复制测试)
#include<stdio.h>
#include<stdlib.h>void Swap(int* a,int* b){//交换数据int temp=*b;*b=*a;*a=temp;
}void BubbleSort(int* a,int n){//冒泡排序int i,j,length=n-1;for (i=0;i<length;i++){//n个元素总共排序n-1个元素int flag=0;
//标志位用来标识一趟循环中是否有数据的交换,如果没有数据的交换说明数组已经为有序数组直接退出函
//数即可for(j=0;j<length-i;j++){//进行一趟循环,每排序好一个元素此循环少进行一次if(a[j] < a[j+1]){//(降序)第j个元素与第j+1个元素比较如果比其小就进行交换,并将flag变1Swap(&a[j],&a[j+1]);flag=1;}}if(0==flag){//没有进行数据交换则说明数组有序,直接退出函数break;}}
}void PrintArray(int* a,int n){//打印数组int i;for(i=0;i<n;i++){printf("%d ",a[i]);}printf("\n");
}int main(){//测试int a[]={4,3,2,7,9,8,6,5,1};BubbleSort(a,sizeof(a)/sizeof(a[0]));PrintArray(a,sizeof(a)/sizeof(a[0]));return 0;
}
3.2快速排序
基本思想:(默认升序)从数组中取一个数的来作为标准数,然后遍历数组,将比这个标准数大的放到其后面,比他小的放到其前面,此时这个标准数所在的位置就是他在有序数组中的位置,然后使用递归对其左右的数据也进行这种操作.直到左元素下标大于等于右元素下标时停止,此时数组是成升序的有序数组.
对快速排序的扩展和优化:
a.优化
三数取中:我们取标准数的时候,取的数越居中则算法的时间复杂度越低越接近O(N*logN)效率越高,我们取的数越极端越接近最大或最小值则时间复杂度越高越接近O(N^2)效率越低,因此我们使用三数取中法取得的标准数更居中,具体操作是从数组的首尾中三个位置取三个数,然后以这三个数中居中的数为标准数.
b.扩展(快排的三种方法)
hoare法:先选取标准数,然后定义两个指针(prev(前),rear(后)),一个从前向后走,一个从后向前走,使用prev找到比标准数大的数,使用rear找到比标准数小的数,然后交换两个数,直到prev和rear走到相同位置时退出循环,再将标准数跟此处进行交换即可.
挖坑法:先选取标准数,将标数放到数组尾部然后记录到temp中,然后定义prev(前),rear(后),prev先走找到比标准数大的直接放到数组尾部,然后rear继续走找到比标准数小的直接放到prev的位置,继续重复上面的操作,直到prev和rear走到一起退出循环,将temp中的数放到此处即可.
前后指针法:先选取标准数,将标准数放到数组首部,然后定义前后指针prev在首部,cur=prev+1,两个指针一起向后走,cur找比标准数小的,prev找比标准数大的,都找到后进行交换,当cur走到末尾时退出循环,因为prev所在的位置一直是小于标准数的数中最后一位,所以将标准数与prev所在的位置进行交换,将标准数放在中间即可.
特性:
时间复杂度:O(N*logN)
空间复杂度:O(logN)
稳定性:不稳定
适用情况:数组元素越无序越混乱,该排序效率越高.
运行图解:
hoare:
代码:(可直接复制测试)
#include<stdio.h>
#include<stdlib.h>void Swap(int* a,int* b){int temp=*b;*b=*a;*a=temp;
}//取中间数
int MidNum(int* a,int b,int c,int d){int n_1=a[b],n_2=a[c],n_3=a[d];int temp[3]={n_1,n_2,n_3};if(temp[0]>temp[1]){Swap(&temp[0],&temp[1]);}if(temp[1]>temp[2]){Swap(&temp[1],&temp[2]);}if(temp[0]>temp[1]){Swap(&temp[0],&temp[1]);}if(temp[1]==a[b]){return b;}if(temp[1]==a[c]){return c;}return d;
}//hoare
int PartSort1(int* a,int left,int right){//降序int i=left,j=right,mid=MidNum(a,left,(left+right)/2,right);//三数取中Swap(&a[right],&a[mid]);//将标准数放到末尾int temp=a[right];//记录标准数while(i<j){while(i<j && a[i]>=temp){//找比标准数小的i++;}while(i<j && a[j]<=temp){//找比标准数大的j--;}Swap(&a[i],&a[j]);//交换}Swap(&a[i],&a[right]);//将标准数放到中间return i;
}//挖坑法
int PartSort2(int* a,int left,int right){int i=left,j=right,mid=MidNum(a,left,(left+right)/2,right);//三数取中Swap(&a[mid],&a[right]);//将标准数放到末尾int temp=a[right];//记录标准数while(i<j){while(i<j && a[i]<=temp){//找比标准数大的数i++;}if(i<j){//将其放到j的位置a[j]=a[i];j--;}while(i<j && a[j]>=temp){//找比标准数小的数j--;}if(i<j){//将其放到i的位置a[i]=a[j];i++;}}a[i]=temp;//将标准数的值放到中间return i;
}//前后指针法
int PartSort3(int* a,int left,int right){int cur,prev,mid=MidNum(a,left,(left+right)/2,right);//三数取中Swap(&a[left],&a[mid]);//将标准数放到首部prev=left;//定义前后指针cur=prev+1;while(cur<=right){if(a[cur]<a[left] && ++prev!=cur){//cur找比标准数小的,prev找比标准数大的然后交换Swap(&a[cur],&a[prev]);}cur++;}Swap(&a[left],&a[prev]);//prev所在的位置永远在小于标准数的最后一位,将标准数与其交换放在中间return prev;
}void QuickSort(int* a,int left,int right){//递归调用if(left<right){//当left小于right时一直进行调用int mid=PartSort3(a,left,right);//找到标准数所在的位置QuickSort(a,left,mid-1);//对标准数的左侧进行调用QuickSort(a,mid+1,right);//对标准数的右侧进行调用}
}void PrintArray(int* a,int n){//打印数组int i;for(i=0;i<n;i++){printf("%d ",a[i]);}printf("\n");
}int main(){测试int a[]={3,4,2,7,5,6,9,8,1};QuickSort(a,0,sizeof(a)/sizeof(a[0])-1);PrintArray(a,sizeof(a)/sizeof(a[0]));return 0;
}
四.归并排序
4.1归并排序
基本思想:(默认升序)先将数组中的元素使用递归进行分解当元素分解成单个数据的时候,采用合并有序数组的方式进行合并.(该算法的核心就是合并两个有序数组),定义三个指针分别指向两个有序数组的首部和一个新的数组的首部(定义一个新的数组用来存储排序好的数据),比较两个有序数组的指针的值将小的放到新的数组中小的这个指针后移,新数组的指针也后移,直到两个有序数组中有一个走到末尾结束循环,将没有走到末尾的那个有序数组中剩余的数据全部放到新数组的后面,此时新数组就是一个成升序的有序数组.
时间复杂度:O(N*logN)
空间复杂度:O(N)
稳定性:稳定
适用范围:归并排序的思考更多的是解决在磁盘中的外排序问题.
运行图解:
代码:(可直接复制测试)
#include<stdio.h>
#include<stdlib.h>
#include<string.h>//合并两个有序数组(降序)
void MergeOrderArray(int* new_a,int* a,int begin_1,int end_1,int begin_2,int end_2){int i=begin_1,j=begin_2,k=begin_1;//定义三个指针while(i<=end_1 && j<=end_2){//对两个有序数组进行合并,当其中一个走到末尾时结束循环if(a[i] >= a[j]){//比较两个有序数组中的元素,大的那个数放到新的数组中,同时两个指针后移new_a[k++]=a[i++];}else{new_a[k++]=a[j++];}}while(i<=end_1){//如果是以i遍历的数组没有走到末尾,则将其后面的元素全加到新数组的后面new_a[k++]=a[i++];}while(j<=end_2){//如果是以j遍历的数组没有走到末尾,则将其后面的元素全加到新数组的后面new_a[k++]=a[j++];}
}void _MergeSort(int* new_a,int* a,int i,int j){//递归调用(类似于二叉树的后序遍历)if(i<j){//对数组进行递归分解当i等于j时停止int mid=i+((j-i)>>1);//找中间数值_MergeSort(new_a,a,i,mid);//分解左边_MergeSort(new_a,a,mid+1,j);//分解右边MergeOrderArray(new_a,a,i,mid,mid+1,j);//调用合并算法对有序的数组进行合并memcpy(a+i,new_a+i,sizeof(int)*(j-i+1));//将合并好的有序数组复制回原数组中}
}void MergeSort(int* a,int n){//归并排序的外部调整int* new_a=(int*)malloc(sizeof(int)*n);//创建新数组用来存储有序元素if(NULL==new_a){return;}_MergeSort(new_a,a,0,n-1);//递归调用free(new_a);
}void PrintArray(int* a,int n){//打印数组int i;for(i=0;i<n;i++){printf("%d ",a[i]);}printf("\n");
}int main(){//测试int a[]={3,4,5,2,1,9,8,7,6};MergeSort(a,sizeof(a)/sizeof(a[0]));PrintArray(a,sizeof(a)/sizeof(a[0]));return 0;
}
五.非比较排序
5.1计数排序
基本思想:(默认升序)从一组数据中找出最小和最大的元素,以其差值为界限创建新数组(初始化为0),然后以要排序数组中元素的值为新数组的下标,遍历要排序数组,每遍历到一个数据时在新数组中以该数据为下标的数组内容加一,及统计要排序数组中每个元素出现的次数,最后将这些次数按顺序填入要排序数组中,此时该数组是成升序的有序数组.
时间复杂度:O(max(N,max-min+1))
空间复杂度:O(max-min+1)
稳定性:稳定
使用情况:在元素特别集中时适用.
运行图解:
代码:(可直接复制测试)
#include<stdio.h>
#include<stdlib.h>
#include<string.h>void CountSort(int* a,int n){//计数排序(降序)int max=a[0],min=a[0];int i;for(i=0;i<n;i++){//获取最大值和最小值if(a[i]>max){max=a[i];}if(a[i]<min){min=a[i];}}int length=max-min+1;int* temp=(int*)calloc(length,sizeof(int));//以max-min+1为界限创建新数组for(i=0;i<n;i++){//统计要排序数组中各个元素出现的次数temp[a[i]-min]++;}int k=0;for(i=length-1;i>=0;i--){//将统计好的数据依次按顺序填入要排序的数组while(temp[i]!=0){a[k++]=i+min;temp[i]--;}}free(temp);
}void PrintArray(int* a,int n){//打印数组int i;for(i=0;i<n;i++){printf("%d ",a[i]);}printf("\n");
}int main(){//测试int a[]={2,3,4,5,6,7,8,2,3,3,2,5,4,7,6,9,8,1};CountSort(a,sizeof(a)/sizeof(a[0]));PrintArray(a,sizeof(a)/sizeof(a[0]));return 0;
}
八大排序算法(C语言)相关推荐
- C语言八大排序算法,附动图和详细代码解释!
文章来源:电子工程专辑.C语言与程序设计.竹雨听闲 一.前言 如果说各种编程语言是程序员的招式,那么数据结构和算法就相当于程序员的内功. 想写出精炼.优秀的代码,不通过不断的锤炼,是很难做到的. 二. ...
- 硬核!C语言八大排序算法,附动图和详细代码解释!
来源 :C语言与程序设计.竹雨听闲等 一 前言 如果说各种编程语言是程序员的招式,那么数据结构和算法就相当于程序员的内功. 想写出精炼.优秀的代码,不通过不断的锤炼,是很难做到的. 二 八大排序算法 ...
- 序列划分c语言,一篇“get”C语言八大排序算法
如果说各种编程语言是程序员的招式,那么数据结构和算法就相当于程序员的内功. 想写出精炼.优秀的代码,不通过不断的锤炼,是很难做到的. 二.八大排序算法 排序算法作为数据结构的重要部分,系统地学习一下是 ...
- c语言的八大排序算法,程序员的内功:C语言八大排序算法
四 一.冒泡排序 冒泡排序算法的运作如下: ●比较相邻的元素.如果第一个比第二个大,就交换他们两个. ●对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对.这步做完后,最后的元素会是最大的数. ...
- 八大排序算法——(万字图文详解)
本篇文章是我对之前写过的八个排序算法的总结,感兴趣的小伙伴可以去我的八大排序算法专栏浏览,也可以点击下方标题跳转. 提示:本篇博客篇幅较长,建议小伙伴们查看目录,按需浏览 目录 正文 1 直接插入排序 ...
- 数据结构初阶最终章------>经典八大排序(C语言实现)
前言: 正如标题所言,本篇博客是数据结构初阶的最终章节.但不是数据结构的最终章节!事实上,诸如AVL 树,红黑树这样高阶复杂的数据结构使用C语言非常麻烦,这些数据结构我会放在后续的C++的博客中去 ...
- python 排序算法 简书_Python---简析八大排序算法
前言 1 .排序的概念 排序是计算机内经常进行的一种操作,其目的是将一组"无序"的记录序列调整为"有序"的记录序列. 排序分为内部排序和外部排序. 若整个排序过 ...
- 图解八大排序算法——我见过的最详细的讲解(转)
一.分类 1.内部排序和外部排序 内部排序:待排序记录存放在计算机随机存储器中(说简单点,就是内存)进行的排序过程. 外部排序:待排序记录的数量很大,以致于内存不能一次容纳全部记录,所以在排序过程中需 ...
- 八大排序算法的 Python 实现
八大排序算法的 Python 实现 本文用Python实现了插入排序.希尔排序.冒泡排序.快速排序.直接选择排序.堆排序.归并排序.基数排序. 1.插入排序 描述 插入排序的基本操作就是将一个数据插入 ...
最新文章
- 小分子蛋白Western blot 检测
- sql语句中as的用法和作用
- 学计算机等级考试电脑版软件,计算机二级考试宝典电脑版
- vb 获取mysql表第一行数据_vb如何将远程获取的数据插入本机数据表中
- JAVA中注解controller_SpringMVC之基于注解的Controller
- Serverless 实战 —— Serverless + Egg.js 后台管理系统实战
- Web前端开发大系概览 (前端开发技术栈)
- 《现代操作系统》阅读笔记
- C程序--输出月份英文名(指针数组)
- selenium打不开Ie浏览器的解决办法
- 多媒体数字展示技术解决方案
- Java集合---Collection接口的常用方法
- k8s拉取镜像规则_k8s
- linux ps命令VSZ和RSS内存使用的区别
- 用python画星座_Python 画简易中文星座
- matlab用excel的数据,使用MATLAB对excel文件数据的读写操作
- css(一):基本样式学习
- 分析恶意Windows程序
- 【车载】Google 与菲亚特克莱斯勒联手,推出基于 Android 的车载系统
- 美团基于知识图谱的剧本杀标准化建设与实践