排序算法总结

Posted Leo_wlCnBlogs

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了排序算法总结相关的知识,希望对你有一定的参考价值。

排序算法总结

趁着有时间把学习过的排序算法又实现了一遍复习一下,实现的排序算法主要有以下几种:冒泡排序、快速排序,选择排序,堆排序,插入排序,合并排序,希尔排序,桶排序等。

下面是网上找的一张图片,总结了常见排序算法的时间复杂度、空间复杂度以及稳定性,可以参考一下。

 

下面排序的都是vector<int>,懒得写模板了

1.冒泡排序

冒泡排序是最简单的排序算法,冒泡排序的基本思想是从后往前(或从前往后)两两比较相邻元素的值,若为逆序,则交换它们,直到序列比较完。我们称它为一趟冒泡。每一趟冒泡都会将一个元素放置到其最终位置上。

复制代码
//冒泡排序
void BubbleSort(vector<int> &vec)
{
    int n = vec.size();
    for (int i = 0; i < n; ++i)
        for (int j = n - 1; j > i; --j)
        {
            if (vec[j] < vec[j - 1])
            {
                int tmp = vec[j];
                vec[j] = vec[j - 1];
                vec[j - 1] = tmp;
            }
        }
}
复制代码

 

2.快速排序

快速排序是对冒泡排序的一种改进。其基本思想是基于分治法:在待排序表L[n]中任取一个元素pivot作为基准,通过一趟排序将序列划分为两部分L[1...K-1]和L[k+1...n],是的L[1...k-1]中的所有元素都小于pivot,而L[k+1...n]中所有元素都大于或等于pivot。则pivot放在了其最终位置L(k)上。然后,分别递归地对两个子序列重复上述过程,直至每部分内只有一个元素或空为止,即所有元素放在了其最终位置上。
复制代码
//划分为两个部分
int Partition(vector<int> &vec, int left, int right)
{
    int base = vec[left];
    while (left < right)
    {    
        while (left < right && vec[right] >= base)//从右向左找到第一个小于base值的元素
            --right;
        vec[left] = vec[right];//将比base值小的数移动到左边
        while (left < right && vec[left] <= base)//从左向右找到第一个大于base值的元素
            ++left;
        vec[right] = vec[left];
    }
    vec[left] = base;
    return left;
}

void QuickSort(vector<int> &vec, int left, int right)
{
    if (left < right)//递归跳出的条件
    {
        int pos = Partition(vec, left, right);
        QuickSort(vec, left, pos - 1);
        QuickSort(vec, pos + 1, right);
    }
}
复制代码

 

 3.简单选择排序

对要排序的序列,选出关键字最小的数据,将它和第一个位置的数据交换,接着,选出关键字次小的数据,将它与第二个位置上的数据交换。以此类推,直到完成整个过程。

所以如果有n个数据,那么则需要遍历n-1遍。

复制代码
void SelectSort(vector<int> &vec)
{
    int n = vec.size();
    for (int i = 0; i < n - 1; ++i)
    {
        int minPos = i;
        //查找最小值
        for (int j = i + 1; j < n; ++j)
        {
            if (vec[j] < vec[minPos])
                minPos = j;
        }

        if (minPos != i)
        {
            int tmp = vec[i];
            vec[i] = vec[minPos];
            vec[minPos] = tmp;
        }
    }
}
复制代码

 

4.直接插入排序

为了实现N个数的排序,将后面N-1个数依次插入到前面已排好的子序列中,假定刚开始第1个数是一个已排好序的子序列,那么经过N-1趟就能得到一个有序序列。

复制代码
void InsertSort(vector<int> &vec)
{
    int n = vec.size();
    int tmp, j;
    for (int i = 1; i < n; ++i)
    {
        tmp = vec[i];
        for (j = i - 1; j >= 0 && tmp < vec[j]; --j)
        {
            vec[j + 1] = vec[j];
        }
        vec[j + 1] = tmp;
    }
}
复制代码

 

5.希尔排序

希尔排序(Shell Sort)是插入排序的一种,是对直接插入排序算法的改进该方法又称缩小增量排序。希尔排序通过比较相距一定间隔的元素,即形如L[i,i+d,i+2d,...i+kd]的序列然后缩小间距,再对各分组序列进行排序。直到只比较相邻元素的最后一趟排序为止,即最后的间距为1。

复制代码
void ShellSort(vector<int> &vec)
{
    int n = vec.size();
    int i, j, tmp;
    int d = n / 2;//分成n/2组
    while (d >= 1)
    {
        //对每组进行直接插入排序
        for (int i = d; i < n; ++i)
        {
            tmp = vec[i];
            for (j = i - d; j >= 0 && tmp < vec[j]; j -= d)
            {
                vec[j + d] = vec[j];
            }
            vec[j + d] = tmp;
        }
        d /= 2;
    }
}
复制代码

 

6.合并排序

