排序算法总结与实现

各种排序算法实现

今天吃饭听同学说,百度面试,当场让写归并算法跪了,后天面试华为,今天晚上啥也不干,将所有排序算法复习一遍。

  • 排序简介

    排序即是将一个数据元素(记录)的任意序列,重新排列成一个按关键字有序的序列的过程
    排序按照记录所在位置分为:内部排序和外部排序。内部排序指待排序记录存放在内存;外部排序指排序过程中需对外存进行访问的排序。
    依据排序的原则,排序又分为以下:插入排序、交换排序、选择排序、归并排序和基数排序
    插入排序:每次将一个待排序的记录,按其关键字值的大小插入到已经排好序的表中,直至全部插入完成。主要包括:直接插入排序、希尔排序等
    交换排序:两两比较待排序的对象的关键码,如果发生逆序,则交换之,直到全部对象都排好序为止。主要包括:冒泡排序,快速排序等
    选择排序:将待排序的节点分为已排序(初始为空)和未排序两组,依次将未排序的节点中值最小的节点插入已排序的数组中。主要包括:直接选择排序、堆排序等
    归并排序:通过对两个或两个以上的有序节点序列的合并来实现排序。
    基数排序:借助“分配”和“收集”对逻辑关键字进行排序。

  • 各种排序算法时间复杂度,稳定性如下表

  • 插入排序
    基本思想:每次将一个待排序的记录按照其关键字值的大小插入到已排好序的表中,直到全部插入完成。
    平均时间复杂度为O(n^2),空间复杂度为O(1),稳定排序
#include <iostream>
#include <vector>
using namespace std;
void InsertSort(vector<int> &arr)
{
    int size = (int)arr.size();
    if (size <= 1)
        return ;
    int temp = 0;
    for (int i = 1; i < size; i++)
    {
        temp = arr[i];
        int j = i - 1;
        // 注意下面循环控制条件必须先为j>=0,否则当j=-1时,访问arr[j]程序崩溃,原因是数组越界
        for ( ; (j >= 0) && (temp < arr[j]); j--)
            arr[j + 1] = arr[j];
        arr[j + 1] = temp;
    }
    return ;
}
int main()
{
    vector<int> arr;
    arr.push_back(3);
    arr.push_back(1);
    arr.push_back(2);
    arr.push_back(5);
    arr.push_back(4);
    InsertSort(arr);
    for (int i = 0; i < (int)arr.size(); i++)
        cout << arr[i] << " ";
    cout << endl;
    return 0;
}
  • 冒泡排序
    基本思想:以从小到大的顺序排列,在每一次循环中,都将最大的数找出来,放到最后面,经过n次循环后,便实现对整个序列的排序。
    平均时间复杂度为O(n^2),空间复杂度为O(1),稳定排序
#include <iostream>
#include <vector>
using namespace std;
void BubbleSort(vector<int> &arr)
{
    int size = (int)(arr.size());
    if (size <= 1)
        return ;
    int flag = 0;    // 用于判断冒泡排序是否完全结束
    for (int i = 0; i < size && flag == 0; i++)
    {
        flag = 1;
        for (int j = i + 1; j < size; j++)
        {
            if (arr[i] > arr[j])
            {
                flag = 0;
                int temp = arr[i];
                arr[i] = arr[j];
                arr[j] = temp;
            }
        }
    }
    return ;
}
int main()
{
    vector<int> arr;
    arr.push_back(3);
    arr.push_back(1);
    arr.push_back(2);
    arr.push_back(5);
    arr.push_back(4);
    BubbleSort(arr);
    for (int i = 0; i < (int)arr.size(); i++)
        cout << arr[i] << " ";
    cout << endl;
    return 0;
}
  • 直接选择排序
    基本思想:将待排序序列分为已排序(初始为空)和未排序两组,依次从未排序的节点中找最小(或最大)节点插入到已排序的数组中。
    平均时间复杂度为O(n^2),空间复杂度为O(1),非稳定排序
