十大排序算法C++实现

总结整理排序算法:

Nothing to say~~

正文开始@Assassin

排序算法概览:

排序算法平均时间复杂度最差时间复杂度空间复杂度数据对象稳定性
冒泡排序O(n2)O(n2)O(1)稳定
选择排序O(n2)O(n2)O(1)数组不稳定、链表稳定
插入排序O(n2)O(n2)O(1)稳定
快速排序O(n*log2n)O(n2)O(log2n)不稳定
堆排序O(n*log2n)O(n*log2n)O(1)不稳定
归并排序O(n*log2n)O(n*log2n)O(n)稳定
希尔排序O(n*log2n)O(n2)O(1)不稳定
计数排序O(n+m)O(n+m)O(n+m)稳定
桶排序O(n)O(n)O(m)稳定
基数排序O(k*n)O(n2)稳定
  • 均按从小到大排列
  • k:代表数值中的"数位"个数
  • n:代表数据规模
  • m:代表数据的最大值减最小值

1. 冒泡排序:

(无序区,有序区)。从无序区通过交换找出最大元素放到有序区前端。
冒泡排序思路:

  • 1. 比较相邻的元素。如果第一个比第二个大,就交换他们两个。
  • 2. 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。
  • 3. 针对所有的元素重复以上的步骤,除了最后一个。
  • 4. 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
// 冒泡排序
void BubbleSort(vector<int> &v)
{
    int len = v.size();
    for (int i = 0; i < len - 1; ++i)
        for (int j = 0; j < len - 1 - i; ++j)
            if (v[j] > v[j + 1])
                swap(v[j], v[j + 1]);
}

// 模板实现冒泡排序
template<typename T> //整数或浮点数皆可使用,若要使用class时必须设定大于(>)的运算子功能
void bubble_sort(T arr[], int len) 
{
	for (int i = 0; i < len - 1; i++)
	{
		for (int j = 0; j < len - 1 - i; j++)
		{
			if (arr[j] > arr[j + 1])
			{
				swap(arr[j], arr[j + 1]);
			}
		}
    }
}

// 冒泡排序(改进版)
void BubbleSort_orderly(vector<int>& v) 
{
	int len = v.size();
	bool orderly = false;
	for (int i = 0; i < len - 1 && !orderly; ++i) 
    {
		orderly = true;
		for (int j = 0; j < len - 1 - i; ++j) 
        {
			if (v[j] > v[j + 1]) {  // 从小到大
				orderly = false;	// 发生交换则仍非有序
				swap(v[j], v[j + 1]);
			}
		}
	}
}

2. 选择排序:

(有序区,无序区)。在无序区里找一个最小的元素跟在有序区的后面。对数组:比较得多,换得少。
选择排序思路:

  • 1. 在未排序序列中找到最小(大)元素,存放到排序序列的起始位置
  • 2. 从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾
  • 3. 以此类推,直到所有元素均排序完毕
// 选择排序
void SelectionSort(vector<int> &v)
{
    int min, len = v.size();
    for (int i = 0; i < len - 1; ++i)
    {
        min = i;
        for (int j = i + 1; j < len; ++j)
        {
            if (v[j] < v[min])
            { // 标记最小的
                min = j;
            }
        }
        if (i != min) // 交换到前面
            swap(v[i], v[min]);
    }
}

// 模板实现
template <typename T>
void Selection_Sort(std::vector<T> &arr)
{
    int len = arr.size();
    for (int i = 0; i < len - 1; i++)
    {
        int min = i;
        for (int j = i + 1; j < len; j++)
            if (arr[j] < arr[min])
                min = j;
        if (i != min)
            std::swap(arr[i], arr[min]);
    }
}

3. 插入排序:

(有序区,无序区)。把无序区的第一个元素插入到有序区的合适的位置。对数组:比较得少,换得多。
插入排序思路:

  • 1. 从第一个元素开始,该元素可以认为已经被排序
  • 2. 取出下一个元素,在已经排序的元素序列中从后向前扫描
  • 3. 如果该元素(已排序)大于新元素,将该元素移到下一位置
  • 4. 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置
  • 5. 将新元素插入到该位置后
  • 6. 重复步骤2~5
// 插入排序
void InsertSort(vector<int> &v)
{
    int len = v.size();
    for (int i = 1; i < len; ++i)
    {
        int temp = v[i];
        for (int j = i - 1; j >= 0; --j)
        {
            if (v[j] > temp)
            {
                v[j + 1] = v[j];
                v[j] = temp;
            }
            else
                break;
        }
    }
}

4. 快速排序:

