经典算法总结(C++)

几种经典的算法在工作中一直用不到,很容易忘记,所以还是总结一下。
先实现swap函数:

void swap(int &a, int &b)
{
    int temp = a;
    a = b;
    b = temp;
}

冒泡排序(Bubble sort)

初级的冒泡排序

也是最容易想到的算法,从第一个数开始,让之后的每一个数与其比较,如果后者比前者小,则二者交换,一直到最后一个数。

现有一组数{4, 9, 10, 2, 32, 6},排序过程如下:

4 9 10 2 32 6
// 省略9,10的比较
2 9 10 4 32 6
// 省略32,6的比较

//第一个数成为最小的数,开始处理第二个数 9
......

代码如下:

void BubbleSort(QVector<int> &a)
{
    int n = a.size();
    int temp;
    for(int i=0; i<n-1;i++)
    {
        for(int j=i+1;j<n;j++)
        {
            if(a[i]>a[j])
            {
                swap(a[i],a[j]);
            }
        }
    }
}

看起来像把最小的数“冒”到最左边,这种算法的效率很低,时间复杂度为O(n²)

标准的冒泡排序

从第一个数开始,每次从左到右两两比较,看起来像把最大的数“冒”到最右边。
同样是上面的数列,排序过程如下:

4 9 10 2 32 6
// 4和9
// 9和10
4 9 2 10 32 6
// 10和32
4 9 2 10 6 32
/* 上面是第一趟,排序n-1次,n为数的个数*/

以此类推,第n趟只需排序1次,所以总计n(n-1)/2次,时间复杂度为O(n²),空间复杂度是O(n)

代码:

void BubbleSort(QVector<int> &a)
{
    int n = a.size();
    int temp;
    // bool flag = true;
    for(int i=1; i<n+1 && flag;i++)    // i代表第几趟
    {
        // flag = false;
        for(int j=0; j<n-i;j++)     // n-i 代表这一趟需要排序几次
        {
            if(a[j]>a[j+1])
            {
                swap(a[j],a[j+1]);
                // flag = true;
            }
        }
    }
}

还可以对算法做出改进,增加标志位判断,若有交换数值则flag为true,说明这一步有排序,否则就是已经排好了,减少多余步骤。

选择排序

选择排序的特点是,第一趟的交换发生在找出最小元素之后,而不像初级冒泡那样每找到一个较小的数就跟第一个交换。排序过程的评价元素其实是数组的下标,对比的值是下标的元素,而不是某个固定位置。
从下面的数列看交换过程:

4  9  10  2  32  6  1

// 先假设最小值的下标是min, 最开始的min=0 然后从第二个数开始比较
// 9>4,10>4 不处理
// 2<4, 交换下标,现在min=3
// 从32开始,与min对应的值比较,因为已经比较的数肯定是大于min的元素的
// 32和6都大于2,不处理
// 1<2,交换下标,现在min=6。 现在可以说数列中的最小值是a[6]

第一趟结束,然后交换a[6]和a[0]。

代码如下:

void MainWindow::sort(QVector<int> &a)
{
    int n = a.size();
    int min;
    for(int i=0; i<n-1;i++)
    {
        min = i;  // min保存最小元素的下标,假设刚开始位置i的元素最小,然后和之后每个元素比较
        for(int j=i+1;j<n;j++)
        {
            // 只要后面的值更小就更新min,这样一趟循环下来min肯定是最小元素下标
            if(a[j]<a[min])
            {
                min = j;
            }
        }
        if(min!=i)  // 如果最初的元素就是最小值,无需交换
        {
            swap(a[min],a[i]);
        }
    }
}

这种方法的核心就是更新min下标,与初级冒泡相比,不必访问已经有序的元素。时间复杂度还是O(n²)
简单选择排序是不稳定的排序算法,举个例子 [5, 4, 5, 3, 2],第一轮会将最下的2与数组第一个位置的5交换,两个等值的5相对位置发生了改变。

插入排序

插入排序的思路是从扑克牌的排序而来的。基本思想是:每次将一个待排序的记录,按其关键字大小插入到前面已经排好序的子文件中的适当位置,直到全部记录插入完成为止。
假设左手拿了几张未排序的牌,我们将第一张放到右手,然后从第二张开始,与右手的牌进行比较,选择合适的位置插入。插入排序就相当于现在只有一个手,将一组数分成两部分,左边为sorted,右边unsorted,从第二个数开始,与左侧的数逐个比较,如果比左边的数小,就进行交换,仍然以上面的数列,为例,排序过程如下:
《经典算法总结(C++)》

代码如下:

void InsertSort(QVector<int> &a)
{
    int n = a.size();
    int value,j;
    for(int i=1; i<n;i++)
    {
        j=i;    //从第二个数开始比较
        //利用了快捷运算,j成为0时,j>0为假,不必再算后面的比较。
        //如果交换二者,会报错,因为a[j-1]下标为-1
        while(j>0 && a[j]<a[j-1])
        {
            swap(a[j],a[j-1]);
            j--;
        }
    }
}

最好情况的时间复杂度为O(n),最坏情况的时间复杂度为O(n²). 是一种稳定排序方法。

    原文作者:SilentAssassin
    原文地址: https://blog.csdn.net/yao5hed/article/details/81092226
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