C++排序算法代码汇总

排序算法稳定性:两个相同的元素排序前后的相对位置关系不会发生改变。

复杂度比较

算法平均时间复杂度最好情况最坏情况空间复杂度稳定性
冒泡排序O(N^2)O(N^2)O(N^2)O(1)稳定
插入排序O(N^2)O(N)O(N^2)O(1)稳定
选择排序O(N^2)O(N^2)O(N^2)O(1)稳定
希尔排序O(N ^ 3/2 ) O(N^2)O(1)不稳定
堆排序O(NlogN)O(NlogN)O(NlogN)O(1)不稳定
归并排序O(NlogN)O(NlogN)O(NlogN)O(N)稳定
快速排序O(NlogN)O(NlogN)O(N^2)O(logN)不稳定

冒泡排序
排序过程
  1. 将第一个元素与第二个元素比较大小,如果第一个元素大于第二个元素则调换他们两的位置;
  2. 比较第二个元素和第三个元素的大小,如果第二个元素大于第三个元素则调换他们两的位置;
  3. 依次类推,进行两两元素的比较和交换,最终最大的元素排在了最后面;
  4. 重复1到3过程,直到所有元素都排序。
图片演示

这里写图片描述

//冒泡排序
//平均时间复杂度:O(N^2)
//最坏情况复杂度:O(N^2)
//空间复杂度:O(1)
//稳定排序
void bubblesort(vector<int>& a)
{
    int n = a.size();
    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < n - 1 - i; j++)
        {
            if (a[j] > a[j+1])
                swap(a[j], a[j+1]);
        }
    }
}

//冒泡排序升级版1
//设置一个标记来标志一趟比较是否发生交换
//如果没有发生交换,则数组已经有序
void bubblesort2(vector<int>& a)
{
    int n = a.size();
    bool flag;
    for (int i = 0; i < n; i++)
    {
        flag = false;
        for (int j = 0; j < n - 1 - i; j++)
        {
            if (a[j] > a[j+1])
            {
                swap(a[j], a[j+1]);
                flag = true;
            }
        }
        if (!flag)
            break;
    }
}

//冒泡排序优化二
//用一个变量记录下最后一个发生交换的位置,后面没有发生交换的已经有序
//所以可以用这个值来作为下一次比较结束的位置
void bubblesort3(vector<int>& a)
{
    int n = a.size();
    int flag = n;
    int stop_pos;
    for (int i = 0; i < n; i++)
    {
        stop_pos = flag - 1;
        flag = 0;
        for (int j = 0; j < stop_pos; j++)
        {
            if (a[j] > a[j+1])
            {
                swap(a[j], a[j+1]);
                flag = j + 1;
            }
        }
    }
}


插入排序

  1. 对于第K个元素,将该元素的值存储在零时变量中,比较第前一个元素与该元素的大小,如果大于该元素就将前一个元素往后移动一步;
  2. 比较前面第二个元素与该元素的大小,如果大于该元素就将前第二个元素往后移动一步;
  3. 重复上述过程直到找到小于等于原来第K个元素(保存在零时变量中)的位置,并将第K个元素插入到这个元素的后面。或者找不到小于等于第K个元素的位置,就将原来第K个元素插入到数组的首地址。
图片演示

这里写图片描述

//插入排序
//平均时间复杂度:O(N^2)
//最坏情况复杂度:O(N^2)
//最好情况复杂度:O(N)
//空间复杂度:O(1)
//最多需要n(n−1)/2次比较
//最少需要n−1次比较
//稳定排序
void insertsort(vector<int>& a)
{
    int n = a.size();
    for (int i = 1; i < n; i++)
    {
        int insert_num = a[i], j;
        for (j = i - 1; j >= 0; j--)
        {
            if (a[j] > insert_num)
                a[j + 1] = a[j];
            else
                break;
        }
        a[j + 1] = insert_num;
    }
}

选择排序

1、每一次从后面选择出一个最小的值(swap_pos),替换到前面来(i)。


//选择排序
//平均时间复杂度 O(n^2)
//最坏时间复杂度 O(n^2)
//最好时间复杂度 O(n^2)
//空间复杂度 O(1)
//我这个写法 是稳定排序
void select_sort(vector<int>& vt)
{
    for (int i = 0; i < vt.size() - 1; i ++)
    {
        int swap_pos = i;
        for (int j = i + 1; j < vt.size(); j++)
        {
            if (vt[swap_pos] > vt[j])
            {
                swap_pos = j;
            }
        }
        
        if (swap_pos != i)
        {
            swap(vt[swap_pos], vt[i]);
        }
    }
}

希尔排序

希尔排序是在插入排序的基础上进行发展的,通过一个希尔增量先排序一定间隔的数据。

排序过程
  1. 插入排序每次与前面一个比较,然后再往前一个,而希尔排序每次往前K个;
  2. 当增量为1的时候,希尔排序与插入排序就完全是一样的过程;
  3. 所以代码也很好实现,将插入排序中增1的地方改为增K就行。
图片演示
初始81941196123517952858
第一趟5排序后35171128128194959658
第二趟3排序后28121135178158959694
第三趟1排序后11121728355881949596
复杂度分析

希尔排序的时间复杂度比较复杂,选用不同的希尔增量也会导致复杂度不同。

//希尔排序
//最坏情况复杂度:O(N^2)
//不稳定排序
void shellsort(vector<int>& a)
{
    int n = a.size();
    for (int increment = n / 2; increment > 0; increment /= 2)
    {
        for (int i = increment; i < n; i++)
        {
            int insert_num = a[i], j;
            for (j = i - increment; j >= 0; j -= increment)
            {
                if (a[j] > insert_num)
                    a[j + increment] = a[j];
                else
                    break;
            }
            a[j + increment] = insert_num;
        }
    }
}


