排序,是计算机程序设计中的一种重要操作,他的功能是将一个数据元素的任意序列,重新排列成一个按关键字有序的序列。
排序方法有稳定与不稳定之分
由于待排序的记录数量不同,使得排序过程中涉及的存储器不同,可将排序方法分为两大类:一类是内部排序,指的是待排序记录存放在计算机随机存储器中进行的排序过程;另一类是外部排序,指的是待排序记录的数量很大,以致内存一次不能容纳全部记录,在排序过程中尚需对外存进行访问的排序过程。
直接插入排序是一种最简单的排序方法他的基本操作是将一个记录插入到已排好序的有序表中,从而得到一个新的、记录数增1的有序表。
#include <stdio.h>
#include <stdlib.h>
void InsertSort1(int *arr,int len)//O(n^2),O(1),稳定,该算法不好
{
int i;
int j;
int tmp;
int k;
for(i=1;i<len;i++)
{
tmp = arr[i];//取出arr[i]和之前有序的值比较 开始时将arr[0]看做一个有序的序列,所以第一次比较时,arr[1]与有序序列arr[0]比较
for(j=0;j<i;j++)//找位置,由于i之前的序列都有序,将arr[i]与前面的有序序列一一比较,找出第一个比他大的数字。这种实现不好,在有序情况下还是O(n^2) 从有序序列从前往后找
{
if(arr[j] > tmp)
{
break;
}
}
for(k=i-1;k>=j;k--)//移数据 由于将arr[i]取出,i的位置为空,找到arr[j]比他大,所以将j——i-1的数字依次向后移动一个位置,将数字插在arr[j]的位置
{
arr[k+1] = arr[k];
}
arr[j] = tmp;//插入
}
}
void InsertSort(int *arr,int len)//O(n^2),O(1),稳定,完全有序则时间复杂度为O(n)
{
int i;
int j;
int tmp;
for(i=1;i<len;i++)
{
tmp = arr[i];
for(j=i-1;j>=0;j--)//从有序序列从后往前找
{
if(arr[j] <= tmp)//前面的已经有序,如果有序的最后一个数据arr[j]都小于tmp则跳出循环,数据放在原来的位置
{
break;
}
arr[j+1] = arr[j];
}
arr[j+1] = tmp;//插在第一个比他小的数据后面
}
}
void Show(int *arr,int len)
{
for(int i=0;i<len;i++)
{
printf("%d ",arr[i]);
}
printf("\n");
}
int main()
{
int arr[] = {5,9,0,23,45,78,65,4,2,1,9,8};
InsertSort(arr,sizeof(arr)/sizeof(arr[0]));
Show(arr,sizeof(arr)/sizeof(arr[0]));
return 0;
}
希尔排序又称缩小增量排序,它也是一种属插入排序类的方法,但在时间效率上较直接排序有较大的改进。它的基本思想是:先将整个待排序记录序列分割成若干个子序列分别进行直接插入排序,待整个序列中的记录“基本有序”时,再对全体记录进行一次直接插入排序。
#include<stdio.h>
#include<stdlib.h>
//gap增量,即组数也是间隔值
void Shell(int *arr,int len,int gap)
{
int i;
int j;
int tmp;
for(i=gap;i<len;i++)
{
tmp = arr[i];
for(j=i-gap;j>=0;j-=gap)
{
if(arr[j] <= tmp)
{
break;
}
arr[j+gap] = arr[j];
}
arr[j+gap] = tmp;
}
}
void ShellSort(int *arr,int len)//O(n^1.3)~O(n^1.5),O(1),不稳定
{
int brr[] = {5,3,1};//缩小增量
for(int i=0;i<sizeof(brr)/sizeof(brr[0]);i++)
{
Shell(arr,len,brr[i]);
}
}
int main()
{
int arr[] = {5,9,0,23,45,78,65,4,2,1,9,8};
ShellSort(arr,sizeof(arr)/sizeof(arr[0]));
Show(arr,sizeof(arr)/sizeof(arr[0]));
return 0;
}
藉助“交换”进行排序的方法,其中最简单的一种就是人们所熟知的“起泡排序” 也叫“冒泡排序”。基本操作是:比较相邻元素的值,如果第一个比第二个大,就交换他们两个。每一趟到结尾时,最后的元素会是最大的数,知道没有任何一对数字需要比较。
void BubbleSort(int *arr,int len)//O(n^2),O(1),稳定
{
int i;//i为下标,len为长度。
int j;
int tmp;
for(i=0;i<len-1;i++)//外层循环用来控制排序趟数
{
for(int j=0;j+1<len-i;j++)//每趟排序内部两两比较,沉底的数字已经有序所以不在参加比较j<len-1-i
{
if(arr[j] > arr[j+1])
{
tmp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = tmp;
}
}
}
}
void Show(int *arr,int len)
{
for(int i=0;i<len;i++)
{
printf("%d ",arr[i]);
}
printf("\n");
}
int main()
{
int arr[] = {5,9,0,23,45,78,65,4,2,1,9,8};
BubbleSort(arr,sizeof(arr)/sizeof(arr[0]));
Show(arr,sizeof(arr)/sizeof(arr[0]));
return 0;
}
快速排序是对起泡排序的一种改进。它的基本思想是,通过一趟排序将待排序记录分割成独立的两部分,其中一部分记录的关键字都比另一部分记录的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。(快速排序越无序越快,有序则退化成选择排序)
/快速排序一次划分
//start:划分的起始下标
//end:划分的结束下标
//返回基准最终存放的下标
int Partition(int *arr,int start,int end)//O(n),O(1)
{
int low = start;
int high = end;
int tmp = arr[low];
while(low < high)
{
while((low<high) && arr[high]>=tmp)//比tmp小的放左边比tmp大的放右边
high--;
arr[low] = arr[high];
while((low<high) && arr[low]<=tmp)
low++;
arr[high] = arr[low];
}
arr[low] = tmp;
return low;
}
static void Quick(int *arr,int start,int end)
{
int par = Partition(arr,start,end);
if(start < par-1)
{
Quick(arr,start,par-1);
}
if(par < end-1)
{
Quick(arr,par+1,end);
}
}
void QuickSort(int *arr,int len)//O(nlogn),O(logn),不稳定
{
Quick(arr,0,len-1);
}
void Show(int *arr,int len)
{
for(int i=0;i<len;i++)
{
printf("%d ",arr[i]);
}
printf("\n");
}
int main()
{
QuickSort(arr,sizeof(arr)/sizeof(arr[0]));
Show(arr,sizeof(arr)/sizeof(arr[0]));
return 0;
}
选择排序:一趟简单选择排序的操作为:通过n-i次关键字间的比较,从n-i+1个记录中选出关键字最小的记录,并和第i个记录交换(从无序序列中找出最小的数据放在有序序列的下一个位置/和无序序列的第一个数据交换)
//选择排序,O(n^2),O(1),不稳定
void SelectSort(int *arr,int len)
{
int minIndex;
int i;
int j;
int tmp;
for(i=0;i<len-1;i++)//外部循环也就相当于控制循环趟数
{
minIndex = i;
for(j=i+1;j<len;j++)
{
if(arr[minIndex] > arr[j])//每一趟循环中找出最小的一个数据
{
minIndex = j;
}
}
if(i != minIndex)//和无序序列的第一个arr[i]交换
{
tmp = arr[i];
arr[i] = arr[minIndex];
arr[minIndex] = tmp;
}
}
}
int main()
{
int arr[] = {5,9,0,23,45,78,65,4,2,1,9,8};
SelectSort(arr,sizeof(arr)/sizeof(arr[0]));
Show(arr,sizeof(arr)/sizeof(arr[0]));
return 0;
}
树形选择排序又称锦标赛排序,是一种按照锦标赛的思想进行选择排序的方法。
//一次堆调整//O(logn),O(1),
void HeapAdjust(int *arr,int start,int end)
{
int tmp = arr[start];
int parent = start;
for(int i=2*start+1;i<=end;i=2*i+1)
{
if(i+1<=end && arr[i]<arr[i+1])//左右孩子较大值的下标
i++;
if(arr[i] > tmp)
{
arr[parent] = arr[i];
parent = i;
}
else
{
break;
}
}
arr[parent] = tmp;
}
void HeapSort(int *arr,int len)//O(nlogn),O(1),不稳定
{
int i;
int j;
int tmp;
//第一次建大根堆
for(i=(len-1-1)/2;i>=0;i--)//O(nlogn)
{
HeapAdjust(arr,i,len-1);
}
for(i=0;i<len-1;i++)
{
tmp = arr[0];
arr[0] = arr[len-1-i];
arr[len-1-i] = tmp;
HeapAdjust(arr,0,len-1-i-1);
}
}
void Show(int *arr,int len)
{
for(int i=0;i<len;i++)
{
printf("%d ",arr[i]);
}
printf("\n");
}
int main()
{
int arr[] = {5,9,0,23,45,78,65,4,2,1,9,8};
HeapSort(arr,sizeof(arr)/sizeof(arr[0]));
Show(arr,sizeof(arr)/sizeof(arr[0]));
return 0;
}