排序总结·八大排序

插入排序

void ImprovedInsertSort(int array[], int n)
{
    int TempRecord;
    for (int i = 1; i < n; i++)
    {
        TempRecord = array[i];
        int j = i - 1;
        //从i开始往前寻找记录i的正确位置
        while ((j >= 0) && (TempRecord < array[j]))
        {
            array[j + 1] = array[j];
            j = j - 1;
        }
        //此时j后面就是记录i的正确位置,回填
        array[j + 1] = TempRecord;
    }
}

稳定
空间代价:0(1)

时间代价:
最佳情况:n-1次比较,2 (n -1)次移动,0(n)
最差情况:0(n^2) 比较次数:0(n^2),移动次数:0(n^2)
平均情况:0(n^2)

Shell排序

相当于利用了插入排序对较小数组较快速的优点以及正序数组较快速的特点

void ModInsSort(int array[], int n, int delta)
{
    for (int i = delta; i < n; i += delta)
    {
        for (int j = i; j >= delta; j -= delta)
        {
            if (array[j] < array[j - delta])
            {
                swap(array, j, j - delta);
            }
            else
            {
                break;
            }
        }
    }
}

void ShellSort(int array[], int n)
{
    //增量delta每次除以2递减
    for (int delta = n / 2; delta > 0; delta /= 2)
    {
        for (int i = 0; i < delta; i++)
        {//分别对delta个子序列进行插入排序,&array[i]传的是地址,数组总长度为n-1
            ModInsSort(&array[i], n - i, delta);
        }
    }
    //如果增量序列不能保证最后一个delta间距为1
    //可以安排下面这个扫尾性质的插入排序
    // ModInsSort(array,n,1);
}

MyBlog:Shell排序

选择排序

直接选择排序

void SelectSort(int array[], int n)
{//依次选出第i小的数,即剩余数中最小的那个
    for (int i = 0; i < n; i++)
    {//首先假设数i就是最小的
        int Smallest = i;
        for (int j = i + 1; j < n; j++)
        {//开始向后扫描所有剩余数,如果发现更小的数,记录他的位置
            if (array[i] < array[Smallest])
            {
                Smallest = j;
            }
        }
        //将第i小的数放在数组中第i个位置
        swap(array,i,Smallest);
    }
}

不稳定
空间代价:0(1)

时间代价:
比较次数:0(n^2)
交换次数:n-1
总时间代价:0(n^2)

堆排序

void sort(int array[], int n)
{
    //建堆
    MaxHeap<int> max_heap = MaxHeap<int>(array, n, n);
    for (int i = 0; i < n - 1; i++)
    {//算法操作n-1次,最小元素不需要出堆
        max_heap.RemoveMax();
    }//依次找出剩余数中的最大数,即堆顶
}

建堆:0(n)
删除堆顶:0(log(n))
一次建堆,n次删除堆顶
总时间代价为0(n*log(n))
空间代价为0(1)
MyBlog:堆排序

交换排序

冒泡排序

void BubbleSort(int array[], int n)
{
    for (int i = 0; i < n - 1; i++)
    {
        bool NoSwap = true;//是否发生交换的标志
        for (int j = n - 1; j > i; i++)
        {
            if (array[j] < array[j - 1])
            {//判断是否逆置
                swap(array, j, j - 1);//交换逆置对
                NoSwap = false;//发生交换,标志变为假
            }
            if (NoSwap)
            {//没发生交换则已完成排序
                return;
            }
        }
    }
}

稳定
空间代价:0(1)

时间代价
比较次数最少:0(n)
比较次数最多:0(n^2)
交换次数最多:0(n^2)
交换次数最少:0
交换次数平均:0(n^2)

快速排序

int SelectPivot(int left, int right)
{//选择轴值的函数
    return (left + right) / 2;//此处选择中间记录
}