#include <iostream>
#include <vector>
using namespace std;
void SelectSort(vector<int> &arr)
{
    int size = (int)(arr.size());
    if (size <= 1)
        return ;
    for (int i = 0; i < size; i++)
    {
        int k = i;
        for (int j = i + 1; j < size; j++)
        {
            if (arr[k] > arr[j])
                k = j;
        }
        if (k != i)
        {
            int temp = arr[k];
            arr[k] = arr[i];
            arr[i] = temp;
        }
    }
    return ;
}
int main()
{
    vector<int> arr;
    arr.push_back(3);
    arr.push_back(1);
    arr.push_back(2);
    arr.push_back(5);
    arr.push_back(4);
    SelectSort(arr);
    for (int i = 0; i < (int)arr.size(); i++)
        cout << arr[i] << " ";
    cout << endl;
    return 0;
}
  • 希尔排序
    基本思想:取一个正整数d < n,把所有相隔d的记录放在一组,组内进行直接插入排序;然后改变d的值,使其变小,重复上述分组和排序,直到d = 1,即所有记录放进一组中排序结束。
    当增量为2时,时间复杂度为O(n^1.5),空间复杂度为O(1),不稳定排序
#include <iostream>
#include <vector>
using namespace std;
void ShellSort(vector<int> &arr)
{
    int size = (int)(arr.size());
    if (size <= 1)
        return ;
    for (int d = size / 2; d > 0; d /= 2)
    {
        for (int i = d; i < size; i++)
        {
            int temp = arr[i];
            int j = i - d;
            for ( ; (j >= 0) && (temp < arr[j]); j -= d)
            {
                arr[j + d] = arr[j];
            }
            arr[j + d] = temp;
        }
    }
    return ;
}
int main()
{
    vector<int> arr;
    arr.push_back(3);
    arr.push_back(1);
    arr.push_back(2);
    arr.push_back(5);
    arr.push_back(4);
    ShellSort(arr);
    for (int i = 0; i < (int)arr.size(); i++)
        cout << arr[i] << " ";
    cout << endl;
    return 0;
}
  • 快速排序
    基本思想:通过一趟排序,将待排序记录分割成独立两部分,其中一部分记录的关键字均比另一部分记录的关键字小,则可分别对两部分进行排序,已达到整个序列有序即可。
    平均时间复杂度O(nlogn),空间复杂度O(logn),不稳定排序
#include <iostream>
#include <vector>
using namespace std;
void QuickSort(vector<int> &arr, int left, int right)
{
    int i, j;
    int temp;
    if (left >= right)
        return ;
    i = left;
    j = right;
    temp = arr[i];
    while (i < j)
    {
        while ((i < j) && (arr[j] >= temp))
            j--;
        if (i < j)
        {
            arr[i] = arr[j];
            i++;
        }
        while ((i < j) && (arr[i] <= temp))
            i++;
        if (i < j)
        {
            arr[j] = arr[i];
            j--;
        }
    }
    arr[i] = temp;
    QuickSort(arr, left, j - 1);
    QuickSort(arr, j + 1, right);
    return ;
}
int main()
{
    vector<int> arr;
    arr.push_back(3);
    arr.push_back(1);
    arr.push_back(2);
    arr.push_back(5);
    arr.push_back(4);
    // 注意此处的调用函数
    QuickSort(arr, 0, (int)arr.size() - 1);
    for (int i = 0; i < (int)arr.size(); i++)
        cout << arr[i] << " ";
    cout << endl;
    return 0;
}
  • 归并排序
    基本思想:将两个或两个以上的有序表组合成一个新的有序表的过程。下面主要针对2路归并排序进行实现。
    平均时间复杂度O(nlogn),空间复杂度O(n),稳定排序
#include <iostream>
#include <vector>
using namespace std;
// 归并过程
void Merge(vector<int> &arr, vector<int> &temp, int low, int mid, int high)
{
    int k = low;
    int j = mid + 1;
    while ((low <= mid) && (j <= high))
    {
        if (arr[low] <= arr[j])
            temp[k] = arr[low++];
        else
            temp[k] = arr[j++];
        k++;
    }
    if (low <= mid)
        for (int i = low; i <= mid; i++)
            temp[k++] = arr[i];
    if (j <= high)
        for (int p = j; p <= high; p++)
            temp[k++] = arr[p];
    for (int i = low; i <= high; i++)
        arr[i] = temp[i];
}
// 归并排序
void MergeSort(vector<int> &arr, vector<int> temp, int low, int high)
{
    if (low < high)
    {
        int mid = (low + high) / 2;
        MergeSort(arr, temp, low, mid);
        MergeSort(arr, temp, mid + 1, high);
        Merge(arr, temp, low, mid, high);
    }
}
int main()
{
    vector<int> arr;
    arr.push_back(3);
    arr.push_back(1);
    arr.push_back(2);
    arr.push_back(5);
    arr.push_back(4);
    vector<int> temp(arr.size(), 0);
    MergeSort(arr, temp, 0, (int)arr.size() - 1);
    for (int i = 0; i < (int)arr.size(); i++)
        cout << arr[i] << " ";
    cout << endl;
    return 0;
}
  • 堆排序
    基本思想:将无序序列建成一个堆,得到关键字最小(或最大)的记录,输出堆顶的最小(最大)值后,使剩余的n - 1个元素重新建成一个堆,则可得n个元素的次小值,重复执行,最后得到一个有序序列。
    平均时间复杂度:O(nlogn),空间复杂度O(1),不稳定排序
