九大排序算法原理并使用C++实现

1.插入排序
假设有N个待排序元素,下标从0到N - 1。从位置p = 1开始从前往后扫描直到N - 1,在第p趟时,我们将位置p上的元素向左移直至找到它前面的元素都比它小为止。时间复杂度为 O ( N 2 ) O(N^2) O(N2)

#ifndef INSERTION_SORT_H
#define INSERTION_SORT_H

#include <vector>
#include <functional>
using namespace std;

template <typename Comparable>
void insertionSort(vector<Comparable> & a)
{
    for (int p = 1; p < a.size(); ++p) {
        Comparable tmp = std::move(a[p]);
        int j;
        for (j = p; j > 0 && tmp < a[j - 1]; --j)
            a[j] = std::move(a[j - 1]);
        a[j] = std::move(tmp);
    }
}

template <typename RandomIterator, typename Comparator>
void insertionSort(const RandomIterator & begin, const RandomIterator & end, Comparator lessThan)
{
    if (begin == end)
        return;
    RandomIterator j;
    for (RandomIterator p = begin + 1; p != end; ++p) {
        auto tmp = std::move(*p);
        for (j = p; j != begin && lessThan(tmp, *(j - 1)); --j)
            *j = std::move(*(j - 1));
        *j = std::move(tmp);
    }
}

template <typename RandomIterator>
void insertionSort(const RandomIterator & begin, const RandomIterator & end)
{
    insertionSort(begin, end, less<decltype(*begin)>{});
}

#endif
#include "insertion_sort.h"
#include <iostream>
using namespace std;

int main()
{
    int x;
    vector<int> a;
    for (int i = 0; i < 10; i++) {
        cin >> x;
        a.push_back(x);
    }
    insertionSort(begin(a), end(a));
    for (auto & tmp : a)
        cout << tmp << " ";
    cout << endl;
    return 0;
}

2.希尔排序
希尔排序是通过比较相距一定间隔的元素实现的,算法的大致思路是先设定一个间隔值gap,然后从第一个元素开始,依次比较与当前元素和相距为gap元素的大小,若当前元素大则交换,当比较的元素到达最后一个时,gap值除以2,再重新从前往后比较,直到gap值为0,排序结束。时间复杂度的上界为 O ( N 2 ) O(N^2) O(N2)。要想提高性能可以使用Hibbard增量序列,该增量的通式为2的k次方减1,k从1开始取,最坏运行时间为 O ( N 3 / 2 ) O(N^{3/2}) O(N3/2),还有Sedgewick提出的几种增量序列。

#ifndef SHELL_SORT_H
#define SHELL_SORT_H

#include <vector>
using namespace std;

template <typename Comparable>
void shellsort(vector<Comparable> & a)
{
    for (int gap = a.size() / 2; gap > 0; gap /= 2)
        for (int i = gap; i < a.size(); ++i) {
            Comparable tmp = std::move(a[i]);
            int j = i;
            for (; j >= gap && tmp < a[j - gap]; j -= gap)
                a[j] = std::move(a[j - gap]);
            a[j] = std::move(tmp);
        }
}

#endif
#include "shell_sort.h"
#include <iostream>
using namespace std;

int main()
{
    int x;
    vector<int> v;
    for (int i = 0; i < 10; i++) {
        cin >> x;
        v.push_back(x);
    }
    shellsort(v);
    for (auto & tmp : v)
        cout << tmp << " ";
    cout << endl;
    return 0;
}

3.堆排序
堆排序的运行时间为 O ( N l o g N ) O(NlogN) O(NlogN),需要一个附加的数组。该算法是通过依次删除(max)堆中最大的元素并把该元素放到堆刚刚减少元素的位置上来实现的,执行N - 1次删除最大值操作后,数组中的元素就变成从小到大排列了。

#ifndef HEAP_SORT_H
#define HEAP_SORT_H

#include <vector>
using namespace std;

inline int leftChild(int i)
{
    return 2 * i + 1;
}