int Partition(int array[], int left, int right)
{//分割函数,分割后轴值已到达正确位置
    int l = left;
    int r = right;
    int TempRecord = array[r];//保存轴值
    while (l != r)//l,r不断向中间移动,直到相遇
    {//l指针向右移动,直到找到一个大于轴值的记录
        while (array[l] <= TempRecord && r > l)
        {
            l++;
        }
        if (l < r)
        {//未相遇,将逆置元素换到右边空位
            array[r] = array[l];
            r--;//r指针向左移动一步
        }
        //r指针向左移动,直到找到一个小于轴值的记录
        while (array[r] >= TempRecord && r > l)
        {
            r--;
        }
        if (l < r)
        {//未相遇,将逆置元素换到左空位
            array[l] = array[r];
            l++;//l指针向右移动一步
        }
    }
    array[l] = TempRecord;//把轴值回填到分界位置l上
    return l;//返回分界位置l
}

void QuickSort(int array, int left, int right)
{
    if (right <= left)
    {
        return;
    }
    int pivot = SelectPivot(left, right);//选择轴值
    swap(array, pivot, right);//轴值放到数组末端
    pivot = Partition(&array, left, right);//分割后轴值正确
    QuickSort(array, left, pivot - 1);//左子序列递归快排
    QuickSort(array, pivot, right);//右子序列递归快排
}

时间代价:,长度为n的序列,时间为T(n)
选择轴值时间为常数
分割时间为c*n
分割后长度分别为in-i-1
左右子序列T(i)T(n-i-1)
求解递推方程
T(n)=T(i)+T(n-1-i)+c*n

最差情况:
时间代价:0(n^2)
空间代价:0(n)
最佳情况:
时间代价:0(n*log(n))
空间代价:0(log(n))
平均情况:
时间代价:0(n*log(n))
空间代价:0(log(n))
MyBlog:快速排序

归并排序

void Merge(int array[], int Temparray[], int left, int right, int middle)
{
    for (int j = left; j <= right; j++)
    {//将数组存入临时数组
        Temparray[j] = array[j];
    }
    int index1 = left;//左边子序列的起始位置
    int index2 = middle + 1;//右边子序列的起始位置
    int i = left;//从左开始归并
    while (index1 <= middle && index2 <= right)
    {//取较小者插入合并数组中
        if (Temparray[index1] <= Temparray[index2])
        {
            array[i++] = Temparray[index1++];
        }
        else
        {
            array[i++] = Temparray[index2++];
        }
    }
    while (index1 <= middle)
    {//只剩左序列,可以直接复制
        array[i++] = Temparray[index1++];
    }
    while (index2 <= right)
    {//与上个循环互斥,复制右序列
        array[i++] = Temparray[index2++];
    }
}

void MergeSort(int array[], int Temparray[], int left, int right)//两路归并排序
{//array为待排序数组,left,right两端
    int middle;
    if (left < right)
    {//序列中只有0或1个记录,不用排序
        middle = (left + right) / 2;
        MergeSort(array, Temparray, left, middle);//左一半
        MergeSort(array, Temparray, middle + 1, right);//右一半
        Merge(array, Temparray, left, right, middle);//归并
    }
}

优化后的归并排序

void ModMerge(int array[], int Temparray[], int left, int right, int middle)
{
    int index1, index2;//两个子序列的起始位置
    for (int i = left; i <= middle; i++)
    {//复制左边的左序列
        Temparray[i] = array[i];
    }
    for (int j = 1; j <= right - middle; j++)
    {//颠倒复制右序列
        Temparray[right - j + 1] = array[j + middle];
    }
    index1 = left;
    index2 = right;
    for (int k = left; k <= right; k++)
    {
        if (Temparray[index1] <= Temparray[index2])
        {
            array[k] = Temparray[index1++];
        }
        else
        {
            array[k] = Temparray[index2--];
        }
    }
}

