【排序算法】内部排序算法总结

  • 直接插入排序
  • 折半插入排序
  • 希尔排序
  • 起泡排序
  • 快速排序
  • 归并排序
  • 堆排序

下面的排序算法代码都是基于数组实现的。

1:直接插入排序(Straight Insertion Sort)

   直接插入排序是一种最简单的排序方法,它的基本操作是将一个记录插入到已排好序的有序表中,从而得到一个新的、记录数增1的有序表。

   一般情况下,第 i 趟直接插入排序的操作为:在含有 i-1 个记录的有序子序列 a[1..i-1] 中插入一个记录 r[i] 后,变成含有 i 个记录的的有序子序列 a[1..i] ;并且,和顺序查找类似,为了在查找插入位置的过程后移记录。整个排序过程为进行 n-1 趟插入,即:先将序列中的第1个记录看成是一个有序的子序列,然后从第2个记录起逐个进行插入,直至整个序列变成关键字非递减有序序列为止。

时间复杂度:Θ(n^2)    空间复杂度: Θ(1)

下面所有空间复杂度为Θ(1)的都是用来进行变量交换的。

/*
直接插入排序 
*/
#include
using namespace std;
const int maxn=1000+7;
int a[maxn];
int n;
void InsertSort()
{
    int i,j;
    for( i=2;i<=n;i++){
        if(a[i]a[0];j--){
                a[j+1]=a[j];        //记录后移
            }
            a[j+1]=a[0];          //插入到正确位置
        }
    }
    return ;
}
int main()
{
    while(scanf("%d",&n)==1){
        for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
        InsertSort();
        for(int i=1;i<=n;i++)
        printf("%d\n",a[i]);
    }
    return 0;
}

2:折半插入排序(Binary Insertion Sort)

折半插入排序就是在直接插入排序的查找过程进行改进,使用折半查找

时间复杂度:Θ(n^2)   空间复杂度:Θ(1)