(小数,基准元素,大数)。在区间中随机挑选一个元素作基准,将小于基准的元素放在基准之前,大于基准的元素放在基准之后,再分别对小数区与大数区进行排序。
快速排序思路:

  • 1. 选取第一个数为基准
  • 2. 将比基准小的数交换到前面,比基准大的数交换到后面
  • 3. 对左右区间重复第二步,直到各区间只有一个数
// ----------------------------------------------------

// 快速排序(递归)
void QuickSort(vector<int> &v, int low, int high)
{
    if (low >= high) // 结束标志
        return;
    int first = low;    // 低位下标
    int last = high;    // 高位下标
    int key = v[first]; // 设第一个为基准

    while (first < last)
    {
        // 将比第一个小的移到前面
        while (first < last && v[last] >= key)
            last--;
        if (first < last)
            v[first++] = v[last];

        // 将比第一个大的移到后面
        while (first < last && v[first] <= key)
            first++;
        if (first < last)
            v[last--] = v[first];
    }
    // 基准置位
    v[first] = key;
    // 前半递归
    QuickSort(v, low, first - 1);
    // 后半递归
    QuickSort(v, first + 1, high);
}

// ----------------------------------------------------

// 模板实现快速排序(递归)
template <typename T>
void quick_sort_recursive(T arr[], int start, int end)
{
    if (start >= end)
        return;
    T mid = arr[end];
    int left = start, right = end - 1;
    while (left < right)
    {
        while (arr[left] < mid && left < right)
            left++;
        while (arr[right] >= mid && left < right)
            right--;
        std::swap(arr[left], arr[right]);
    }
    if (arr[left] >= arr[end])
        std::swap(arr[left], arr[end]);
    else
        left++;
    quick_sort_recursive(arr, start, left - 1);
    quick_sort_recursive(arr, left + 1, end);
}
template <typename T> 
void quick_sort(T arr[], int len)
{
    quick_sort_recursive(arr, 0, len - 1);
}

// ----------------------------------------------------

// 模板实现快速排序(迭代)
struct Range
{
    int start, end;
    Range(int s = 0, int e = 0)
    {
        start = s, end = e;
    }
};
template <typename T> 
void quick_sort(T arr[], const int len)
{
    if (len <= 0)
        return; 
    Range r[len];
    int p = 0;
    r[p++] = Range(0, len - 1);
    while (p)
    {
        Range range = r[--p];
        if (range.start >= range.end)
            continue;
        T mid = arr[range.end];
        int left = range.start, right = range.end - 1;
        while (left < right)
        {
            while (arr[left] < mid && left < right)
                left++;
            while (arr[right] >= mid && left < right)
                right--;
            std::swap(arr[left], arr[right]);
        }
        if (arr[left] >= arr[range.end])
            std::swap(arr[left], arr[range.end]);
        else
            left++;
        r[p++] = Range(range.start, left - 1);
        r[p++] = Range(left + 1, range.end);
    }
}

5. 堆排序:

#include <iostream>
#include <algorithm>
using namespace std;

// 堆排序:(最大堆,有序区)。从堆顶把根卸出来放在有序区之前,再恢复堆。

void max_heapify(int arr[], int start, int end)
{
    int dad = start;
    int son = dad * 2 + 1;
    while (son <= end)
    {                                                  
        if (son + 1 <= end && arr[son] < arr[son + 1]) 
            son++;
        if (arr[dad] > arr[son]) 
            return;
        else
        { 
            swap(arr[dad], arr[son]);
            dad = son;
            son = dad * 2 + 1;
        }
    }
}

void heap_sort(int arr[], int len)
{
    for (int i = len / 2 - 1; i >= 0; i--)
        max_heapify(arr, i, len - 1);
    for (int i = len - 1; i > 0; i--)
    {
        swap(arr[0], arr[i]);
        max_heapify(arr, 0, i - 1);
    }
}

int main()
{
    int arr[] = {3, 5, 3, 0, 8, 6, 1, 5, 8, 6, 2, 4, 9, 4, 7, 0, 1, 8, 9, 7, 3, 1, 2, 5, 9, 7, 4, 0, 2, 6};
    int len = (int)sizeof(arr) / sizeof(*arr);
    heap_sort(arr, len);
    for (int i = 0; i < len; i++)
        cout << arr[i] << ' ';
    cout << endl;
    return 0;
}

6. 归并排序:

归并排序把数据分为两段,从两段中逐个选最小的元素移入新数据段的末尾。可从上到下或从下到上进行。