void ModMergeSort(int array[], int Temparray[], int left, int right)
{
    int middle;
    if (left - right + 1 > THRESHOLD)
    {//长序列递归
        middle = (left + right) / 2;
        ModMergeSort(array, Temparray, left, middle);//左一半
        ModMergeSort(array, Temparray, middle + 1, right);//右一半
        ModMerge(array, Temparray, left, right, middle);//归并
    }
    else
    {//小序列插入排序
        InsertSort(&array[left], right - left + 1);
    }
}

空间代价:0(n)
划分时间、排序时间、归并时间:T(n) = 2T(n/2) +c*nT(1) = 1
归并排序总时间代价也为:0(n* log(n))
不依赖与原始数组的输入情况,最大最小以及平均时间代价均为0(n*log(n))

桶排序

void BucketSort(int array[], int n, int max)
{
    int *Temparray = new int[n];//临时数组
    int *count = new int[max];//桶容量计数器
    for (int i = 0; i < n; i++)
    {//把序列复制到临时数组
        Temparray[i] = array[i];
    }
    for (int i = 0; i < max; i++)
    {//所有计数器初始都为0
        count[i] = 0;
    }
    for (int i = 0; i < n; i++)
    {//统计每个取值出现的次数
        count[array[i]]++;
    }
    for (int i = 1; i < max; i++)
    {//统计小于等于i的元素个数
        count[i] = count[i - 1] + count[i];//c[i]记录i+1的起址
    }
    for (int i = n - 1; i >= 0; i--)
    {//尾部开始,保证稳定性
        array[--count[Temparray[i]]] = Temparray[i];
    }
}

数组长度为n,所有记录区间[0,m)
时间代价:
统计计数:0(n+m),输出有序序列时循环n次
总的时间代价为0(m+n)
适用于m相对于n很小的情况
空间代价:
m个计数器,长度为n的临时数组,0(m+n)
稳定

基数排序

静态数组型

void RadixSort(int array[], int n, int d, int r)
{
    int *Temparray = new int[n];
    int *count = new int[r];
    int Radix = 1;//模进位,用于取array[i]的第i位
    for (int i = 0; i <= d; i++)
    {//对于第i个排序码分配
        for (int j = 0; j < r; j++)
        {//初始计数器均为0
            count[j] = 0;
        }
        for (int j = 0; j < n; j++)
        {//统计每桶记录数
            int k = (array[j] / Radix) % r;//取第i位
            count[k]++;//相应计数器加1
        }
        for (int j = 1; j < r; j++)
        {//给桶划分下标界
            count[j] = count[j - 1] + count[j];
        }
        for (int j = n - 1; j >= 0; j--)
        {//从数组尾部收集
            int k = (array[j] / Radix) % r;//取第i位
            count[k]--;//桶剩余量计数器减1
            Temparray[count[k]] = array[j];//入桶
        }
        for (int j = 0; j < n; j++)
        {//内容复制回array中
            array[j] = Temparray[j];
        }
        Radix *= r;//修改模Radix
    }
}

空间代价:
临时数组,n
r个计数器
总空间代价0(n+r)

时间代价:
桶式排序:0(n+r)
d次桶式排序
0(d * (n + r))

链表型

struct Node
{//节点类
public:
    int key;//节点对关键码值
    int next;//下一节点在数组中的下标
};

class StaticQueue
{//静态队列类
public:
    int head;
    int tail;
};

void Distribute(Node *array, int first, int i, int r, StaticQueue *queue)
{
    for (int j = 0; j < r; j++)
    {
        queue[j].head = -1;
    }
    int curr = first;
    while (curr != -1)
    {//对整个静态链进行分配
        int k = array[curr].key;
        for (int a = 0; a < i; a++)
        {//取第i位排序码数字k
            k = k / r;
        }
        k = k % r;
        if (queue[k].head == -1)
        {//把数据分配到第k个桶中
            queue[k].head = curr;
        }
        else
        {
            array[queue[k].tail].next = curr;
        }
        queue[k].tail = curr;
        curr = array[curr].next;//curr移动,继续分配
    }
}

