1.冒泡排序
主要思想:相邻元素比较,大的放后面,小的放前面,实质上是交换排序。
void BubbleSort(int *arr,int len)//冒泡排序
{
for(int i =0;i<len-1;i++)//趟数
{
for(int j = 0;j<len-i-1;j++)
{
int tmp = 0;
if(arr[j]>arr[j+1])
{
tmp = arr[j+1];
arr[j+1] = arr[j];
arr[j] = tmp;
}
}
}
}
时间复杂度:O(n^2)
最好:O(n^2)
最坏:O(n^2)
空间复杂度:O(1)
稳定性:稳定
冒泡排序的优化算法
void BubbleSort(int *arr,int len)//冒泡排序的优化算法
{
bool swap = false;
for(int i =0;i<len-1;i++)//趟数
{
for(int j = 0;j<len-i-1;j++)
{
int tmp = 0;
if(arr[j]>arr[j+1])
{
tmp = arr[j+1];
arr[j+1] = arr[j];
arr[j] = tmp;
swap = true;
}
}
if(!swap)
{
break;
}
}
时间复杂度:O(n^2)
最好:O(n)
最坏:O(n^2)
空间复杂度:O(1)
稳定性:稳定
2.选择排序
主要思想:每一趟在n-i+1(i = 1,2,…,n-1)个记录中选取关键字最小的记录作为有序序列的第i个记录。
void SelectSort(int *arr,int len)//简单选择排序
{
for(int i = 0;i<len;i++)
{
for(int j = i+1;j<len;j++)
{
int tmp = 0;
if(arr[i]>arr[j])
{
tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
}
}
}
时间复杂度:O(n^2)
最好:O(n^2)
最坏:O(n^2)
空间复杂度:O(1)
稳定性:不稳定
3.直接插入排序
主要思想:将一个记录插入到已排好序的有序表中,从而得到一个新的,记录数增1的有序表。
void InsertSort(int *arr,int len)//直接插入排序
{
for(int i = 1;i<len;i++)
{
int tmp = arr[i];
int j = 0;
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)
最坏:O(n^2)
空间复杂度:O(1)
稳定性:稳定
4.希尔排序
主要思想:又称缩小增量排序,先将整个待排序序列分割成为若干个子序列分别进行直接插入排序,待整个序列中的记录“基本有序”时,再对全体记录进行一次直接插入排序。
void Shell(int *arr,int len,int gap)
{
for(int i = gap;i < len;i++)
{
int tmp = arr[i];
int j = 0;
for(j = i-gap;j >= 0;j = j-gap)
{
if(arr[j] > tmp)
{
arr[j+gap] = arr[j];
}
else
{
break;
}
}
arr[j+gap] = tmp;
}
}
void ShellSort(int *arr,int len)//希尔排序
{
int drr[] = {5,3,1};
int lend = sizeof(drr)/sizeof(drr[0]);
for(int i = 0;i < lend;i++)
{
Shell(arr,len,drr[i]);
}
}
时间复杂度:O(n^1.3~1.5)
最好:O(n^2)
最坏:O(n^2)
空间复杂度:O(1)
稳定性:不稳定
5.堆排序
主要思想:先建立一个“大根堆”,使记录序列按关键字非递减有序排序,则在堆排序的算法这先建一个“大顶堆”,即先选得一个关键字为最大的记录并与序列中最后一个记录交换,然后对系列中前n-1记录进行筛选,重新将它调整为一个“大顶堆”,如此反复排序结束。
void Adjust(int *arr,int start,int end)//一次调整过程
{
int tmp = arr[start];
for(int i = 2*start+1;i <= end;i = 2*i+1)
{
//是否有右孩子
if((i<end)&&arr[i]<arr[i+1])
{
i++;
}
//i肯定是当前孩子最大值的下标
if(arr[i]>tmp)
{
arr[start] = arr[i];
start = i;
}
else
{
break;
}
}
arr[start] = tmp;
}
void HeapSort(int *arr,int len)//堆排序
{
int i = 0;
for(i = (len-1-1)/2;i>=0;i--)
{
Adjust(arr,0,len-1);
//肯定是大根堆
//先交换,后调整(只需要调整0号下标这颗树)
}
for(i = 0;i<len-1;i++)
{
int tmp = arr[0];
arr[0] = arr[len-i-1];
arr[len-1-i] = tmp;
Adjust(arr,0,len-1-i-1);
}
}
时间复杂度:O(nlog2n)
最好:O(nlog2n)
最坏:O(nlog2n)
空间复杂度:O(1)
稳定性:不稳定
6.归并排序
主要思想:即先使每个子序列有序,再使子序列段间有序 。
void Merge(int *arr,int len,int gap)
{
int *brr = (int*)malloc(sizeof(int)*len);
assert(brr!=NULL);
int i = 0;//brr下标
int start1 = 0;
int end1 = start1+gap-1;
int start2 = end1+1;
int end2 = start2+gap-1<len-1?start2+gap-1:len-1;
//当有两个归并段的时候
while(start2 < len)
{
//当两个归并段还没有比较的时候
while(start1 <= end1&&start2 <= end2)
{
if(arr[start1] <= arr[start2])
{
brr[i++] = arr[start1++];
}
else
{
brr[i++] = arr[start2++];
}
}
while(start1 <= end1)
{
brr[i++] = arr[start1++];
}
while(start2 <= end2)
{
brr[i++] = arr[start2++];
}
start1 = end2+1;
end1 = start1+gap-1;
start2 = end1+1;
end2 = start2+gap-1<len-1?start2+gap-1:len-1;
}
while(start1<len)
{
brr[i++] = arr[start1]++;
}
for(int i = 0;i < len;i++)
{
brr[i] = arr[i];
}
}
void MergeSort(int *arr,int len)//归并排序
{
for(int i = 1;i < len;i*=2)
{
Merge(arr,len,i);
}
}
时间复杂度:O(nlog2n)
最好:O(nlog2n)
最坏:O(nlog2n)
空间复杂度:O(n)
稳定性:稳定
7.快速排序
主要思想:通过一趟排序将待排记录分割成独立的两部分,其中一部分的关键字均比另一部分记录的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。
(1)固定选取基准法
1*递归形式
int Partion(int *arr,int low,int high)//一趟排序
{
int tmp = arr[low];
while(low<high)
{
while(low<high&&arr[high]>tmp)
{
high--;
}
if(low>=high)
{
break;
}
else
{
arr[low] = arr[high];
}
while(low<high&&arr[low]<tmp)
{
low++;
}
if(low>=high)
{
break;
}
else
{
arr[high] = arr[low];
}
}
arr[low] = tmp;
return low;
}
void Quick(int *arr,int start,int end)//快速排序递归
{
int par = Partion(arr,start,end);//左边 右边
if(par>start+1)
{
Quick(arr,start,par-1);
}
if(par<end-1)
{
Quick(arr,par+1,end);
}
}
void QuickSort(int *arr,int len)
{
Quick(arr,0,len-1);
}
2*非递归形式(用栈来实现)
void QuickSort1(int *arr,int len)//非递归快速排序
{
int tmpSize = (int)log((double)len)/long((double)2);
int *stack = (int*)malloc(sizeof(int)*tmpSize*2);
int top = 0;//数组的下标
assert(stack != NULL);
int low = 0;
int high = len-1;
int par = Partion(arr,low,high);//第一次找完基准
if(par>low+1)
{
stack[top++] = low;
stack[top++] = par-1;
}
if(par<high-1)
{
stack[top++] = par+1;
stack[top++] = high;
}
while(top>0)//栈不为空
{
high = stack[--top];
low = stack[--top];
par = Partion(arr,low,high);
if(par>low+1)
{
stack[top++] = low;
stack[top++] = par-1;
}
if(par<high-1)
{
stack[top++] = par+1;
stack[top++] = high;
}
}
free(stack);
}
(2)随机选取基准法
随机选取基准防止序列有序带来的时间上的浪费,提高效率。
int Partion(int *arr,int low,int high)//一趟排序
{
int tmp = arr[low];
while(low<high)
{
while(low<high&&arr[high]>tmp)
{
high--;
}
if(low>=high)
{
break;
}
else
{
arr[low] = arr[high];
}
while(low<high&&arr[low]<tmp)
{
low++;
}
if(low>=high)
{
break;
}
else
{
arr[high] = arr[low];
}
}
arr[low] = tmp;
return low;
}
void Swap(int *arr,int start,int end)
{
int tmp = arr[start];
arr[start] = arr[end];
arr[end] = tmp;
}
void Quick(int *arr,int start,int end)
{
srand((unsigned int)time(NULL));
Swap(arr,low,rand() % (high-low)+low);
int par = Partion(arr,start,end);//左边 右边
if(par>start+1)
{
Quick(arr,start,par-1);
}
if(par<end-1)
{
Quick(arr,par+1,end);
}
}
void QuickSort(int *arr,int len)
{
Quick(arr,0,len-1);
}
(3)三分取中法
减少有序和随机性发生的不确定性造成的不必要的时间上的浪费。
int Partion(int *arr,int low,int high)//一趟排序
{
int tmp = arr[low];
while(low<high)
{
while(low<high&&arr[high]>tmp)
{
high--;
}
if(low>=high)
{
break;
}
else
{
arr[low] = arr[high];
}
while(low<high&&arr[low]<tmp)
{
low++;
}
if(low>=high)
{
break;
}
else
{
arr[high] = arr[low];
}
}
arr[low] = tmp;
return low;
}
void Swap(int *arr,int start,int end)
{
int tmp = arr[start];
arr[start] = arr[end];
arr[end] = tmp;
}
void SelectPivotMedianOfThree(int *arr,int low,int high)//三分取中法
{
int mid = (high-low)/2+low;//(high+low)>>1;
//arr[mid]<=arr[low]<=arr[high];
if(arr[mid]>arr[low])
{
Swap(arr,mid,low);
}
if(arr[low]>arr[high])
{
Swap(arr,low,high);
}
if(arr[mid]>arr[high])
{
Swap(arr,mid,high);
}
}
void Quick(int *arr,int start,int end)
{
SelectPivotMedianOfThree(arr,start,end);
int par = Partion(arr,start,end);//左边 右边
if(par>start+1)
{
Quick(arr,start,par-1);
}
if(par<end-1)
{
Quick(arr,par+1,end);
}
}
void QuickSort(int *arr,int len)
{
Quick(arr,0,len-1);
}
时间复杂度:O(nlog2n)
最好:O(nlog2n)
最坏:O(n^2)
空间复杂度:O(log2n)
稳定性:不稳定
快速排序优化
(1)当待排序的区间中,待排序的个数小于某一个数量级的时候,使用插入排序(插入排序越有序越快,可以达到O(n)的时间复杂度)
void InsertSort(int *arr,int low,int high)//直接插入排序
{
for(int i = low+1;i<high;i++)
{
int tmp = arr[i];
int j = 0;
for(j = i-1;j>=0;j--)
{
if(arr[j] > tmp)
{
arr[j+1] = arr[j];
}
else
{
break;
}
}
arr[j+1] = tmp;
}
}
int Partion(int *arr,int low,int high)//一趟排序
{
int tmp = arr[low];
while(low<high)
{
while(low<high&&arr[high]>tmp)
{
high--;
}
if(low>=high)
{
break;
}
else
{
arr[low] = arr[high];
}
while(low<high&&arr[low]<tmp)
{
low++;
}
if(low>=high)
{
break;
}
else
{
arr[high] = arr[low];
}
}
arr[low] = tmp;
return low;
}
void Swap(int *arr,int start,int end)
{
int tmp = arr[start];
arr[start] = arr[end];
arr[end] = tmp;
}
void Quick(int *arr,int start,int end)
{
if((end-start+1) < 100)
{
InsertSort(arr,start,end);
return;
}
int par = Partion(arr,start,end);//左边 右边
if(par>start+1)
{
Quick(arr,start,par-1);
}
if(par<end-1)
{
Quick(arr,par+1,end);
}
}
void QuickSort(int *arr,int len)
{
Quick(arr,0,len-1);
}
(2)聚集相同元素基准法
当有多个元素相同时,用此法可以大大提高效率。
int Partion(int *arr,int low,int high)//一趟排序
{
int tmp = arr[low];
while(low<high)
{
while(low<high&&arr[high]>tmp)
{
high--;
}
if(low>=high)
{
break;
}
else
{
arr[low] = arr[high];
}
while(low<high&&arr[low]<tmp)
{
low++;
}
if(low>=high)
{
break;
}
else
{
arr[high] = arr[low];
}
}
arr[low] = tmp;
return low;
}
void Swap(int *arr,int start,int end)
{
int tmp = arr[start];
arr[start] = arr[end];
arr[end] = tmp;
}
void FocusNumPar(int *arr,int low,int par,int high,int *left,int *right)
{
if(low < high);
{
int parLeft = par-1;
int ph = par+1;
int i = par-1;
for(int i = par-1;i >= low;i--)
{
if(arr[i] == arr[par])
{
if(i != parLeft)
{
Swap(arr,i,parLeft);
parLeft--;
}
else
{
parLeft--;
}
}
}
int parRight = par+1;
for(int i = par+1;i <= high;i++)
{
if(arr[i] == arr[par])
{
if(i != parRight)
{
Swap(arr,i,parRight);
parRight++;;
}
else
{
parRight++;
}
}
}
}
}
void Quick(int *arr,int start,int end)
{
int par = Partion(arr,start,end);//左边 右边
int left = 0;
int right = 0;
FocusNumPar(arr,start,par,end,&left,&right);
if(left >= start+1)
{
Quick(arr,start,left);
}
if(right <= end-1)
{
Quick(arr,right,end);
}
}
void QuickSort(int *arr,int len)
{
Quick(arr,0,len-1);
}
以下是具体的每个排序算法的复杂度