template <typename T>
void merge_sort(T arr[], int len)
{
    T *a = arr;
    T *b = new T[len];
    for (int seg = 1; seg < len; seg += seg)
    {
        for (int start = 0; start < len; start += seg + seg)
        {
            int low = start, mid = min(start + seg, len), high = min(start + seg + seg, len);
            int k = low;
            int start1 = low, end1 = mid;
            int start2 = mid, end2 = high;
            while (start1 < end1 && start2 < end2)
                b[k++] = a[start1] < a[start2] ? a[start1++] : a[start2++];
            while (start1 < end1)
                b[k++] = a[start1++];
            while (start2 < end2)
                b[k++] = a[start2++];
        }
        T *temp = a;
        a = b;
        b = temp;
    }
    if (a != arr)
    {
        for (int i = 0; i < len; i++)
            b[i] = a[i];
        b = a;
    }
    delete[] b;
}

/*****************
    递归版
*****************/
template <typename T>
void merge_sort_recursive(T arr[], T reg[], int start, int end)
{
    if (start >= end)
        return;
    int len = end - start, mid = (len >> 1) + start;
    int start1 = start, end1 = mid;
    int start2 = mid + 1, end2 = end;
    merge_sort_recursive(arr, reg, start1, end1);
    merge_sort_recursive(arr, reg, start2, end2);
    int k = start;
    while (start1 <= end1 && start2 <= end2)
        reg[k++] = arr[start1] < arr[start2] ? arr[start1++] : arr[start2++];
    while (start1 <= end1)
        reg[k++] = arr[start1++];
    while (start2 <= end2)
        reg[k++] = arr[start2++];
    for (k = start; k <= end; k++)
        arr[k] = reg[k];
}

