C语言——八大排序在这里插入代码片
#一、冒泡排序
时间复杂度:平均情况:O(n^2) 最好情况:O(n)
空间复杂度:O(1)
稳定性:稳定
主要思路:
1.比较相邻的元素。如果第一个比第二个大,就交换它们两个。
2.对每一个相邻元素做同样的工作,从开始第一对到结尾的每一对。在这一 点,最后的元素应该会是最大的数。
3.针对多有的元素重复以上的步骤,除了最后一个。
持续每次对越来越少的元素重复上面的步骤,知道没有任何一对数字需要比较。
描述:冒泡排序是一种简单的排序算法。它重复地按顺序走访过要排序的数列,一次比较前后两个元素,如果它们的顺序错误就把它们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。
步骤:
比较相邻的元素。如果第一个比第二个大,就交换它们两个
对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对,这样在最后的元素应该会是最大的数
针对所有的元素重复以上的步骤,除了最后一个
重复步骤1~3,直到排序完成,即不再发生交换
#二、选择排序
时间复杂度:O(n^2)
空间复杂度:O(1)
稳定性:不稳定(相同的数值都交换了)
主要思路:
每一次从无序组的数据元素中选出最小的一个元素,存放在无序组的起始位置,无需组的元素减少,有序组的元素增加,直到全部待排序的数据元素排完。
描述:选择排序(Selection-sort)是一种简单直观的排序算法。它的工作原理:首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。
算法:
初始状态:无序区为R[1…n],有序区为空。
第i趟排序(i=1,2,3…n-1)开始时,当前有序区和无序区分别为R[1…i-1]和R[i…n]。该趟排序从当前无序区中-选出关键字最小的记录 R[k],将它与无序区的第1个记录即R[i]与R[k]交换,使R[1…i]和R[i+1…n)分别变为记录个数增加1个的新有序区和记录个数减少1个的新无序区;
n-1趟结束,数组有序化了。
#三、插入排序
直接插入排序:
时间复杂度:无序 O(n^2) 有序O(n)
空间复杂度:O(1)
稳定性:稳定
主要思路:
插入排序是最简单常用的方法,将数组分为两部分,排好序的数列,以及未排序的数列,将未排序的数列中的元素 与排好序的数列进行比较,然后将该元素插入到已排序列的合适位置中。
描述:插入排序(Insertion-Sort)的算法描述是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。
算法:
初始状态:R[1]有序;
第i趟排序(i=1,2,3…n-1)开始时,当前有序区和无序区分别为R[1…i-1]和R[i…n]。取出下一个元素即R[i],在已经排序的元素序列中从后向前扫描,如果该元素(已排序)大于新元素,将该元素移到下一位置,重复上述步骤,直到找到已排序的元素小于或者等于新元素的位置;
将新元素插入到该位置后,i=i+1,重复上述步骤。
n-1趟结束,数组有序化了。
#四、希尔排序:(直接插入的优化)
时间复杂度:O(n1.5-n1.5)
空间复杂度:O(1)
稳定性:不稳定
工作原理:
把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止。
希尔排序,也称递减增量排序算法,是插入排序的一种更高效的改进版本。希尔排序是非稳定排序算法。
希尔排序是基于插入排序的以下两点性质而提出改进方法的:
1.插入排序在对几乎已经排好序的数据操作时,效率高,即可以达到线性排序的效率
2.但插入排序一般来说是低效的,因为插入排序每次只能将数据移动一位
for(int i=n/2;i>0;i/=2){
for(int k=i;k<n;k++){
int inserted = a[k];
int j;
for(j=k-i;j>=0 && inserted < a[j]; j-=i)
a[j+i]=a[j];
a[j+i]=inserted;
}
}
五、堆排序
时间复杂度:O(nlog2n)
空间复杂度:O(1)
稳定性:不稳定
主要思路:
1.将待排序序列构造成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点。
2.将其与末尾元素进行交换,此时末尾就是最大值。
3.然后将剩余n -1 个元素重新构造成一个堆,这样会得到n个元素的次小值。
4.如此反复执行,便得到一个有序序列。
/*堆排序子函数1*/
void heapInsert(int arr[],int n) {
for (int i = 0; i < n; i++) {
//当前插入的索引
int currentIndex = i;
//父结点索引
int fatherIndex = (currentIndex - 1) / 2;
//如果当前插入的值大于其父结点的值,则交换值,并且将索引指向父结点
//然后继续和上面的父结点值比较,直到不大于父结点,则退出循环
while (arr[currentIndex] > arr[fatherIndex]) {
//交换当前结点与父结点的值
swap(arr[currentIndex], arr[fatherIndex]);
//将当前索引指向父索引
currentIndex = fatherIndex;
//重新计算当前索引的父索引
fatherIndex = (currentIndex - 1) / 2;
}
}
}
/*堆排序子函数2*/
void heapify(int arr[], int index, int size) {
int left = 2 * index + 1;
int right = 2 * index + 2;
while (left < size) {
Print_Data_D(arr,LEN,left,688);
int largestIndex;
//判断孩子中较大的值的索引(要确保右孩子在size范围之内)
if (arr[left] < arr[right] && right < size) {
largestIndex = right;
}
else {
largestIndex = left;
}
//比较父结点的值与孩子中较大的值,并确定最大值的索引
if (arr[index] > arr[largestIndex]) {
largestIndex = index;
}
//如果父结点索引是最大值的索引,那已经是大根堆了,则退出循环
if (index == largestIndex) {
break;
}
//父结点不是最大值,与孩子中较大的值交换
swap(arr[largestIndex], arr[index]);
//将索引指向孩子中较大的值的索引
index = largestIndex;
//重新计算交换之后的孩子的索引
left = 2 * index + 1;
right = 2 * index + 2;
}
}
/*堆排序子函数3*/
void heapSort(int arr[],int n) {
//构造大根堆
heapInsert(arr,n);
Print_Data_f(arr,n,n,688);
int size = n;
while (size > 1) {
//固定最大值
swap(arr[0], arr[size - 1]);
size--;
//构造大根堆
heapify(arr, 0, size);
}
}
/*堆排序*/
void HeapSort(int a[],int n)
{
Print_it();
time_t start,stop;
start = time(NULL);
heapSort(a,n);
stop = time(NULL);
Print_Data_Last(a,n,stop-start);
}
六、快速排序
时间复杂度:最好情况下:O(nlong2n) 最坏情况下:O(n^2)
空间复杂度:O(nlong2n)
稳定性:不稳定
主要思路:
快速排序是找出一个元素(理论上可以随便找一个)作为基准,然后对数组进行分区操作,使基准左边元素的值都不大于基准值,基准右边的值都不小于基准值,如此作为基准的元素调整到排序后的正确位置。递归快速排序,将其他n - 1 个元素也调整到排序后的正确位置。最后每个元素都是在排序后的正确位置,排序完成。所以快速排序算法的核心算法是分区操作,及如何调整基准的位置以及调整返回基准的最终位置以便分治递归。
/*快速排序子函数1*/
int Quick_middle(int a[],int low,int high){
int temp_i = (rand() % (high-low+1))+ low;//随机取,稳定时间
swap(a[low],a[temp_i]);
int temp = a[low];
while(low<high){
while(low<high&&a[high]>=temp)high--;
a[low]=a[high];
while(low<high&&a[low]<=temp)low++;
a[high]=a[low];
}
a[low]=temp;
return low;
}
/*快速排序子函数2*/
void Qsort(int a[],int low,int high){
if(low<high){
int temp = Quick_middle(a,low,high);
Print_Data_D(a,LEN,temp,1855);
Qsort(a,low,temp-1);
Qsort(a,temp+1,high);
}
}
/*快速排序*/
void QuickSort(int a[],int n)
{
Print_it();
time_t start,stop;
start = time(NULL);
Qsort(a,0,n-1);
stop = time(NULL);
Print_Data_Last(a,n,stop-start);
}
六、qsort.&&&&sort.
#include <stdio.h>
#include <stdlib.h>
int cmp(const void *a,const void *b)
{
return *(int *)a-*(int *)b;//这是从小到大排序,若是从大到小改成: return *(int *)b-*(int *)a;
}
int main()
{
int a[5]={1,3,4,9,6};
int n=5;
qsort(a,n,sizeof(a[0]),cmp);//(数组,需要排序的数字个数,单个数字所占内存大小,比较函数)
for(int i=0;i<n;i++)
printf("%d ",a[i]);
return 0;
}
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
int main(){
int a[5]={1,3,4,9,6};
sort(a,a+5);
for(int i=0;i<5;i++)
cout<<a[i]<<' ';
return 0;
}
八、归并排序
时间复杂度:O(nlong2n)
空间复杂度:O(n)
稳定性:稳定
主要思想:
将待排序序列R[0…n-1]看成是n个长度为1的有序序列,将相邻的有序表成对归并,得到n/2个长度为2的有序表;将这些有序序列再次归并,得到n/4个长度为4的有序序列;如此反复进行下去,最后得到一个长度为n的有序序列。
所谓归并排序,就是将待排序的数分成两半后排好序,然后再将两个排好序的序列合并成一个有序序列
对于合并,其实非常简单,我只要不断地取出两个有序数组中比较小的那一个放在一个辅助数组中(通过比较),直到把两个有序数组中的元素取完
/*归并排序子函数1*/
template <typename T> void merge(int lo,int mi,int hi,T *elem){
T *A = elem + lo;
int lb = mi-lo; T *B = new T[lb]; //左边
for(int i=0;i<lb;B[i] = A[i++]) ; //复制到左边 B
int lc = hi - mi; T *C = elem + mi;
for(int i=0,j=0,k=0; j<lb; ){
if((k < lc) && (C[k] <B[j]))
A[i++] = C[k++];
if(lc <= k || (B[j] <= C[k]))
A[i++] = B[j++];
}
delete []B;
}
/*归并排序子函数2*/
template <typename T> void mergeSort(int lo,int hi,T *elem){
if(hi - lo < 2) return ; //单元素区间自然有序
int mi = (lo + hi) >> 1;
Print_Data_D(elem,LEN,mi,624);
mergeSort(lo,mi ,elem); //递归的归并排序左边
mergeSort(mi,hi,elem); //递归的归并排序右边
merge(lo, mi,hi,elem); //合并
}
/*归并排序*/
void MergeSort(int a[],int n)
{
mergeSort(0,n,a);
}
void mergeSort(int *arr,int length);
void sortProcess(int *arr, int L, int R);
void merge(int *arr,int L,int mid,int R);
void MergeSort(int a[],int n){
int b[LEN];
copya(a,b,n);
if(b==NULL&& n<2)
return;
sortProcess(b,0,n-1);
Print_Data(b,n);
}
void mergeSort(int *arr,int length){
if(arr==NULL&& length<2)
return;
sortProcess(arr,0,length-1);
}
void sortProcess(int *arr, int L, int R){
int i=0;
int mid=(L+R)/2;
if(L==R)
return;
sortProcess(arr,L,mid);
sortProcess(arr,mid+1,R);
merge(arr,L,mid,R);
}
void merge (int *arr,int L,int mid,int R){
int help[R-L+1];
int i=0;
int p1=L;
int p2=mid+1;
while(p1<=mid&&p2<=R){
help[i++]=arr[p1]>arr[p2]? arr[p2++]:arr[p1++];
}
while(p1<=mid){
help[i++]=arr[p1++];
}
while(p2<=R){
help[i++]=arr[p2++];
}
for(i=0;i<R-L+1;i++){
arr[L+i]=help[i];
}
}
**总结:
- 当n比较小时,可以直接采用直接插入排序或者选择排序;
2.若数据初始状态基本有序(正序),选择直接插入排序、冒泡排序或者快速排序为宜;
3.若n比较大,则采用时间复杂度为O(nlong2n)的排序算法:快速排序、堆排序或者归并排序;
4.快速排序是目前基于比较的排序中被认为最好的方法,当待排关键字是随机分布时,快速排序的平均时间最短;
5.堆排序所需的辅助空间少于快速排序,并且不会出现快速排序可能出现的最坏情况,所以这两个排序实际运用的时候可以比较一下,选择最优的;但是这两种排序都是不稳定的。
6.若要求排序稳定,则可以选用归并排序,但是实际运用的时候通常可以将它和直接插入排序结合使用,先利用直接插入排序求得较长有序子序列,然后再两两归并。**
https://blog.csdn.net/songjiasheng1314/article/details/80663744