/*
折半插入排序
*/
#include
using namespace std;
const int maxn=1000+7;
int n,a[maxn];
void BinaryInsertionSort()
{
    for(int i=2;i<=n;i++){
        a[0]=a[i];
        int low=1,high=i-1;
        while(low<=high){    //在 [low..high]中折半查找有序插入的位置
            int mid=low+(high-low)/2;     //折半
            if(a[0]=high+1;j--)
        a[j+1]=a[j];  //记录后移
        a[high+1]=a[0];  //插入
    }
    return ;
}
int main()
{
    while(scanf("%d",&n)==1){
        for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
        BinaryInsertionSort();
        for(int i=1;i<=n;i++)
        printf("%d\n",a[i]);
    }
    return 0;
}

3:希尔排序 (Shell's Sort)

希尔排序又称“缩小增量排序”(Diminishing Increment Sort)。基本思想:先将整个待排记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录“基本有序”时,再对全体记录进行一次直接插入排序。

特点:子序列的构成不是简单地“逐段分割”,而是将相隔某个“增量”的记录组成一个子序列。

注意:应使增量序列的值没有除1之外的公因子,并且1最后一个增量的值必须等于1。

当增量序列为 dlta[k] = 2^(t-k+1)-1时,希尔排序的时间复杂度为Θ(n^3/2),其中 t 为排序趟数,1<=k<=t

/*
希尔排序 又名缩小增量排序
*/
#include
using namespace std;
const int maxn=1000+7;
int a[maxn];
int n;
void ShellInsert(int* a,int dk)   //增量为dk 最后一个dk必须为1
{
    int i,j;
    for( i=dk+1;i<=n;i++){
        if(a[i]0&&a[0]

4:起泡排序(Bubble Sort)

一般地,第 i 趟起泡排序是从 a[1] 到 a[n-i+1] 依次比较相邻两个记录的关键字,并在“逆序”时交换相邻记录,其结果是这n-i+1个记录中关键字最大的记录被交换到第 n-i+1 的位置上。在起泡排序的过程中,关键字较小的记录好比水中的气泡逐趟向上漂浮,而关键字较大的记录好比石块往下沉,每一趟有一块“最大”的石头沉到水底。

时间复杂度:Θ(n^2)  空间复杂度:Θ(1)

#include
using namespace std;
const int maxn=1000+7;
int a[maxn];
int n;
void BubbleSort()
{
    for(int i=1;ia[j+1]){   //逆序交换
                int t=a[j];
                a[j]=a[j+1];
                a[j+1]=t;
            }
        }
    }
    return ;
}
int main()
{
    while(scanf("%d",&n)==1){
        for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
        BubbleSort();
        for(int i=1;i<=n;i++)
        printf("%d\n",a[i]);
    }
    return 0;
}

5:快速排序(Quick Sort)

快速排序是对起泡排序的一种改进。它的基本思想是,通过一趟排序将待排记录分割成独立的两部分,其中一部分记录的关键字均比另一部分记录的关键字小,则可分别对1这两部分记录继续进行排序,以达到整个序列有序。

假设待排序序列为 a[s..t] ,首先任意选取一个记录(通常可选第一个记录 a[s] 作为枢轴,将所有关键字较它小的记录都安置在它的位置之前,将所有关键字较它大的记录都安置在它的位置之后。由此可以该“枢轴”记录最后的位置作为分界线,将序列分割成两个子序列。

一趟快速排序的具体做法:附设两个指针 low 和 high ,它们的初值分别为 low 和 high ,设枢轴记录的关键字为 pivotkey ,则首先从 high 所指位置向前搜索找到第一个关键字小于 pivotkey 的记录和枢轴记录相互交换,然后从 low 所指位置起向后搜索,找到第一个关键字大于 pivotkey 的记录和枢轴记录相互交换,重复这两步直至 low = high 为止。

时间复杂度:Θ(n^2)    空间复杂度 :Θ(logn)

给出两种风格不同的代码:

1:两个函数,便于理解

#include
using namespace std;
const int maxn=1000+7;
int a[maxn];
int n;
int Partition(int* a,int low,int high)   //一次快排
{
    a[0]=a[low];
    int pivokey=a[low];
    while(low=pivokey) --high;
        a[low]=a[high];
        while(low

2:嵌套递归,代码量少

#include
using namespace std;
const int maxn=1000+7;
int a[maxn];
int n;
int QuickSort(int* a,int low,int high)
{
    a[0]=a[low];
    int pivokey=a[low];
	int tlow=low,thigh=high;     // 记录区间
    while(low=pivokey) --high;
        a[low]=a[high];
        while(low

6:归并排序(Merging Sort)

归并:将两个或两个以上的有序表组合成一个新的有序表。

假设初始序列含有 n 个记录,则可以看成 n 个有序的子序列,每个子序列长度为 1 ,然后两两归并,得到 n/2(向上取整)个长度为 2 或 1 的有序子序列;再两两归并.如此重复直至得到一个长度为 n 的有序序列为止,这种排序称为 2 - 路归并排序。

时间复杂度:Θ(nlogn)   空间复杂度:Θ(n)

下面直接给出嵌套递归的代码

/*
归并排序
*/
#include
#include
using namespace std;
const int maxn=1000+7;
int a[maxn];
int n;
void MergeSort(int* a,int low,int mid,int high )
{
    if(low

7:堆排序(Heap Sort)

堆的定义:把一个一维数组按顺序写成一棵完全二叉树。

大顶堆:完全二叉树中所有非终端结点的值不小于其左、右孩子结点的值。堆顶元素为序列中的最大值。

小顶堆:完全二叉树中所有非终端结点的值不大于其左、右孩子结点的值。堆顶元素为序列中的最小值。

堆排序:若在输出堆顶的最小值之后,使得剩余 n-1 个元素的序列重又建成一个堆,则得到 n 个元素中元素的次小值,如此反复执行,边能得到一个有序序列。

堆的调整:输出堆顶元素之后,此时根结点的左、右子树均为堆,则仅需自上至下进行调整即可。这个自堆顶至叶子的调整过程为“筛选”。

建堆:从一个无序序列建堆的过程就是一个反复筛选的过程。若将此序列看成是一个完全二叉树,则最后一个非终端结点是第 n/2(向下取整)个元素,由此“筛选”只需从第 n/2(向下取整)个元素开始。

时间复杂度:Θ(nlogn)    空间复杂度 :Θ(1)

/*
堆排序
*/
#include
using namespace std;
const int maxn=1000+7;
int heap[maxn];
int n;
void heapadjust(int* a,int s,int m)
{
    // 已知a[s..m]除关键字a[s]外均满足堆的定义 本函数调整a[s]
    //的关键字,使a[s..m]成为一个大顶堆
    int t=a[s];
    for(int j=2*s;j<=m;j++){   //眼key较大的孩子结点向下筛选
        if(j=a[j]) break;   //t 应该插入在位置s上
        a[s]=a[j];
        s=j;
    }
    a[s]=t;
}
void heapsort(int* a)
{
    for(int i=n/2;i>0;i--)   //把 a[1..n]建成大顶堆
    heapadjust(a,i,n);
    for(int i=n;i>1;i--){
        int t=a[1];    //将堆顶记录和当前未经排序子序列a[1..i]中
        a[1]=a[i];    //最后一个记录相互交换
        a[i]=t;
        heapadjust(a,1,i-1);   //将a[1..i-1]重新调整为大顶堆
    }
}
int main()
{
    while(scanf("%d",&n)==1){
        for(int i=1;i<=n;i++)
        scanf("%d",&heap[i]);
        heapsort(heap);
        for(int i=1;i<=n;i++)
        printf("%d\n",heap[i]);
    }
    return 0;
}

 

你可能感兴趣的:(排序)