template <typename Comparable>
void percDown(vector<Comparable> & a, int i, int n)
{
    int child;
    Comparable tmp;
    for (tmp = std::move(a[i]); leftChild(i) < n; i = child) {
        child = leftChild(i);
        if (child != n - 1 && a[child] < a[child + 1])
            ++child;
        if (tmp < a[child])
            a[i] = std::move(a[child]);
        else
            break;
    }
    a[i] = std::move(tmp);
}

template <typename Comparable>
void heapsort(vector<Comparable> & a)
{
    for (int i = a.size() / 2 - 1; i >= 0; --i) /* buildHeap */
        percDown(a, i, a.size());
    for (int j = a.size() - 1; j > 0; --j) { /* deleteMax */
        std::swap(a[0], a[j]);
        percDown(a, 0, j);
    }
}

#endif
#include "heap_sort.h"
#include <iostream>
using namespace std;

int main()
{
    int x;
    vector<int> v;
    for (int i = 0; i < 10; i++) {
        cin >> x;
        v.push_back(x);
    }
    heapsort(v);
    for (auto & tmp : v)
        cout << tmp << " ";
    cout << endl;
    return 0;
}

4.归并排序
归并排序算法的大概思路是先把待排序数组分成两部分,分别对那两部分进行排序,再将那两部分合并到一起。时间复杂度为 O ( N l o g N ) O(NlogN) O(NlogN)

#ifndef MERGE_SORT_H
#define MERGE_SORT_H

#include <vector>
using namespace std;

/**
 * tmpArray为放置归并结果的数组
 * leftPos为子数组最左元素的下标
 * rightPos为后半部分起点的下标
 * rightEnd为子数组最右元素的下标
 */
template <typename Comparable>
void merge(vector<Comparable> & a, vector<Comparable> & tmpArray, int leftPos, int rightPos, int rightEnd)
{
    int leftEnd = rightPos - 1;
    int tmpPos = leftPos;
    int numElements = rightEnd - leftPos + 1;
    while (leftPos <= leftEnd && rightPos <= rightEnd)
        if (a[leftPos] <= a[rightPos])
            tmpArray[tmpPos++] = std::move(a[leftPos++]);
        else
            tmpArray[tmpPos++] = std::move(a[rightPos++]);
    while (leftPos <= leftEnd)
        tmpArray[tmpPos++] = std::move(a[leftPos++]);
    while (rightPos <= rightEnd)
        tmpArray[tmpPos++] = std::move(a[rightPos++]);
    for (int i = 0; i < numElements; ++i, --rightEnd)
        a[rightEnd] = std::move(tmpArray[rightEnd]);
}

template <typename Comparable>
void mergeSort(vector<Comparable> & a, vector<Comparable> & tmpArray, int left, int right)
{
    if (left < right) {
        int center = (left + right) / 2;
        mergeSort(a, tmpArray, left, center);
        mergeSort(a, tmpArray, center + 1, right);
        merge(a, tmpArray, left, center + 1, right);
    }
}

template <typename Comparable>
void mergeSort(vector<Comparable> & a)
{
    vector<Comparable> tmpArray(a.size());
    mergeSort(a, tmpArray, 0, a.size() - 1);
}

#endif
#include "merge_sort.h"
#include <iostream>
using namespace std;

int main()
{
    int x;
    vector<int> v;
    for (int i = 0; i < 10; i++) {
        cin >> x;
        v.push_back(x);
    }
    mergeSort(v);
    for (auto & tmp : v)
        cout << tmp << " ";
    cout << endl;
    return 0;
}