void Collect(Node *array, int &first, int r, StaticQueue *queue)
{
    int k = 0;//已收集到的最后一个记录
    while (queue[k].head == -1)
    {//找到第一个非空队
        k++;
    }
    first = queue[k].head;
    int last = queue[k].tail;
    while (k < r - 1)
    {//继续收集下一个非空队列
        k++;
        while (k < r - 1 && queue[k].head == -1)
        {
            k++;
        }
        if (queue[k].head != -1)
        {//试探下一个队列
            array[last].next = queue[k].head;
            last = queue[k].tail;//最后一个为序列的尾部
        }
    }
    array[last].next = -1;//收集完毕
}

void AddrSort(Node *array, int n, int first)
{
    int j = first;//j待处理数据下标
    Node TempRec;
    for (int i = 0; i < n - 1; i++)
    {//循环,每次处理第i个记录
        TempRec = array[j];//暂存第i个的记录array[j]
        swap(array[i], array[j]);
        array[i].next = j;//next链要保留调换轨迹j
        j = TempRec.next;//j移动到下一位
        while (j <= i)
        {//j比i小,则是轨迹,顺链找
            j = array[j].next;
        }
    }
}

void RadixSort(Node *array, int n, int d, int r)
{
    int first = 0;//first指向第一个记录
    StaticQueue *queue = new StaticQueue[r];
    for (int i = 0; i < n - 1; i++)
    {//初始化静态指针域
        array[i].next = i + 1;
    }
    array[n - 1].next = -1;//链尾next为空
    //对第i个排序码进行分配和收集,一共d趟
    for (int i = 0; i < d; i++)
    {
        Distribute(array, first, i, r, queue);
        Collect(array, first, r, queue);
    }
    delete[]queue;
    AddrSort(array, n, first);//整理后,按下标有序
    for (int i = 0; i < 9; i++)
    {
        cout << array[i].key << endl;
    }
}

MyBlog:基数排序

索引排序

交换指针,减少交换记录的次数

void AddrSort(int array[], int n)
{//插入排序的索引地址排序版本
    int *IndexArray = new int[n], TempIndex;
    int TempRec;//只需要一个临时空间
    for (int i = 0; i < n; i++)
    {
        IndexArray[i] = i;
    }
    for (int i = 1; i < n; i++)
    {//依次插入第i个记录
        for (int j = i; j > 0; j--)
        {//依次比较,发现逆置就交换
            if (array[IndexArray[j]] < array[IndexArray[j - 1]])
            {
                swap(IndexArray, j, j - 1);
            }
            else
            {//此时i前面记录已排序
                break;
            }
        }
    }
    for (int i = 0; i < n; i++)
    {//调整为按下标有序
        int j = i;
        TempRec = array[i];
        while (IndexArray[j] != i)
        {
            int k = IndexArray[j];
            array[j] = array[k];
            IndexArray[j] = j;
            j = k;
        }
        array[j] = TempRec;
        IndexArray[j] = j;
    }
}

时间代价

最大时间平均时间最小时间辅助空间代价稳定性
直接插入排序0(n^2)0(n^2)0(n)0(1)稳定
冒泡排序0(n^2)0(n^2)0(n)0(1)稳定
选择排序0(n^2)0(n^2)0(n^2)0(1)不稳定
Shell排序(3)0(n^3/2)0(n^3/2)0(n^3/2)0(1)不稳定
快速排序0(n^2)0(n*log(n))0(n*log(n))0(log(n))不稳定
归并排序0(n*log(n))0(n*log(n))0(n*log(n))0(n)稳定
堆排序0(n*log(n))0(n*log(n))0(n*log(n))0(1)不稳定
桶式排序0(n+m)0(n+m)0(n+m)0(n+m)稳定
基数排序0(d*(n+r))0(d*(n+r))0(d*(n+r))0(n+r)稳定
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zhj12399

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值