目录
1.排序
(1)排序的稳定性
(2)内部排序和外部排序
2.插入排序
(1)直接插入 排序
(2)折半插入排序
(3)希尔排序
3.冒泡排序
4.快速排序
5.简单选择排序
6.堆排序
7.归并排序
8.基数排序
9.各种排序算法比较总结
1.排序
是计算机程序设计中的一种重要的操作,指将一个数据元素(或记录)的任意序列,重新排列成一个按关键字有序的序列。
(1)排序的稳定性
首先假设两个相同的关键字key1=key2,且在排序之前关键字key1在关键字key2之前;若排序之后的结果是关键字key1任然在关键字key2之前,那么排序是稳定的,否则排序不是稳定的。
(2)内部排序和外部排序
内部排序:待排序列记录存放在计算机随机存储器中进行的排序过程(也就是在内存中进行的排序)。
外部排序:待排记录的数量非常的大,导致内存一次性不能读取全部的记录,所以在排序过程中尚需对外存进行访问的排序过程。
2.插入排序
(1)直接插入 排序
算法思想:从待排序列中将一个关键字插入到排序好的有序表中,从而得到一个新的有序表。
假设待排记录为:49,38,65,97,76,13,27,49²
直接插入排序
初始关键字 | (49) | 38 | 65 | 97 | 76 | 13 | 27 | 49² | |
i=2 | (38) | (38,49) | 65 | 97 | 76 | 13 | 27 | 49² | |
i=3 | (38) | (38,49,65) | 97 | 76 | 13 | 27 | 49² | ||
i=4 | (38) | (38,49,65,97) | 76 | 13 | 27 | 49² | |||
i=5 | (76) | (38,49,65,76,97) | 13 | 27 | 49² | ||||
i=6 | (13) | (13,38,49,65,76,97) | 27 | 49² | |||||
i=7 | (27) | (13,27,38,49,65,76,97) | 49² | ||||||
i=8 | (49²) | (13,27,38,49,49²,65,76,97) |
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#define MAXSIZE 11
typedef int ElemType;
void InsertSort(int a[],int n){
for(int i=1;i<n;i++){
int temp=a[i];
int j=i;
while(j>0&&temp<a[j-1]){
a[j]=a[j-1];
j--;
}
a[j]=temp;
}
}
void display(int a[],int n){
for(int i=0;i<n;i++){
printf("%d\t",a[i]);
}
}
int main(){
int array[MAXSIZE];
int n;
for(int i=0;i<MAXSIZE;i++){
array[i]=0;
}
printf("请输入待排关键字个数: ");
scanf("%d",&n);
printf("请输入关键字: ");
for(int i=0;i<n;i++){
scanf("%d",&array[i]);
}
InsertSort(array,n);
display(array,n);
return 0;
}
/*
49 38 65 97 76 13 27 49
*/
(2)折半插入排序
思想:之前的直接插入排序从待排记录中取一个关键字,依次与有序表中的关键字进行比较,查找插入的位置,然而折半插入排序利用有序表的特点,进行折半查找,找到待插入关键字的位置。
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#define MAXSIZE 11
typedef int ElemType;
void BinarySort(int array[],int n){
for(int i=1;i<n;i++){
int left=0;
int right=i-1;
int temp=array[i];
while(left<=right){
int mid=(left+right)/2;
if(array[i]>array[mid]){
left=mid+1;
}else{
right=mid-1;
}
}
for(int j=i;j>=left;j--){
array[j]=array[j-1];
}
array[left]=temp;
}
}
void display(int a[],int n){
for(int i=0;i<n;i++){
printf("%d\t",a[i]);
}
}
int main(){
int array[MAXSIZE];
int n;
for(int i=0;i<MAXSIZE;i++){
array[i]=0;
}
printf("请输入待排关键字个数: ");
scanf("%d",&n);
printf("请输入关键字: ");
for(int i=0;i<n;i++){
scanf("%d",&array[i]);
}
BinarySort(array,n);
display(array,n);
return 0;
}
/*
49 38 65 97 76 13 27 49
*/
(3)希尔排序
思想:先将整个待排记录序列分割成为若干个子序列分别进行直接插入排序,待整个序列中的记录“基本有序”时,再对全体记录进行一次直接插入排序。
希尔排序
初始关键字 | 49 | 38 | 65 | 97 | 76 | 13 | 27 | 49² | 55 | 4 |
第一趟排序(d=5) | 13 | 27 | 49² | 55 | 4 | 49 | 38 | 65 | 97 | 76 |
第二趟排序(d=2) | 13 | 4 | 49² | 38 | 27 | 49 | 55 | 65 | 97 | 76 |
第三趟排序(d=1) | 4 | 13 | 27 | 38 | 49² | 49 | 55 | 65 | 76 | 97 |
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#define MAXSIZE 15
typedef int ElemType;
void ShellSort(int array[],int n){
int temp;
int d=n/2;
while(d){
for(int start=d;start<=2*d&&start<n;start++){
//这里面是一个直接插入排序
for(int i=start;i<n;i++){
temp=array[i];
int j=i;
while(j>=d&&temp<array[j-d]){
array[j]=array[j-d];
j-=d;
}
array[j]=temp;
}
d=d/2;
}
}
}
void display(int a[],int n){
for(int i=0;i<n;i++){
printf("%d\t",a[i]);
}
}
int main(){
int array[MAXSIZE];
int n;
for(int i=0;i<MAXSIZE;i++){
array[i]=0;
}
printf("请输入待排关键字个数: ");
scanf("%d",&n);
printf("请输入关键字: ");
for(int i=0;i<n;i++){
scanf("%d",&array[i]);
}
ShellSort(array,n);
display(array,n);
return 0;
}
/*
49 38 65 97 76 13 27 49 55 4
49 38 65 97 76 13 27 49 55 4
*/
3.冒泡排序
思想:首先将第一个关键字和第二个关键字进行比较,若为逆序,则交换两个关键字,然后比较第二个关键字和第三个关键字,直到第n-1个关键字和第n个关键字进行比较,完成一趟冒泡排序,继续后面的操作直到比较到只有一个关键字为止。
冒泡排序
初始关键字 | 49 | 38 | 65 | 97 | 76 | 13 | 27 | 52 |
第一趟排序结果 | 38 | 49 | 65 | 76 | 13 | 27 | 52 | (97) |
第二趟排序结果 | 38 | 49 | 65 | 13 | 27 | 52 | (76,97) | |
第三趟排序结果 | 38 | 49 | 13 | 27 | 52 | (65,76,97) | ||
第四趟排序结果 | 38 | 13 | 27 | 49 | (52,65,76,97) | |||
第五趟排序结果 | 13 | 27 | 38 | (49,52,65,76,97) | ||||
第六趟排序结果 | 13 | 27 | (38,49,52,65,76,97) | |||||
第七趟排序结果 | 13 | (27,38,49,52,65,76,97) | ||||||
第八趟排序结果 | (13,27,38,49,52,65,76,97) |
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#define MAXSIZE 15
typedef int ElemType;
void BubbleSort(int array[],int n){
for(int i=0;i<n;i++){
for(int j=0;j<n-i-1;j++){
if(array[j]>array[j+1]){
int temp=array[j];
array[j]=array[j+1];
array[j+1]=temp;
}
}
}
}
void display(int a[],int n){
for(int i=0;i<n;i++){
printf("%d\t",a[i]);
}
}
int main(){
int array[MAXSIZE];
int n;
for(int i=0;i<MAXSIZE;i++){
array[i]=0;
}
printf("请输入待排关键字个数: ");
scanf("%d",&n);
printf("请输入关键字: ");
for(int i=0;i<n;i++){
scanf("%d",&array[i]);
}
BubbleSort(array,n);
display(array,n);
return 0;
}
/*
49 38 65 97 76 13 27 49 55 4
49 38 65 97 76 13 27 49 55 4
*/
4.快速排序
思想:通过一趟排序将待排记录分割成独立的两部分,其中一部分记录的关键字均另一部分记录的关键字 小,然后再分别对这两部分记录进行排序,以达到整个记录有序。
快速排序
初始关键字 | 49,38,65,97,76,13,27,49² | |||||||
第一趟排序结果 | {27,38,13} | 49 | {76,97,65,49²} | |||||
第二趟排序结果 | {13} | 27 | {38} | 49 | {49²,65} | {76} | {97} | |
第三趟排序结果 | 13 | 27 | 38 | 49 | 49² | 65 | 76 | 97 |
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#define MAXSIZE 15
typedef int ElemType;
int Pariation(int array[],int low,int high){
int temp=array[low];
while(low<high){
while(low<high&&temp<array[high]){
high--;
}
if(low<high);
array[low]=array[high];
while(low<high&&temp>=array[low]){
low++;
}
if(low<high)
array[high]=array[low];
}
array[low]=temp;
return low;
}
void QuickSort(int array[],int low,int high,int n){
if(low<high){
int pivo=Pariation(array,low,high);
QuickSort(array,low,pivo-1,n);
QuickSort(array,pivo+1,high,n);
}
}
void display(int a[],int n){
for(int i=0;i<n;i++){
printf("%d\t",a[i]);
}
}
int main(){
int array[MAXSIZE];
int n;
for(int i=0;i<MAXSIZE;i++){
array[i]=0;
}
printf("请输入待排关键字个数: ");
scanf("%d",&n);
printf("请输入关键字: ");
for(int i=0;i<n;i++){
scanf("%d",&array[i]);
}
QuickSort(array,0,n-1,n);
display(array,n);
return 0;
}
/*
49 38 65 97 76 13 27 49
*/
5.简单选择排序
思想:每一趟从n-i+1(i=1,2,……,n-1)个记录中选择最小关键字作为有序序列中第i个记录。
简单选择排序
初始关键字 | 23 | 5 | 13 | 35 | 32 |
第一趟排序结果 | 5 | 23 | 13 | 35 | 32 |
第二趟排序结果 | (5,13) | 23 | 35 | 32 | |
第三趟排序结果 | (5,13,23) | 35 | 32 | ||
第四趟排序结果 | (5,13,23,32) | 35 | |||
第五趟排序结果 | (5,13,23,32,35) |
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#define MAXSIZE 15
typedef int ElemType;
int SelectSort(int array[],int n){
for(int i=0;i<n;i++){
int min_index=i;
for(int j=i+1;j<n;j++){
if(array[min_index]>array[j]){
min_index=j;
}
}
if(min_index!=i){
int temp=array[i];
array[i]=array[min_index];
array[min_index]=temp;
}
}
}
void display(int a[],int n){
for(int i=0;i<n;i++){
printf("%d\t",a[i]);
}
}
int main(){
int array[MAXSIZE];
int n;
for(int i=0;i<MAXSIZE;i++){
array[i]=0;
}
printf("请输入待排关键字个数: ");
scanf("%d",&n);
printf("请输入关键字: ");
for(int i=0;i<n;i++){
scanf("%d",&array[i]);
}
SelectSort(array,n);
display(array,n);
return 0;
}
/*
49 38 65 97 76 13 27 49
*/
6.堆排序
思想:首相将待排序列调整为堆大堆根(或者小堆根),由于堆顶一定最大的(或者最小的)关键字,所以将堆顶关键字输出;之后继续对剩下的n-1个关键字继续调整为大堆根(或者小堆根),直到剩下的关键字为0.
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#define MAXSIZE 15
void PercolateDown( int pa[],int pos,int size)
{
int p=pos,c=2*p+1;
int temp=pa[p];
while(c<size)
{
if(c+1<size&&pa[c+1]>pa[c])
{
++c;
}
if(temp>=pa[c])
{
break;
}else
{
pa[p]=pa[c];
p=c;
c=2*p+1;
}
}
pa[p]=temp;
}
void PercolateDown(int pa[],int size)
{
int p=0,c=2*p+1;
int temp=pa[p];
while(c<size)
{
if(c+1<size&&pa[c+1]>pa[c])
{
++c;
}
if(temp>pa[c])
{
break;
}else
{
pa[p]=pa[c];
p=c;
c=2*p+1;
}
}
pa[p]=temp;
}
void BuildHeap(int pa[],int size)
{
for(int i=size/2-1;i>=0;i--)
{
PercolateDown(pa,i,size);
}
}
void Heapsort(int pa[],int n)
{
int temp;
BuildHeap(pa,n);
for(int i=n-1;i>=0;i--)
{
temp=pa[0];
pa[0]=pa[i];
pa[i]=temp;
PercolateDown(pa,0,i);
}
}
void PercolateUp(int pa[],int pos)
{
int c=pos,p=(c-1)/2;
int temp=pa[c];
while(c>0)
{
if(temp<pa[p])
{
break;
}else
{
pa[c]=pa[p];
c=p;
p=(c-1)/2;
}
}
pa[c]=temp;
}
void BuildHeap2(int pa[],int size)
{
for(int i=0;i<size;i++)
{
PercolateUp(pa,i);
}
}
void Heapsort2(int pa[],int n)
{
int temp;
BuildHeap2(pa,n);
for(int i=n-1;i>=0;i--)
{
temp=pa[0];
pa[0]=pa[i];
pa[i]=temp;
PercolateDown(pa,i);
//PercolateDown(pa,i)与BuildHeap2(pa,i)等价;
}
}
int main()
{
int pa[10]={6,7,89,5,12,4,3,2,35,8};
printf("向下调整为大堆根并排序:\n");
BuildHeap(pa,10);
Heapsort(pa,10);
for(int i=0;i<10;i++)
{
printf("%d\t",pa[i]);
}
printf("\n");
printf("向上调整为大堆根并排序:\n");
int pa1[10]={6,7,89,5,12,4,3,2,35,8};
BuildHeap2(pa1,10);
Heapsort2(pa,10);
for(int i=0;i<10;i++)
{
printf("%d\t",pa[i]);
}
return 0;
}
7.归并排序
思想:假设初始序列为含有n个记录,则可以看成是n个有序的子序列,每个子序列的长度为1,然后进行两两归并,得到[n/2](向下取整)个长度为2或1的有序子序列;之后再进行两两归并,……,直到归并为长度为n的有序序列为止。
归并排序
初始关键字 | 49 | 38 | 65 | 97 | 76 | 13 | 27 |
第一趟归并结果 | (38,49) | (65,97) | (13,76) | 27 | |||
第二趟归并结果 | (38,49,65,97) | (13,27,76) | |||||
第三趟归并结果 | (13,27,38,49,65,76,97) |
归并排序
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#define MAXSIZE 11
typedef int ElemType;
int merge[MAXSIZE];
//合并两个有序子序列
void Merge(int merge[],int a[],int left,int right,int middle){
int i=left,j=middle+1;
int k=left;
while(i<=middle&&j<=right){
if(a[i]<=a[j]){
merge[k++]=a[i++];
}else{
merge[k++]=a[j++];
}
}
while(i<=middle)merge[k++]=a[i++];
while(j<=right)merge[k++]=a[j++];
i=0;
for(i=left;i<=right;i++)a[i]=merge[i];
}
//合并排序算法
void Mergesort(int a[],int left,int right){
if(left<right){
int middle=(left+right)>>1;
Mergesort(a,left,middle);
Mergesort(a,middle+1,right);
Merge(merge,a,left,right,middle);
}
}
void display(int a[],int n){
for(int i=0;i<n;i++){
printf("%d\t",a[i]);
}
}
int main(){
int array[MAXSIZE];
int n;
for(int i=0;i<MAXSIZE;i++){
array[i]=0;
}
printf("请输入待排关键字个数: ");
scanf("%d",&n);
printf("请输入关键字: ");
for(int i=0;i<n;i++){
scanf("%d",&array[i]);
}
Mergesort(array,0,n-1);
display(array,n);
return 0;
}
/*
49 38 65 97 76 13 27 49
*/
8.基数排序
基数排序
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<list>
using namespace std;
const int maxx=10;
int pa[maxx];
int n;
template<class T>
class Queue{
private:
list<T>q;
public:
int Size(){return q.size();}
int Empty(){return q.empty();}
void Clear(){return q.clear();}
void Push(const T&item){q.push_back(item);}
T Pop(){T item=q.front();q.pop_front();return item;}
};
void Radixsort(int n){
Queue<int>q[10];//10个箱子
int base=1,flag=1,k;
while(flag){
for(int i=0;i<n;i++){//分配
k=(pa[i]/base)%10;
q[k].Push(pa[i]);
}
base*=10;//晋级到更高一位
flag=0;//每趟收集前,令flag=0
int i=0;
for(k=0;k<10;k++){//收集
while(!q[k].Empty()){
pa[i++]=q[k].Pop();
if(pa[i-1]/base!=0&&flag==0){
flag=1;//还有更大的数位
}
}
}
}
}
int main(){
while(scanf("%d",&n)!=EOF){
for(int i=0;i<n;i++){
scanf("%d",&pa[i]);
}
Radixsort(n);
for(int i=0;i<n;i++){
cout<<pa[i]<<" ";
}
}
return 0;
}
9.各种排序算法比较总结
排序算法 比较
排序方法 | 平均时间 | 最坏情况 | 辅助空间 | 稳定性 |
直接插入排序 | O(n²) | O(n²) | O(1) | 稳定 |
折半插入排序 | O(n²) | O(n²) | O(1) | 稳定 |
希尔排序 | 不稳定 | |||
简单选择排序 | O(n²) | O(n²) | O(1) | 稳定 |
快速排序 | O(nlogn) | O(n²) | O(logn) | 不稳定 |
堆排序 | O(nlogn) | O(nlogn) | O(1) | 不稳定 |
归并排序 | O(nlogn) | G(nlogn) | O(n) | 稳定 |
基数排序 | O(d(n+rd)) | O(d(n+rd)) | O(rd) | 稳定 |
参考数据:《数据结构》清华大学