5.快速排序
快速排序的思路大致是取待排序数组 S S S中的任一元素为枢纽元,将除去枢纽元外的元素划分为小于等于枢纽元的集合 S 1 S1 S1以及大于等于枢纽元的集合 S 2 S2 S2,返回排序后的 S 1 S1 S1,后跟枢纽元,继而再跟 S 2 S2 S2。在这里,采用三数中值法选取枢纽元,选取 S S S中最左边,最右边以及中间的元素,取它们大小处于中间的值作为枢纽元,并把枢纽元与最后的元素交换位置。接着是分割策略,把 S S S分成三部分,这里用 i i i代表 S S S的最左边元素, j j j代表右边枢纽元的前一个元素,将 i i i右移,移过那些小于枢纽元的元素,将 j j j左移,移过那些大于枢纽元的元素,当 i i i j j j停止时,如果 i i i j j j的左边,那么将这两个元素交换,重复该过程直到 i i i j j j彼此交错为止。
对于很小的数组,快速排序性能不如插入排序,故这里当元素个数小于10的时候用插入排序。

#ifndef QUICK_SORT_H
#define QUICK_SORT_H

#include <vector>
using namespace std;

template <typename Comparable>
void insertionSort(vector<Comparable> & a, int left, int right)
{
    if (left == right)
        return;
    for (int p = left; p <= right; ++p) {
        Comparable tmp = std::move(a[p]);
        int j;
        for (j = p; j > 0 && tmp < a[j - 1]; --j)
            a[j] = std::move(a[j - 1]);
        a[j] = std::move(tmp);
    }
}

/*
* 返回 left center right 三项的中值
*/
template <typename Comparable>
const Comparable & median3(vector<Comparable> & a, int left, int right)
{
    int center = (left + right) / 2;
    if (a[center] < a[left])
        std::swap(a[left], a[center]);
    if (a[right] < a[left])
        std::swap(a[left], a[right]);
    if (a[right] < a[center])
        std::swap(a[center], a[right]);
    std::swap(a[center], a[right - 1]);
    return a[right - 1];
}

/*
* 使用三中值分割法
*/
template <typename Comparable>
void quicksort(vector<Comparable> & a, int left, int right)
{
    if (left + 10 <= right) {
        const Comparable & pivot = median3(a, left, right);
        int i = left, j = right - 1;
        // 开始分割
        for (; ;) {
            while (a[++i] < pivot) {}
            while (pivot < a[--j]) {}
            if (i < j)
                std::swap(a[i], a[j]);
            else
                break;
        }
        std::swap(a[i], a[right - 1]); // 恢复枢纽元
        quicksort(a, left, i - 1); // 将小于等于枢纽元的元素排序
        quicksort(a, i + 1, right); // 将大于等于枢纽元的元素排序
    }
    else // 若元素小于10个则进行插入排序
        insertionSort(a, left, right);
}

template <typename Comparable>
void quicksort(vector<Comparable> & a)
{
    quicksort(a, 0, a.size() - 1);
}

#endif
#include "quick_sort.h"
#include <iostream>
using namespace std;

int main()
{
    int x;
    vector<int> v;
    for (int i = 0; i < 15; i++) {
        cin >> x;
        v.push_back(x);
    }
    quicksort(v);
    for (auto & tmp : v)
        cout << tmp << " ";
    cout << endl;
    return 0;
}

6.桶排序
桶排序中待排序的元素必须只由小于 M M M的正整数构成的,算法的思路大概是使用一个大小为 M M M的count数组,初始化为全0,当读入待排序元素 a i ai ai时,count[ a i ai ai]增1。在所有的输入数据读入后,扫描count数组,打印出排序后的数据,时间复杂度为 O ( M + N ) O(M + N) O(M+N)

#include <vector>
#include <cstring>
#include <iostream>
using namespace std;

int findMax(vector<int> & v, int N)
{
    int Max = v[0];
    for (int i = 1; i < N; i++) {
        if (v[i] > Max)
            Max = v[i];
    }
    return Max;
}

void bucketsort(vector<int> & v, int N)
{
    int M, i;
    M = findMax(v, N);
    int count[M + 1];
    memset(count, 0, sizeof(count));
    for (i = 0; i < N; i++)
        count[v[i]]++;
    for (i = 0; i <= M; i++) {
        while (count[i] > 0) {
            cout << i << " ";
            count[i]--;
        }
    }
    cout << endl;
}