#include <iostream>
#include <vector>
using namespace std;
// 取得堆中左孩子索引值
int LeftChild(int parent)
{
    return 2 * parent + 1;
}
// 取得堆中右孩子索引值
int RightChild(int parent)
{
    return 2 * parent + 2;
}
// 堆中元素向下进行调整操作
void SiftDown(vector<int> &arr, int index, int n)
{
    int max_index = LeftChild(index);
    while (max_index <= n)
    {
        if (max_index <= n - 1 && arr[RightChild(index)] > arr[max_index])
            max_index++;
        if (arr[index] > arr[max_index])
            break;
        int temp = arr[index];
        arr[index] = arr[max_index];
        arr[max_index] = temp;
        index = max_index;
        max_index = LeftChild(index);
    }
}
// 构建堆过程
void BuildHeap(vector<int> &arr)
{
    for (int i = (int)arr.size() / 2 - 1; i >= 0; i--)
        SiftDown(arr, i, arr.size() - 1);
}
// 堆排序过程
void HeapSort(vector<int> &arr)
{
    // 构建最大堆
    BuildHeap(arr);
    int n = (int)arr.size() - 1;
    for (int i = n; i >= 1; i--)
    {
        int temp = arr[0];
        arr[0] = arr[i];
        arr[i] = temp;
        SiftDown(arr, 0, i - 1);
    }
}
int main()
{
    vector<int> arr;
    arr.push_back(7);
    arr.push_back(11);
    arr.push_back(2);
    arr.push_back(5);
    arr.push_back(4);
    arr.push_back(9);
    arr.push_back(1);
    arr.push_back(12);
    arr.push_back(6);
    arr.push_back(4);
    vector<int> temp(arr.size(), 0);
    HeapSort(arr);
    for (int i = 0; i < (int)arr.size(); i++)
        cout << arr[i] << " ";
    cout << endl;
    return 0;
}
  • 基数排序
    基本思想:借助“分配”和“收集”的思想对单逻辑关键字进行排序。
    平均时间复杂度O(d(n+m)),其中r为所采取的计数,m为堆数,它的排序效率高于其他比较排序算法,稳定排序
#include<iostream>
#include <vector>
using namespace std;
// 计算关键字位数的最大值
int MaxSize(vector<int> &arr)
{
    int size = (int)arr.size();
    int maxsize = 1;
    for (int i = 0; i < size; i++)
    {
        int temp = 1;
        int n = 10;
        while (arr[i] / n > 0)
        {
            temp++;
            n *= 10;
        }
        maxsize = (temp > maxsize) ? temp : maxsize;
    }
    return maxsize;
}
// 基数排序
void RadixSort(vector<int> &arr)
{
    // 定义技术的桶
    vector< vector<int> > bucket(10, vector<int>(10, 0));
    // 保存每个桶中元素数目
    vector<int> order(10, 0);
    // 计算关键字位数的最大值
    int maxsize = MaxSize(arr);
    for (int n = 1; maxsize > 0; n *= 10, maxsize--)
    {
        // 将待排序元素依据关键字放入对应的桶中
        for (int i = 0; i < (int)arr.size(); i++)
        {
            int temp = arr[i] / n % 10;
            bucket[temp][order[temp]] = arr[i];
            order[temp]++;
        }
        // 将桶中元素串接起来
        int k = 0;
        for (int i = 0; i < 10; i++)
        {
            if (order[i] != 0)
            {
                for (int j = 0; j < order[i]; j++)
                {
                    arr[k] = bucket[i][j];
                    k++;
                }
                // 将其全部置为0
                order[i] = 0;
            }
        }
    }
    return ;
}
int main()
{
    vector<int> arr;
    arr.push_back(36);
    arr.push_back(72);
    arr.push_back(53);
    arr.push_back(73);
    arr.push_back(55);
    arr.push_back(19);
    arr.push_back(24);
    arr.push_back(65);
    arr.push_back(38);
    arr.push_back(81);
    RadixSort(arr);
    for (int i = 0; i < (int)arr.size(); i++)
        cout << arr[i] << " ";
    cout << endl;
    return 0;
}

参考文献:数据结构与算法

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值