template <typename T>
void merge_sort(T arr[], const int len)
{
    T *reg = new T[len];
    merge_sort_recursive(arr, reg, 0, len - 1);
    delete[] reg;

7. 希尔排序:

希尔排序每一轮按照事先决定的间隔进行插入排序,间隔会依次缩小,最后一次一定要是1。

template <typename T>
void shell_sort(T array[], int length)
{
    int h = 1;
    while (h < length / 3)
    {
        h = 3 * h + 1;
    }
    while (h >= 1)
    {
        for (int i = h; i < length; i++)
        {
            for (int j = i; j >= h && array[j] < array[j - h]; j -= h)
            {
                std::swap(array[j], array[j - h]);
            }
        }
        h = h / 3;
    }
}

8. 计数排序:

计数排序:
统计小于等于该元素值的元素的个数i,于是该元素就放在目标数组的索引i位(i≥0)。 计数排序基于一个假设,待排序数列的所有数均为整数,且出现在(0,k)的区间之内。 如果 k(待排数组的最大值) 过大则会引起较大的空间复杂度,一般是用来排序 0 到 100 之间的数字的最好的算法,但是它不适合按字母顺序排序人名。 计数排序不是比较排序,排序的速度快于任何比较排序算法。
时间复杂度为O(n+k),空间复杂度为O(n+k)

算法的步骤如下:

  • 1. 找出待排序的数组中最大和最小的元素
  • 2. 统计数组中每个值为 i 的元素出现的次数,存入数组 C 的第 i 项
  • 3. 对所有的计数累加(从 C 中的第一个元素开始,每一项和前一项相加)
  • 4. 反向填充目标数组:将每个元素 i 放在新数组的第 C[i] 项,每放一个元素就将 C[i] 减去 1
#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

// 计数排序
void CountSort(vector<int> &vecRaw, vector<int> &vecObj)
{
    // 确保待排序容器非空
    if (vecRaw.size() == 0)
        return;

    // 使用 vecRaw 的最大值 + 1 作为计数容器 countVec 的大小
    int vecCountLength = (*max_element(begin(vecRaw), end(vecRaw))) + 1;
    vector<int> vecCount(vecCountLength, 0);

    // 统计每个键值出现的次数
    for (int i = 0; i < vecRaw.size(); i++)
        vecCount[vecRaw[i]]++;

    // 后面的键值出现的位置为前面所有键值出现的次数之和
    for (int i = 1; i < vecCountLength; i++)
        vecCount[i] += vecCount[i - 1];

    // 将键值放到目标位置
    for (int i = vecRaw.size(); i > 0; i--) // 此处逆序是为了保持相同键值的稳定性
        vecObj[--vecCount[vecRaw[i - 1]]] = vecRaw[i - 1];
}

int main()
{
    vector<int> vecRaw = {0, 5, 7, 9, 6, 3, 4, 5, 2, 8, 6, 9, 2, 1};
    vector<int> vecObj(vecRaw.size(), 0);

    CountSort(vecRaw, vecObj);

    for (int i = 0; i < vecObj.size(); ++i)
        cout << vecObj[i] << "  ";
    cout << endl;

    return 0;
}

9. 桶排序:

桶排序: 将值为i的元素放入i号桶,最后依次把桶里的元素倒出来。
桶排序序思路:

  • 1. 设置一个定量的数组当作空桶。
  • 2. 寻访序列,并且把项目一个一个放到对应的桶去。
  • 3. 对每个不是空的桶进行排序。
  • 4. 从不是空的桶里把项目再放回原来的序列中。
    假设数据分布在[0,100)之间,每个桶内部用链表表示,在数据入桶的同时插入排序,然后把各个桶中的数据合并。
#include <iterator>
#include <iostream>
#include <vector>
using std::vector;

const int BUCKET_NUM = 10;

struct ListNode
{
    explicit ListNode(int i = 0) : mData(i), mNext(NULL) {}
    ListNode *mNext;
    int mData;
};

ListNode *insert(ListNode *head, int val)
{
    ListNode dummyNode;
    ListNode *newNode = new ListNode(val);
    ListNode *pre, *curr;
    dummyNode.mNext = head;
    pre = &dummyNode;
    curr = head;
    while (NULL != curr && curr->mData <= val)
    {
        pre = curr;
        curr = curr->mNext;
    }
    newNode->mNext = curr;
    pre->mNext = newNode;
    return dummyNode.mNext;
}

ListNode *Merge(ListNode *head1, ListNode *head2)
{
    ListNode dummyNode;
    ListNode *dummy = &dummyNode;
    while (NULL != head1 && NULL != head2)
    {
        if (head1->mData <= head2->mData)
        {
            dummy->mNext = head1;
            head1 = head1->mNext;
        }
        else
        {
            dummy->mNext = head2;
            head2 = head2->mNext;
        }
        dummy = dummy->mNext;
    }
    if (NULL != head1)
        dummy->mNext = head1;
    if (NULL != head2)
        dummy->mNext = head2;

    return dummyNode.mNext;
}

void BucketSort(int n, int arr[])
{
    vector<ListNode *> buckets(BUCKET_NUM, (ListNode *)(0));
    for (int i = 0; i < n; ++i)
    {
        int index = arr[i] / BUCKET_NUM;
        ListNode *head = buckets.at(index);
        buckets.at(index) = insert(head, arr[i]);
    }
    ListNode *head = buckets.at(0);
    for (int i = 1; i < BUCKET_NUM; ++i)
    {
        head = Merge(head, buckets.at(i));
    }
    for (int i = 0; i < n; ++i)
    {
        arr[i] = head->mData;
        head = head->mNext;
    }
}

10. 基数排序:

基数排序: 一种多关键字的排序算法,可用桶排序实现。

int maxbit(int data[], int n) //辅助函数,求数据的最大位数
{
    int maxData = data[0]; ///< 最大数
    /// 先求出最大数,再求其位数,这样有原先依次每个数判断其位数,稍微优化点。
    for (int i = 1; i < n; ++i)
    {
        if (maxData < data[i])
            maxData = data[i];
    }
    int d = 1;
    int p = 10;
    while (maxData >= p)
    {
        //p *= 10; // 可能溢出
        maxData /= 10;
        ++d;
    }
    return d;
    /*    int d = 1; //保存最大的位数
    int p = 10;
    for(int i = 0; i < n; ++i)
    {
        while(data[i] >= p)
        {
            p *= 10;
            ++d;
        }
    }
    return d;*/
}
void radixsort(int data[], int n) //基数排序
{
    int d = maxbit(data, n);
    int *tmp = new int[n];
    int *count = new int[10]; //计数器
    int i, j, k;
    int radix = 1;
    for (i = 1; i <= d; i++) //进行d次排序
    {
        for (j = 0; j < 10; j++)
            count[j] = 0; //每次分配前清空计数器
        for (j = 0; j < n; j++)
        {
            k = (data[j] / radix) % 10; //统计每个桶中的记录数
            count[k]++;
        }
        for (j = 1; j < 10; j++)
            count[j] = count[j - 1] + count[j]; //将tmp中的位置依次分配给每个桶
        for (j = n - 1; j >= 0; j--)            //将所有桶中记录依次收集到tmp中
        {
            k = (data[j] / radix) % 10;
            tmp[count[k] - 1] = data[j];
            count[k]--;
        }
        for (j = 0; j < n; j++) //将临时数组的内容复制到data中
            data[j] = tmp[j];
        radix = radix * 10;
    }
    delete[] tmp;
    delete[] count;
}

see you next blog~~

  • 36
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 37
    评论
评论 37
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

宁海没有七号公园

谢谢你%%%

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

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

打赏作者

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

抵扣说明:

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

余额充值