int main()
{
    int x;
    vector<int> v;
    for (int i = 0; i < 13; i++) {
        cin >> x;
        v.push_back(x);
    }
    bucketsort(v, v.size());
    return 0;
}

7.基数排序
对字符串排序:

void radixSortA(vector<string> & arr, int stringLen)
{
    const int BUCKETS = 256;
    vector<vector<string> > buckets(BUCKETS);

    for (int pos = stringLen - 1; pos >= 0; --pos) {
        for (string & s : arr)
            buckets[s[pos]].push_back(std::move(s));
        cout << endl;
        int idx = 0;
        for (auto & thisBucket : buckets) {
            for (string & s : thisBucket)
                arr[idx++] = std::move(s);
            thisBucket.clear();
        }
    }
}

对字符串进行计数排序:

void countingRadixSort(vector<string> & arr, int stringLen)
{
    const int BUCKETS = 256;
    int N = arr.size();
    vector<string> buffer(N);
    vector<string> *in = &arr;
    vector<string> *out = &buffer;
    for (int pos = stringLen - 1; pos >= 0; --pos) {
        vector<int> count(BUCKETS + 1);

        for (int i = 0; i < N; ++i)
            ++count[(*in)[i][pos] + 1];

        for (int b = 1; b <= BUCKETS; ++b)
            count[b] += count[b - 1];

        for (int i = 0; i < N; ++i)
            (*out)[count[(*in)[i][pos]]++] = std::move((*in)[i]);
        std::swap(in, out);
    }

    if (stringLen % 2 == 1)
        for (int i = 0; i < arr.size(); ++i)
            (*out)[i] = std::move((*in)[i]);
}

对长度不一致的字符串进行排序:

void radixSort(vector<string> & arr, int maxLen)
{
    const int BUCKETS = 256;

    vector<vector<string> > wordsByLength(maxLen + 1);
    vector<vector<string> > buckets(BUCKETS);

    for (string & s : arr)
        wordsByLength[s.length()].push_back(std::move(s));

    int idx = 0;
    for (auto & wordList : wordsByLength)
        for (string & s : wordList)
            arr[idx++] = std::move(s);

    int startingIndex = arr.size();
    for (int pos = maxLen - 1; pos >= 0; --pos) {
        startingIndex -= wordsByLength[pos + 1].size();

        for (int i = startingIndex; i < arr.size(); ++i)
            buckets[arr[i][pos]].push_back(std::move(arr[i]));

        idx = startingIndex;
        for (auto & thisBucket : buckets) {
            for (string & s : thisBucket)
                arr[idx++] = std::move(s);
            thisBucket.clear();
        }
    }
}

8.冒泡排序

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

template <typename Comparable>
void bubblesort(vector<Comparable> & a, int n)
{
    for (int i = 0; i < n - 1; ++i) {
        for (int j = i; j < n; ++j)
            if (a[i] > a[j])
                std::swap(a[i], a[j]);
    }
}

template <typename Comparable>
void bubblesort(vector<Comparable> & a)
{
    bubblesort(a, a.size());
}

int main()
{
    int x;
    vector<int> v;
    for (int i = 0; i < 2; i++) {
        cin >> x;
        v.push_back(x);
    }
    bubblesort(v);
    for (auto & tmp : v)
        cout << tmp << " ";
    cout << endl;
    return 0;
}

9.选择排序
从待排序数组的第一个元素开始比较,找出比当前元素小的并且是最小的元素和第一个元素互换,以此类推。

template <typename Comparable>
void selectionSort(vector<Comparable> & toSort)
{
    int minIndex;
    for (int i = 0; i < toSort.size(); ++i) {
        minIndex = i;
        for (int j = i + 1; j < toSort.size(); ++j) {
            if (toSort[j] < toSort[minIndex])
                swap(toSort[j], toSort[i]);
        }
    }
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值