堆排序

排序过程
  1. 建立最大堆,建堆的过程是从N/2的位置开始,将父节点与子节点比较,如果子节点大于父节点则交换。为什么是N/2,是因为堆中树叶的个数是N/2。
  2. 从堆中删除堆顶元素,对于最大堆而言,堆顶元素也就是最大元素。每删除一个堆顶元素,就将堆顶元素放在数组的后面,因为每删除一个就出现一个空位,所以数组后面是有地方存放的。
  3. 进行N-1次的删除以后,整个数组就是排序的状态了。
图片演示


这里写图片描述

//堆排序
//建堆的平均时间是:O(N)
//建堆的最坏情况是:O(NlogN)
//删除元素的时间是:O(logN)
//整个排序平均时间复杂度:O(N+NlogN)=O(NlogN)
//最坏情况复杂度:O(NlogN)
//不稳定排序

//建立一个大顶堆O(n),要求就是 把最大的元素 移动到堆顶 也就是a[0]
void make_heap(vector<int>& a, int size) //size的当前堆的大小,也就是数组的前size个数
{
    for (int i = size - 1; i > 0; i--)
    {
        if (i % 2 && a[i] > a[(i - 1) / 2])//奇数
            swap(a[i], a[(i - 1) / 2]);
        else if (i % 2 == 0 && a[i] > a[(i - 2) / 2])//偶数
            swap(a[i], a[(i - 2) / 2]);
    }
}
void heapsort(vector<int>& a)
{
    int n = a.size();
    while (n)
    {
        make_heap(a, n); //每次把新的最大元素移到堆顶,也就是a[0]
        n--;
        swap(a[0], a[n]); //然后把当前最大移动到后面来作为排好序的元素
    }
}


归并排序

排序过程
  1. 将数组N从中间分成两个数组N1和N2;
  2. 将N1和N2分别递归用归并排序来排序。
  3. 归并N1与N2。
图片演示

这里写图片描述

//归并排序
//平均时间复杂度:O(NlogN)
//稳定排序
vector<int> mergeHelper(vector<int> &a, int left, int right)
{
    if (left == right) return vector<int> (1, a[left]);
    int mid = (right - left) / 2 + left;
    vector<int> l = mergeHelper(a, left, mid);
    vector<int> r = mergeHelper(a, mid + 1, right);
    //merge
    vector<int> ret;
    int ll = 0, rr = 0;
    while (ll < l.size() && rr < r.size())
    {
        if (l[ll] <= r[rr])     ret.push_back(l[ll++]);
        else                    ret.push_back(r[rr++]);
    }
    while (ll < l.size()) ret.push_back(l[ll++]);
    while (rr < r.size()) ret.push_back(r[rr++]);
    return ret;
}

void mergesort(vector<int>& a)
{
    a = mergeHelper(a, 0, a.size() - 1);
}



快速排序

排序过程
  1. 选择一个枢纽元,可以选择首,尾,中三个数的中位数作为枢纽元;
  2. 将枢纽元的为止与数组的尾地址进行交换;
  3. 定义两个指针,P1指向数组首地址,P2指向数组倒数第二个位置,P1所指元素的值与枢纽元比较,如果小于枢纽元则后移一位,如果大于就停下来。P1所指元素的值与枢纽元比较,如果大于枢纽元则前移一位,如果小于就停下来;
  4. 交换P1和P2所指向的元素;
  5. 重复3和4直到P1大于P2;
  6. 对数组的分割过程同样采用递归的方法。
快排是必须背住的排序!
//快速排序
//平均时间复杂度:O(NlogN)
//最坏情况复杂度:O(N^2)
//不稳定排序
void quicksortHelper(vector<int>& a, int start, int end)
{
    if (start >= end) return;
    int l = start, r = end;
    int pivot = a[(end - start) / 2 + start];
    while (l <= r)
    {
        while (l <= r && a[r] > pivot)  r--;
        while (l <= r && a[l] < pivot)  l++;
        if (l <= r) swap(a[l++], a[r--]);
    }
    quicksortHelper(a, start, r);
    quicksortHelper(a, l, end);
}
void quicksort(vector<int>& a)
{
    quicksortHelper(a, 0, a.size() - 1);
}
//快排的最差时间复杂度为O(n²)
//通常出现在选择的轴值(pivot)不能将数组划分为两个长度相等的子数组的时候
//一个较好的办法是“三数取中”,查看当前数组的第一个、中间一个和最后一个位置的数组,取其中位数,以此来降低轴值选择得不好的可能性。
int findmiddle(int a, int b, int c)
{
    if (a >= b && a <= c)
        return a;
    else if (b >= a && b <= c)
        return b;
    else
        return c;
}
void quicksortHelper(vector<int>& a, int start, int end)
{
    if (start >= end) return;
    int l = start, r = end;
    int pivot = findmiddle(a[start], a[end], a[(end - start) / 2 + start]);
    while (l <= r)
    {
        while (l <= r && a[r] > pivot)  r--;
        while (l <= r && a[l] < pivot)  l++;
        if (l <= r) swap(a[l++], a[r--]);
    }
    quicksortHelper(a, start, r);
    quicksortHelper(a, l, end);
}
void quicksort(vector<int>& a)
{
    quicksortHelper(a, 0, a.size() - 1);
}







  • 41
    点赞
  • 203
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值