常用的排序算法整理(C语言)
稳定的概念:数值相同的两个元素在排序前后其相对位置是未发生改变则是稳定。稳定是为了保证多次排序结果相同。
一、BubbleSort
void Swap(int *a,int *b)
{
//接下来所有的Swap函数都是这个
int temp=*b;
*b=*a;
*a=temp;
}
void BubbleSort(int arr[],int nLength)
{
int i,j;
for(i=0;i<nLength;i++)
{
for(j=i+1;j<nLength;j++)
{
if(arr[i]>arr[j])
{
Swap(&arr[i],&arr[j]);
}
}
}
}
BubbleSort最好的时间复杂度O(n)
平均的时间复杂度O(n²)
最坏的时间复杂度O(n²)
空间复杂度O(1)
BubbleSort是稳定的(但不是绝对稳定)
二、SelectSort
SelectSort的核心思想是遍历元素找到里面最大或者最小的元素放在相应的位置上
void SelectSort(int arr[],int nLength)
{
int i,j;
int nMin;
for(i=0;i<nLength;i++)
{
nMin=i;
for(j=i+1;j<nLength;j++)
{
//找到了更小的 更新
if(arr[j]<arr[nMin])
{
nMin=j;
}
}
//把最小的放在最前面 i是从0到nLength-1;
if(nMin!=i)
{
Swap(&arr[nMin],&arr[i]);
/*
//交换的另类写法:
arr[nMin]=arr[nMin]^arr[i];
arr[i]=arr[nMin]^arr[i];
arr[nMin]=arr[nMin]^arr[i];
*/
}
}
}
SelectSort最好的时间复杂度O(n²)
平均的时间复杂度O(n²)
最坏的时间复杂度O(n²)
空间复杂度O(1)
SelectSort是不稳定的
三、InsertSort
InsertSort的核心思想:将待排序数据分为两部分,一部分有序,一部分无序,将无序元素依次插入到有序中,完成排序
void InsertSort(int arr[],int nLength)
{
int i,j;
int temp;
for(i=1;i<nLength;i++)
{
//保存 防止移动覆盖的的值
temp=arr[i];
//j=i-1 有序的最后一个位置
//倒序遍历有序数组
for(j=i;j>0&&arr[j-1]>temp;j--)
{
//往后移
arr[j]=arr[j-1];
}
//放入无序元素
arr[j]=temp;
}
}
InsertSort最好的时间复杂度O(n)
平均的时间复杂度O(n²)
最坏的时间复杂度O(n²)
空间复杂度O(1)
InsertSort是稳定的
应用:1.元素较少 2.每个元素排序到前的位置距其最终排序的位置不远的时候
四、ShellSort
ShellSort是一个典型的缩小增量排序,其核心思想是数据分组,各组内经行插入排序
void ShellSort(int arr[],int nLength)
{
int nGap;
int i,j,k,temp;
//间隔 细分组
for(nGap=nLength/2;nGap>=1;nGap/=2)
{
//组
for(i=0;i<nGap;i++)
{
//各组内经行插入排序
for(j=i+nGap;j<nLength;j+=nGap)
{
k=j-nGap;
temp=arr[j];
//倒叙遍历有序数组
while(arr[k]>temp&&k>=i)
{
arr[k+nGap]=arr[k];
k-=nGap;
}
arr[k+nGap]=temp;
}
}
}
}
ShellSort的运行时间依赖于增量序列的选择,无法精确的计算平均时间复杂度与最坏时间复杂度。其最好的时间复杂度是O(n),其空间复杂度是O(1)
ShellSort是不稳定的排序算法。
五、HeapSort
堆(又称优先队列),他的逻辑结构遵循树。通常分为大根堆和小根堆。
堆=数组+完全二叉树的逻辑结构。
大根堆--------->父左右------>父亲max,小根堆---->父亲min(左右无大小关系)
HeapSort的核心:运用大根堆的思想,将最大值与最后经行交换,再次调成,反复经行此操作直到排序完成。
#define Left 2*nRoot+1
#define Right 2*nRoot+2
void Adjust(int arr[],int nLength,int nRoot)
{
/*
调整的步骤:
找到孩子里面最大的;
拿最大的孩子与父亲比较
比父亲大交换,重新调整当前节点
比父亲小结束
*/
int nMax;
//循环结束时,对根经行了换值,此时nMax也应该经行换值
for(nMax=Left;Max<nLength;nMax=Left)
{
if(Right<nLength)
{
//nMax=Left,找到两个孩子里面的较大的值
if(arr[nMax]<arr[Right])
{
nMax=Right;
}
}
//大的和父亲比较,比父亲大交换
if(arr[nRoot]<arr[nMax])
{
Swap(&arr[nRoot],&arr[nMax]);
//替换根
nRoot=nMax;
}
else{
break;
}
}
}
void HeapSort(int arr[],int nLength)
{
int i;
//build Heap
for(i=nLength/2-1;i>=0;i--)
{
Adjust(arr,nLength,i);
}
//Sort
for(i=nLength-1;i>0;i--)
{
Swap(&arr[i],&arr[0]);
Adjust(arr,i,0);
}
}
HeapSort最好的时间复杂度O(n㏒2 n)
平均的时间复杂度O(n㏒2 n)
最坏的时间复杂度O(n㏒2 n)
空间复杂度O(1)
HeapSort是不稳定的
六、QuickSort
QuickSort的核心思想是分治法,也就是找一个标准值,将比其小的元素放在其左侧,比其大的元素放在右侧。
分治法的应用标准:1.随着数据的规模的缩小难度在降低 。2.当前问题得可分。3.解可合并。4.子问题相互独立
/*
步骤:
确定标准值位置
保存标准值
从右向左找到比标准值小的放在左边
从左向右找到比标准值大得放在右边
重复以上过程
标准值放入(左右操作位置相遇)
根据标准值位置将当前数据分割成两部分
两部分分别重复以上操作
*/
void QuickSort(int arr[],int Left,int Right)
{
if(Left>=Right)
return ;
//中间是QSort
int Standard=arr[Left];
int i=Left;
int j=Right
while(i<j)
{
while(i<j&& (Standard<=arr[j]))
{
j--;
}
Swap(&arr[i],&arr[j]);
while(i<j &&(Standard>=arr[i]))
{
i++;
}
Swap(&arr[i],&arr[j]);
}
//
QuickSort(arr,Left,i-1);
QuickSort(arr,i+1,Right);
}
QuickSort最好的时间复杂度O(n㏒2 n)
平均的时间复杂度O(n㏒2 n)
最坏的时间复杂度O(n²)
空间复杂度O(n㏒2 n)–>递归深度
QuickSort是不稳定的
QSort的优化:
void ProQSort(int arr[],int nBegin,int nEnd)
{
int nSmall=nBegin-1;
for(nBegin;nBegin<nEnd;nBegin++)
{
if(arr[nBegin]<arr[nEnd])
{
//小区间扩张
if(++nSmall != nBegin)
{
Swap(&arr[nSmall],arr[nBegin]);
}
}
}
if(++nSamll !=nEnd)
{
Swap(&arr[nSamll],arr[nBegin]);
}
return nSamll;
}
void QuickSort(int arr[],int nBegin,int nEnd)
{
if(arr == NULL || nBegin>=nEnd) return ;
int Standard=ProQSort(arr,nBegin,nEnd);
QuickSort(arr,nBegin,Standard-1);
QuickSort(arr,Standard+1,nEnd);
}
QuickSort的优化:
- 标准值的选取(枢轴的选取)
random()3个
前中后=>中间
选9个中选一个,方法按上
集中—>相等元素 - 数据规模切割(15个一下)到很小的时候,使用InsertSort而不再使用快排递归
- 用循坏取代递归
七、MergeSort
MergeSort的核心思想是将多个有序数组经行合并,Merge算法是经典的分治cel,它将问题分成一些小的问题然后递归求解,而治的阶段将分的阶段得到的各个答案修补到一起。
/*
步骤:
拆分,两路
将两路数组处理成有序
合并
遍历两个有序数组,有一个数组遍历完成,将剩下的另一个数组,直接放在后面
*/
void Merge(int arr[],int nBegin,int nEnd)
{
int nLow1,nHigh1,nLow2,nHigh2;
nLow1=nBegin;
nHigh1=nBegin+(nEnd-nBegin)/2;
nLow2=nHigh1+1;
nHigh2=nEnd;
int *pTemp=NULL;
pTemp=(int *)malloc(sizeof(int )*(nEnd-nBegin+1));
int i=0;
//合并
while(nLow1<=nHigh1 && nLow2<=nHigh2)
{
if (arr[nLow2]<arr[nLow1])
{
pTemp[i]=arr[nLow2];
i++;
nLow2++;
}
else{
pTemp[i]=arr[nLow1];
i++;
nLow1++;
}
}
//将剩余的元素放到后面
while(nLow1<=nHigh1)
{
pTemp[i]=arr[nLow1];
i++;
nLow1++;
}
while(nLow2<=nHigh2)
{
pTemp[i]=arr[nLow2];
i++;
nLow2++;
}
//放回
for (i = 0; i < nEnd-nBegin+1; ++i)
{
arr[nBegin+i]=pTemp[i];
}
//释放
free(pTemp);
pTemp=NULL;
}
void MergeSort(int arr[],int nBegin,int nEnd)
{
if(arr == NULL || nBegin>=nEnd) return;
//拆分
int nMid=nBegin+(nEnd-nBegin)/2;
MergeSort(arr,nBegin,nMid);
MergeSort(arr,nMid+1,nEnd);
//合并
Merge(arr,nBegin,nEnd);
}
MergeSort最好的时间复杂度O(n㏒2 n)
平均的时间复杂度O(n㏒2 n)
最坏的时间复杂度O(n㏒2 n)
空间复杂度O(n)
八、RadixSort
RadixSort (基数排序)是一个典型的空间换时间的算法,其的核心思想是:利用数字特性,将数按位分组处理,LSD(低位优先) MSD(高位优先)。
/*
需要结构体:
数值
指向下一个元素的变量
步骤:
找到最大值,计算最大值位数
按位处理
申请表头,赋初值
元素按位入表(尾添加)
元素放回原数组
*/
typedef struct radix
{
int nValue;
struct radix *pNext;
}Radix;
void RadixSort(int arr[],int nLength)
{
//找最大值
int nMax;
int i;
nMax =arr[0];
for (i = 0; i < nLength; ++i)
{
if (arr[i]>nMax)
{
nMax=arr[i];
}
}
//最大值位数
int nCount=0;
while(nMax)
{
nCount++;
nMax/=10;
}
int nBase;
int nDeal;
int j;
//按位处理
for (i = 1; i <=nCount; ++i)
{
//拆位
nDeal=i;
nBase=1;
while(nDeal>1)
{
nBase*=10;
nDeal--;
}
//按位入组
Radix **pRadix=NULL;
pRadix=(Radix**)malloc(sizeof(Radix*)*10);
memset(pRadix,0,sizeof(Radix*)*10);
Radix *pTemp=NULL;
Radix *pNode=NULL;
for (j=0; j<nLength;j++)
{
nDeal=arr[j]/nBase%10;
pTemp=(Radix*)malloc(sizeof(Radix));
pTemp->nValue=arr[j];
pTemp->pNext=NULL;
//尾添加
if (pRadix[nDeal] == NULL)
{
pRadix[nDeal]=pTemp;
}
else
{
pNode=pRadix[nDeal];
while(pNode->pNext)
{
pNode=pNode->pNext;
}
//节点放入
pNode->pNext=pTemp;
}
}
//数据放回原数组
nDeal=0;
for(j=0;j<10;j++)
{
pTemp=pRadix[j];
while(pTemp)
{
arr[nDeal]=pTemp->nValue;
pTemp=pTemp->pNext;
nDeal++;
}
}
//释放
for(j=0;j<10;j++)
{
pTemp=pRadix[j];
while(pTemp)
{
pNode=pTemp;
pTemp=pTemp->pNext;
free(pNode);
pNode=NULL;
}
}
//清空表头
//memset(pRadix,0,sizeof(Radix*)*10);
}
}
RadixSort最好的时间复杂度O(d*(n+r))
平均的时间复杂度O(d*(n+r))
最坏的时间复杂度O(d*(n+r))
空间复杂度O(n+r)
其中d是最大值位数,r是组数。
RadixSort是最稳定的排序算法,它是基于非比较的。
author:Cooper519
使用请标明来源