合并排序采用分治法,思路是将两个或以上的有序表合并为一个有序表,把待排序的序列分割为若干个子序列,每个子序列有序,然后再把子序列合并为一个有序序列。若将两个有序表合并成一个有序表,则成为2路合并排序。
2-路归并就是将2个有序表组合成一个新的有序表。假定待排序表有n个元素,则可以看成是n个有序的子表,每个子表长度为1,然后两两归并...不停重复,直到合成一个长度为n的有序序列为止。Merge()函数是将前后相邻的两个有序表归并为一个有序表,设A[low...mid]和A[mid+1...high]存放在同一顺序表的相邻位置上,先将它们复制到辅助数组B中。每次从对应B中的两个段取出一个元素进行比较,将较小者放入A中。
复制代码
//将两个有序序列vec[low..mid] vec[mid..high]合并
void Merge(vector<int> &vec, vector<int> &tmpArray, int low, int mid, int high)
{
    int i = low, j = mid + 1;
    int m = mid, n = high;
    int k = 0;
    while (i <= m && j <= n)
    {
        if (vec[i] <= vec[j])
            tmpArray[k++] = vec[i++];
        else
            tmpArray[k++] = vec[j++];
    }
    while (i <= m)
    {
        tmpArray[k++] = vec[i++];
    }
    while (j <= n)
    {
        tmpArray[k++] = vec[j++];
    }

    for (i = 0; i < k; ++i)
    {
        vec[low + i] = tmpArray[i];
    }
}

void MergeSort(vector<int> &vec, vector<int> &tmpArray, int low, int high)
{
    if (low < high)
    {
        int mid = low + (high - low) / 2;
        MergeSort(vec, tmpArray, low, mid);
        MergeSort(vec, tmpArray, mid + 1, high);
        Merge(vec, tmpArray, low, mid, high);
    }
}

void MergeSort(vector<int> &vec)
{
    int n = vec.size();
    vector<int> tmpArray(n);
    MergeSort(vec, tmpArray, 0, n - 1);
}
复制代码

 

7.堆排序

堆排序是一种树形选择排序方法,在排序过程中,将L[n]看成是一棵完全二叉树的顺序存储结构,利用完全二叉树中双亲节点和孩子节点之间的内在关系,在当前无序区中选择关键字最大(或最小)的元素。堆排序的思路是:首先将序列L[n]的n个元素建成初始堆,由于堆本身的特点(以大根堆为例),堆顶元素就是最大值。输出堆顶元素后,通常将堆底元素送入堆顶,此时根结点已不满足大根堆的性质,堆被破坏,将堆顶元素向下调整使其继续保持大根堆的性质,再输出堆顶元素。如此重复,直到堆中仅剩下一个元素为止。
 

 

复制代码
//使用数组二叉树 数组实现的堆中,第N个节点的左孩子的索引值是(2N+1),右孩子的索引是(2N+2)
void HeapAdjust(vector<int> &vec, int root, int size)
{
    int child = 2 * root + 1;//左孩子
    if (child <= size - 1)//有左孩子
    {
        int rightChild = child + 1;
        if (rightChild <= size - 1)//有右孩子
            if (vec[child] < vec[rightChild])
                child = rightChild;
        if (vec[root] < vec[child])
        {
            int tmp = vec[child];
            vec[child] = vec[root];
            vec[root] = tmp;
            HeapAdjust(vec, child,size);
        }
    }
}

void HeapSort(vector<int> &vec)
{
    int size = vec.size();
    for (int i = size / 2 - 1; i >= 0; --i)
    {
        HeapAdjust(vec, i,size);
    }
    
    for (int i = size - 1; i > 0; --i)
    {
        int tmp = vec[0];
        vec[0] = vec[i];
        vec[i] = tmp;
        HeapAdjust(vec, 0, i);
    }    
}
复制代码

 

8.桶排序

假设输入是由一个随机过程产生的[0, 1)区间上均匀分布的实数。将区间[0, 1)划分为n个大小相等的子区间(桶),每桶大小1/n:[0, 1/n), [1/n, 2/n), [2/n, 3/n),…,[k/n, (k+1)/n ),…将n个输入元素分配到这些桶中,对桶中元素进行排序,然后依次连接桶输入0 ≤A[1..n] <1辅助数组B[0..n-1]是一指针数组,指向桶(链表)

9.基数排序

基数排序可以说是桶排序的一种改进和推广,它不需要比较关键字的大小。它是根据关键字中各位的值,通过对排序的N个元素进行若干趟“分配”与“收集”来实现排序的。

由于比较简单,这里就不给出代码了

 

10.计数排序

计数排序是一个类似于桶排序的排序算法,其优势是对已知数量范围的数组进行排序。它创建一个长度为这个数据范围的数组C,C中每个元素记录要排序数组中对应记录的出现个数。基本思想是对于给定的输入序列中的每一个元素x,确定该序列中值小于x的元素的个数。一旦有了这个信息,就可以将x直接存放到最终的输出序列的正确位置上。

计数排序很快,O(n)的时间复杂度,但是我们平时还是用的很少,是因为它需要一个至少等于待排序数组取值范围的缓冲区,而且通常只能用于正整数。

复制代码
int Max(vector<int> &vec)
{
    if (vec.empty())
        return -1;
    int max = vec[0];
    for (auto num : vec)
    {
        if (max < num)
            max = num;
    }
    return max;
}

void CountSort(vector<int> &vec)
{
    int size = vec.size();
    int max = Max(vec);
    vector<int> countingArray(max+1);
    for (int i = 0; i < size; ++i)
    {
        countingArray[vec[i]]++;
    }

    int cur = 0, num = 0;
    while (cur < size)
    {
        while (countingArray[num] > 0)
        {
            vec[cur] = num;
            countingArray[num]--;
            cur++;
            if (cur >= size)
                break;
        }
        num++;
    }
}
复制代码

 

 

以上是关于排序算法总结的主要内容,如果未能解决你的问题,请参考以下文章

排序算法总结

排序算法总结——冒泡排序与鸡尾酒排序

排序算法总结:冒泡排序

常用排序算法总结1一一冒泡排序

常用排序算法总结1一一冒泡排序

算法——排序算法个人总结