目录
一、冒泡排序:
-
时间复杂性:一般平均是O(n^2),最好的情况是O(n),最坏的情况是O(n^2)。
-
空间复杂性:O(1)。
-
稳定性:稳定排序。
-
冒泡排序的原理:
1、比较相邻的元素。如果第一个比第二个大,就交换他们两个。
2、对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。
3、针对所有的元素重复以上的步骤,除了最后一个。
4、持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
#include<stdio.h>
void BubbleSort(int *arr,int len)
{
for(int i=0;i<len-1;i++)
{
bool swap = false;
for(int j=0;j<len-1-i;j++)
{
int temp = 0;
if(arr[j]>arr[j+1])
{
temp=arr[j+1];
arr[j+1]=arr[j];
arr[j]=temp;
swap = true;
}
}
if(!swap)
{
break ;
}
}
}
void Show(int *arr,int len)
{
for(int i=0;i<len;i++)
{
printf("%d ",arr[i]);
}
printf("\n");
}
int main()
{
int arr[]={4,6,1,9,10,11,24,23};
int len =sizeof(arr)/sizeof(arr[0]);
BubbleSort(arr,len);
Show(arr,len);
}
运行结果如下:
二 、直接选择排序:
从待排数字开始,后面找到比待排数字小的就发生交换直到排完。
时间复杂性:O(n^2):最好O(n^2):最坏:O(n^2)。
空间复杂性:O(1)
有序 O(n^2)
无序 O(n^2)
稳定性:不稳定排序
#include<stdio.h>
void SelectSort(int *arr, int len)
{
for(int i=0;i<len-1;i++)
{
for(int j=i+1;j<len;j++)
{
int temp=0;
if(arr[j]<arr[i])
{
temp=arr[i];
arr[i]=arr[j];
arr[j]=temp;
}
}
}
}
void Show(int *arr,int len)
{
for(int i=0;i<len;i++)
{
printf("%d ",arr[i]);
}
printf("\n");
}
int main()
{
int arr[]={4,6,1,9,10,11,24,23};
int len =sizeof(arr)/sizeof(arr[0]);
SelectSort(arr,len);
Show(arr,len);
return 0;
}
运行结果如下:
三、直接插入排序:
直接插入排序是由两层嵌套循环组成的。外层循环标识并决定待比较的数值。内层循环为待比较数值确定其最终位置。直接插入排序是将待比较的数值与它的前一个数值进行比较,所以外层循环是从第二个数值开始的。当前一数值比待比较数值大的情况下继续循环比较, 直到找到比待比较数值小的并将待比较数值置入其后一位置,结束该次循环。
时间复杂性:一般平均是O(n^2),最好的情况是O(n),最坏的情况是O(n^2)。
空间复杂性:O(1)。
有序 O(n) 越有序越快。
无序 O(n^2)。
稳定性:稳定排序。
#include<stdio.h>
void InsertSort(int *arr,int len)
{
for(int i = 1;i<len;i++)
{
int temp=arr[i];
int j=0;
for(j=i-1;j>=0;j--)
{
if(arr[j]>temp)
{
arr[j+1]=arr[j];
}
else
{
break;
}
}
arr[j+1]=temp;
}
}
void Show(int *arr,int len)
{
for(int i=0;i<len;i++)
{
printf("%d ",arr[i]);
}
printf("\n");
}
int main()
{
int arr[]={4,6,1,9,10,11,24,23};
int len =sizeof(arr)/sizeof(arr[0]);
InsertSort(arr,len);
Show(arr,len);
return 0;
}
运行结果如下:
四、希尔排序
希尔排序简单说就是一种插入排序的优化,是分组插入的方法。
时间复杂性:一般平均是O(n^1.3),最好的情况是O(n),最坏的情况是O(n^2)。
空间复杂性:O(1)。
稳定性:不稳定。
代码如下:
#include<stdio.h>
void Shell(int *arr,int len,int gap)
{
for(int i = gap;i<len;i++)
{
int temp =arr[i];
int j=0;
for( j = i-gap;j >= 0;j=j-gap)
{
if(arr[j]>temp)
{
arr[j+gap]=arr[j];
}
else
{
break;
}
}
arr[j+gap]=temp;
}
}
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]);
}
}
void Show(int *arr,int len)
{
for(int i=0;i<len;i++)
{
printf("%d ",arr[i]);
}
printf("\n");
}
int main()
{
int arr[]={4,6,1,9,10,11,24,23,15,56,9,4,8,6,1};
int len =sizeof(arr)/sizeof(arr[0]);
ShellSort(arr,len);
Show(arr,len);
return 0;
}
运行结果如下:
五、快速排序
快速排序:是找基准的过程
快速排序(Quicksort)是对冒泡排序的一种改进。
快速排序由C. A. R. Hoare在1962年提出。它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
时间复杂性:一般平均是O(nlog2n),最好的情况是O(nlog2n),最坏的情况是O(n^2)。
空间复杂性:O(log2n)。
稳定性:不稳定排序。
代码如下:
#include<stdio.h>
#include<math.h>
#include<stdlib.h>
int Partion(int *arr,int low,int high)
{
int temp = arr[low];
while(low<high)
{
while(low<high&&arr[high]>temp)
{
high--;
}
arr[low]=arr[high];
while(low<high&&arr[low]<temp)
{
low++;
}
arr[high]=arr[low];
}
arr[low]=temp;
return low;
}
void Quick(int *arr,int low,int high)
{
int key=Partion(arr,low,high);
if(key-1>low)
{
Quick(arr,low,key-1);
}
if(key+1<high)
{
Quick(arr,key+1,high);
}
}
void QuickSort(int *arr,int len)//递归
{
Quick(arr,0,len-1);
}
void QuickSort1(int *arr,int len)//非递归
{
int top=0;
int low=0;
int high=len-1;
int Timesize= (int)log((double)len)/log((double)2);
int *stack =(int *)malloc(sizeof(int)*Timesize*2);
int par =Partion(arr,low,high);
if(par+1<high)
{
stack[top++]=par+1;
stack[top++]=high;
}
if(par-1>low)
{
stack[top++]=low;
stack[top++]=par-1;
}
while(top>0)
{
high=stack[--top];
low=stack[--top];
par=Partion(arr,low,high);
if(par+1<high)
{
stack[top++]=par+1;
stack[top++]=high;
}
if(par-1>low)
{
stack[top++]=low;
stack[top++]=par-1;
}
}
free(stack);
stack =NULL;
}
void Show(int *arr,int len)
{
for(int i=0;i<len;i++)
{
printf("%d ",arr[i]);
}
printf("\n");
}
int main()
{
int arr[]={1,26,5,8,6,4,12,45,63,2};
int len = sizeof(arr)/sizeof(arr[0]);
//QuickSort(arr,len);
QuickSort1(arr,len);
Show(arr,len);
return 0;
}
运行结果:
快速排序三种找基准方式及两种优化
快速排序基准数选取三种方式:
1、固定位置基准法:从左到右依次找基准
2、随机选取基准法:以下可以生成随机数;:ps在调用时函数中要加上srand((unsigned int)time(NULL));
#include<time.h>
int main()
{
int arr[100]={};
int len = sizeof(arr)/sizeof(arr[0]);
srand(time(NULL));
for(int i=0;i<len;i++)
{
arr[i]=rand()%100;
}
QuickSort(arr,len);
Show(arr,len);
return 0;
}
3、三数取中法:
void Swap(int *arr,int low,int high)
{
int temp =arr[low];
arr[low]=arr[high];
arr[high]=temp;
}
void SelectPivotMedianOfThree(int *arr,int low,int high)
{
int mid = (high-low)/2+low;//(high+low)>>1;
if(arr[high]<arr[low])
{
Swap(arr,low,high);
}
if(arr[mid]>arr[high])
{
Swap(arr,mid,high);
}
if(arr[mid]>arr[low])
{
Swap(arr,mid,low);
}
}
快速排序两种优化方式
1、聚集相同基准法
void FocusNumPar(int *arr,int low,int high,int par,int *Left,int *Right )
{
if(low<high)
{
int parleft=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--;
}
}
}
*Left=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++;
}
}
}
*Right=parright;
}
}
2、当数据量很少时,采用直接插入排序
void InsertSort(int *arr,int low,int high)
{
int temp;
for(int i=low+1;i<=high;i++)
{
temp=arr[i];
int j;
for(j=i-1;j>=low;j--)
{
if(arr[j]>temp)
{
arr[j+1]=arr[j];
}
else
{
break;
}
}
arr[j+1]=temp;
}
}
六、堆排
在堆的数据结构中,堆中的最大值总是位于根节点(在优先队列中使用堆的话堆中的最小值位于根节点)。堆中定义以下几种操作:
1、最大堆调整(Max Heapify):将堆的末端子节点作调整,使得子节点永远小于父节点
2、创建最大堆(Build Max Heap):将堆中的所有数据重新排序
3、堆排序(HeapSort):移除位在第一个数据的根节点,并做最大堆调整的递归运算
时间复杂性:一般平均是O(nlog2n),最好的情况是O(nlog2n),最坏的情况是O(nlog2n)。
空间复杂性:O(1)。
稳定性:不稳定排序。
代码如下:
#include<stdio.h>
#include<math.h>
#include<stdlib.h>
void Swap(int *arr,int start,int end)
{
int temp =arr[start];
for(int i =start*2+1;i<end;i=i*2+1)
{
if(arr[i]<arr[i+1]&&i<end)
{
i++;
}
if(arr[start]<arr[i])
{
arr[start]=arr[i];
start=i;
}
arr[start]=temp;
}
}
void Fun(int *arr,int start,int end)
{
for(int i = start;i>=0;i--)
{
Swap(arr,i,end);
}
}
void Fun2(int *arr,int len)
{
int end = len-1;
for(int i=end;i>0;i--)
{
Fun(arr,(i-1)/2,i);
int temp=arr[0];
arr[0]=arr[i];
arr[i]=temp;
}
}
void Show(int *arr,int len)
{
for(int i=0;i<len;i++)
{
printf("%5d",arr[i]);
}
printf("\n");
}
int main()
{
int arr[]={4,9,10,11,24,23,15,56,8,6,1};
int len =sizeof(arr)/sizeof(arr[0]);
int end=len-1;
int start=(end-1)/2;
//Swap(arr,start, end);
//Fun(arr,start, end);
Fun2(arr,len);
Show(arr,len);
return 0;
}
运行结果如下:
七、归并排序
归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。
时间复杂性:一般平均是O(nlog2n),最好的情况是O(nlog2n),最坏的情况是O(nlog2n)。
空间复杂性:O(n)。
稳定性:稳定排序。
代码如下:
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
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++)
{
arr[i] = brr[i];
}
}
void MergeSort(int *arr,int len)
{
for(int i = 1;i < len;i *= 2)
{
Merge(arr,len,i);
}
}
void Show(int *arr,int len)
{
for(int i = 0;i < len;i++)
{
printf("%d ",arr[i]);
}
printf("\n");
}
int main()
{
int arr[]={1,26,5,8,6,4,12,45,63,2};
int len = sizeof(arr)/sizeof(arr[0]);
MergeSort(arr,len);
Show(arr,len);
return 0;
}
运行结果:
以上是目前总结的七种排序,在八大排序中,还有一个基数排序,这个目前不做讨论。