- 概述
分类
内排序:数据量少,在内存中
插入
交换
选择
归并
外排序:数据量大,内外存需要交换数据
按复杂度分
简单算法:冒泡、简单选择、直接插入
改进算法:希尔、堆、归并、快速
稳定性:两个一样的数字,排序后相对位置不变,则稳定
- 冒泡排序
思想:从上到下/从下到上,与相邻元素比较
改进:设置有无交换的标志位
复杂度:最好/最坏
稳定:arr[j]严格>arr[j+1]时,才做交换

void BubbleSort(vector<int> arr)
{
    int sz=arr.size();
    bool flag=true;
    for(int i=0;i<sz-1&&flag;i++)
    {
        flag=false;//没交换
        for(int j=sz-2;j>=i;j--)
        {
            if(arr[j]>arr[j+1])
            {
                swap(arr[j],arr[j+1]);
                flag=true;
            }
        }
    }
}

- 简单选择排序
思想:通过n-i次比较,从n-i+1中选出最小的,与位置i的交换
复杂度

void SelectSort(vector<int> arr)
{
    int sz=arr.size();
    int min;
    for(int i=0;i<sz-2;i++)
    {
        min=i;
        for(j=i+1;j<sz-1;j++)
        {
            if(arr[min]>arr[j])
                min=j;
        }
        if(i!=min) swap(arr[i],arr[min]);
    }                   
}

- 直接插入排序
思想:扑克牌 将一个元素插入已经排好序的序列中
复杂度
稳定

void InsertSort(vector<int> arr)
{
    int sz=arr.size();
    for(int i=1;i<sz;i++)
    {
        int tmp=arr[i];//摸下一张牌
        for(int j=i;j>0&&arr[j-1]>tmp;j--)
            arr[j]=arr[j-1];//为新牌找位置,比它大的都向后挪
        arr[j]=tmp;//新牌落座
    }
}

- 希尔排序
思想:定义增量序列(最后一定是1);对每个增量序列进行排序;基本有序;跳跃;
更多增量序列
复杂度
不稳定

void ShellSort(vector<int> arr)
{
    int N=arr.size();
    for(int D=N/2;D>0;D/=2)//增量序列
    {
        for(int i=D;i<N;i++)//插入排序
        {
            int tmp=arr[i];
            for(int j=i;j>=D&&arr[j-D]>tmp;j-=D)
                arr[j]=arr[j-D];
            arr[j]=tmp;
        }
    }
}

- 堆排序(选择排序的改进)
思路:构建最大堆;堆顶与堆尾元素交换;删去堆尾后重建最大堆;重复
(利用堆的性质:完全二叉树;编号;父/子节点编号)
数组编号从0开始,堆从1开始
复杂度(最好/坏/平均一样)
不稳定
不适合待排序序列个数少的

// 递归方式构建大根堆(len是arr的长度,index是第一个非叶子节点的下标)
void adjust(vector<int> &arr, int len, int index)
{
    int left = 2*index + 1; // index的左子节点
    int right = 2*index + 2;// index的右子节点

    int maxIdx = index;//maxIdx保存根、左、右最大的下标
    if(left<len && arr[left] > arr[maxIdx])     maxIdx = left;
    if(right<len && arr[right] > arr[maxIdx])     maxIdx = right;

    if(maxIdx != index)
    {
        swap(arr[maxIdx], arr[index]);
        adjust(arr, len, maxIdx);//从此次最大子节点的位置递归
    }

}
// 堆排序
void HeapSort(vector<int> &arr, int size)
{
    // 构建大根堆(从最后一个非叶子节点向上)
    for(int i=size/2 - 1; i >= 0; i--)
    {
        adjust(arr, size, i);
    }
    // 调整大根堆
    for(int i = size - 1; i >= 1; i--)
    {
        swap(arr[0], arr[i]);  // 将当前最大的放置到数组末尾
        adjust(arr, i, 0);     // 去除最后位置的元素,将未完成排序的部分继续进行堆排序
    }
}

- 归并排序
思想:有序子列的归并
复杂度:最好/坏/平均一样
空复:常用于外排序
稳定:两两比较,无跳跃

递归:
void Merge(int a[], int b[], int left, int mid, int right)//归并到a
{
    int i=left,j=mid+1;
    int k=left;//存放结果数组的初始位置
    while(i<=mid&&j<=right)
    {
        if(a[i]<=a[j]) b[k++]=a[i++];
        else b[k++]=a[i++];
    }
    while(i<=mid) 
        b[k++]=a[i++];
    while(j<=right)
        b[k++]=a[j++];
    for(i=left;i<right-left+1;i++)
        a[i]=b[i];
}
void Msort(int a[], int b[], int l, int r)
{
    if (l >= r) //必须有等号
        return;
    int m = (l + r)/2;
    Msort(a, b, l, m);
    Msort(a, b, m + 1, r);
    Merge(a, b, l, m, r);
}
void MergeSort(int a[], int len)
{
    int *b = new int[len];
    //int *b;
    //b=malloc(len*sizeof(int));
    if(!b)
    {
        Error("空间不足");
    }
    else
    {
        Msort(a, b, 0, len - 1);
        delete[] b;
        //free(b);
    }
}
//非递归:
void MergePass(int a[],int b[],int len,int sz)
{
    int i;    
    for(i=0;i<=len-2*sz;i+=2*sz)
        Merge1(a,b,i,i+sz,i+2*sz-1);//将a归并到b
    if(i+sz<len)//归并最后两个子列
        Merge1(a,b,i,i+sz,len-1);
    else//最后只剩一个子列
        for(int j=i;j<len;j++)
            b[j]=a[j];
}
void MergeSort(int a[], int len)
{
    int sz=1;//初始化子序列长度
    int *b = new int[len];
    //int *b;
    //b=malloc(len*sizeof(int));
    if(!b)
    {
        Error("空间不足");
    }
    else
    {
        while(sz<len)
        {
            MergePass(a,b,len,sz);
            sz*=2;
            MergePass(b,a,len,sz);
            sz*=2;
        }        
        delete[] b;
        //free(b);
    }
}
  • 稳定:冒泡、直接插入、归并
    不稳定:简单选择、希尔、堆、快速