『数据结构与算法』解读排序算法(C++版本)!

解读排序算法(C++版本)!

一. 排序的基本概念

1.1. 排序的定义

所谓排序,就是整理表中的记录,使之按关键字递增或者递减有序排列(说明:排序可以存在相同关键字的记录)。

1.2. 内排序和外排序

1.3. 稳定性及复杂度

►常见的快速排序、归并排序、堆排序、冒泡排序等属于比较排序 。在排序的最终结果里,元素之间的次序依赖于它们之间的比较。每个数都必须和其他数进行比较,才能确定自己的位置。
计数排序、基数排序、桶排序则属于非比较排序。非比较排序是通过确定每个元素之前,应该有多少个元素来排序。针对数组 a r r arr arr,计算 a r r [ i ] arr[i] arr[i] 之前有多少个元素,则唯一确定了 a r r [ i ] arr[i] arr[i] 在排序后数组中的位置。非比较排序只要确定每个元素之前的已有的元素个数即可,所有一次遍历即可解决。算法时间复杂度 O ( n ) O(n) O(n)

二. 插入排序

►基本思路如下:

2.1. 直接插入排序

2.1.1. 算法实现

#include <iostream>
#include <vector>
using namespace std;
class Solution
{
public:
    bool insertSort1(vector<int> &arr) //1、对R[0,...,n-1]按递增有序进行直接插入排序
    {
        if (arr.size() == 0)
            return false;
        int i, j, tmp;
        for (i = 1; i < arr.size(); i++)
        {
            tmp = arr[i];
            j = i - 1; //从右往左再有序区R[0,...,i-1]中找R[i]的插入位置;
            while (j >= 0 && tmp < arr[j])
            {
                arr[j + 1] = arr[j]; //将大于R[i]的关键字后移;
                j--;
            }
            arr[j + 1] = tmp; //在R[j+1]处插入R[i];
        }
        return true;
    }

    bool insertSort2(vector<int> &arr) //2、全部利用for循环
    {
        if (arr.size() == 0)
            return false;
        int tmp;
        for (int i = 1; i < arr.size(); i++)
            for (int j = i; j > 0 && arr[j] < arr[j - 1]; j--)
            { //交换
                tmp = arr[j];
                arr[j] = arr[j - 1];
                arr[j - 1] = tmp;
            }
        return true;
    }
};
int main()
{
    Solution sol;
    vector<int> arr{5, 3, 6, 8, 1, 7, 9, 4, 2};
    if (sol.insertSort1(arr))
        for (int i = 0; i < arr.size(); i++)
            cout << arr[i] << " ";
    cout << endl;
    system("pause");
    return 0;
}
1 2 3 4 5 6 7 8 9
请按任意键继续. . .

2.1.2. 算法分析

2.2. 希尔排序

2.2.1. 算法实现

#include <iostream>
#include <vector>
using namespace std;
class Solution
{
public:
    bool insertSort1(vector<int> &arr)
    {
        if (arr.size() == 0)
            return false;
        int tmp, j, len = arr.size();
        int d = len / 2; //增量设置初始值;d = len >> 1; //效率更高,右移;
        while (d > 0)
        {
            for (int i = d; i < len; i++) //对相隔d位置的元素组直接插入排序
            {
                tmp = arr[i];
                j = i - d;
                while (j >= 0 && tmp < arr[j])
                {
                    arr[j + d] = arr[j];
                    j -= d;
                }
                arr[j + d] = tmp;
            }
            d /= 2; //减小增量;
        }
        return true;
    }
};
int main()
{
    Solution sol;
    vector<int> arr{5, 3, 6, 8, 1, 7, 9, 4, 2};
    if (sol.insertSort1(arr))
        for (int i = 0; i < arr.size(); i++)
            cout << arr[i] << " ";
    cout << endl;
    system("pause");
    return 0;
}
1 2 3 4 5 6 7 8 9
请按任意键继续. . .

2.2.2. 算法分析

三. 选择排序

3.1. 直接选择排序

3.1.1. 算法实现

#include <iostream>
#include <vector>
using namespace std;
class Solution
{
public:
    bool selectSort(vector<int> &arr)
    {
        if (arr.size() == 0)
            return false;
        int i, j, tmp;
        for (i = 0; i < arr.size() - 1; i++)
        {
            int min_index = i; //每一趟选择中,先假设起始位置元素值最小;
            for (j = i + 1; j < arr.size(); j++)
            {
                /*  if (arr[min_index] > arr[j])
                    min_index = j;*/
                min_index = arr[min_index] > arr[j] ? j : min_index; //如果当前位置元素小于minPos位置元素,要更新;
            }
            if (min_index != i) //交换位置
            {
                tmp = arr[min_index];
                arr[min_index] = arr[i];
                arr[i] = tmp;
            }
        }
        return true;
    }
};
int main()
{
    Solution sol;
    vector<int> arr{5, 3, 6, 8, 1, 7, 9, 4, 2};
    if (sol.selectSort(arr))
        for (int i = 0; i < arr.size(); i++)
            cout << arr[i] << " ";
    cout << endl;
    system("pause");
    return 0;
}
1 2 3 4 5 6 7 8 9
请按任意键继续. . .

3.1.2. 算法分析

3.2. 堆排序

堆的定义:

完全二叉树角度理解:

3.2.1. 算法实现

堆排序算法设计: 堆排序的关键是构造堆,这里采用筛选算法建堆(这里按照大根堆来)。
注意:这里只有根节点不满足大根堆,左右子树都满足了大根堆。

筛选算法:

筛选建立大根堆的过程(左右子树都是大根堆,只有根节点不是)

筛选算法代码实现如下:采用了2种方式迭代策略和递归策略

  • 方式1:迭代策略
/*===========1、筛选或者调整堆的算法 ===========*/
void shif(vector<int> &arr, int index, int size)
{
    int i = index, j = 2 * i + 1; //R[j]是R[i]的左孩子;
    int tmp = arr[i];             //临时变量存放根节点;
    while (j < size)
    {
        if (j < size - 1 && arr[j] < arr[j + 1])
            j++;          //j指向大孩子;
        if (tmp < arr[j]) //双亲小
        {
            arr[i] = arr[j]; //将R[j]的值调整到双亲节点上;
            i = j;
            j = 2 * i + 1;
        }
        else
            break; //双亲大,不需要调整;
    }
    arr[i] = tmp; //放原来根节点的记录;
}
  • 方式2:递归策略
void shif(vector<int> &arr, int index, int size)
{
    int left = 2 * index + 1;
    int right = left + 1;
    int maxIndex = index;
    if (left < size && arr[left] > arr[maxIndex])
        maxIndex = left;
    if (right < size && arr[right] > arr[maxIndex])
        maxIndex = right;
    if (maxIndex != index)
    {
        swap(arr[maxIndex], arr[index]);
        shif(arr, maxIndex, size);
    }
}

注意:下面算法实现的过程中考虑到vector向量的下标是从0开始的,与上面的分析稍微有点差别!

#include <iostream>
#include <vector>
using namespace std;
class Solution
{
public:
    /*===========1、堆排序算法 =====================*/
    void heapSort(vector<int> &arr)
    {
        int len = arr.size();
        buildHeap(arr, len);
        for (int i = len - 1; i >= 1; i--)
        {
            swap(arr[0], arr[i]); // 将当前最大的放置到数组末尾;
            shift(arr, 0, i);     //筛选arr[0]节点到arr[i]间节点,构造大根堆;
        }
    }
    /*===========2、循环建立大根堆 ==================*/
    void buildHeap(vector<int> &arr, int len)
    {
        for (int i = len / 2 + 1; i >= 0; i--) //从最后一个非叶子节点向上,直到第一节点;
            shift(arr, i, len);
    }
    /*===========3、筛选或者调整堆的算法(递归策略) ============*/
    void shift(vector<int> &arr, int i, int len)
    {
        int left = 2 * i + 1;                        //左孩子索引;
        int right = 2 * i + 2;                       //右孩子索引;
        int maxIndex = i;                            //假定父节点为最大索引;
        if (left < len && arr[left] > arr[maxIndex]) //左孩子存在,并且大于父节点;
            maxIndex = left;
        if (right < len && arr[right] > arr[maxIndex]) //右孩子存在并且大于根节点;
            maxIndex = right;
        if (maxIndex != i)
        {
            swap(arr[maxIndex], arr[i]);
            shift(arr, maxIndex, len); //左子树或者右子树递归调用;
        }
    }
    // /*===========3、筛选或者调整堆的算法(迭代策略) ============*/
    // void shift(vector<int> &arr, int index, int len)
    // {
    //     int i = index, j = 2 * i + 1; //arr[j]arr[i]的左孩子;
    //     int tmp = arr[i];             //临时变量存放根节点;
    //     while (j < len)
    //     {
    //         if (j < len - 1 && arr[j] < arr[j + 1]) //纯在右孩子且右孩子值大;
    //             j++;          //j指向右孩子;
    //         if (tmp < arr[j]) //双亲小
    //         {
    //             arr[i] = arr[j]; //将R[j]的值调整到双亲节点上;
    //             i = j;
    //             j = 2 * i + 1;
    //         }
    //         else
    //             break; //双亲大,不需要调整;
    //     }
    //     arr[i] = tmp; //放原来根节点的记录;
    // }
    /*===========4、交换元素 =======================*/
    void swap(int &a, int &b)
    {
        int tmp = a;
        a = b;
        b = tmp;
    }
};
int main()
{
    Solution sol;
    vector<int> arr{4, 3, 5, 2, 1, 6};
    sol.heapSort(arr); //注意vector的下标是从0开始的;
    for (int i = 0; i < arr.size(); i++)
        cout << arr[i] << " ";
    cout << endl;
    system("pause");
    return 0;
}
1 2 3 4 5 6
请按任意键继续. . .

例子: 设待排序的表有10个记录,其关键字分别为 { 6 , 8 , 7 , 9 , 0 , 1 , 3 , 2 , 4 , 5 } \{6,8,7,9,0,1,3,2,4,5\} {6,8,7,9,0,1,3,2,4,5}。说明采用堆排序方法进行的过程。

3.2.2. 算法分析

四. 交换排序

4.1. 冒泡排序

例如下图中: 7 7 7 个数字需要 6 6 6 趟,第 1 1 1 趟需要比较 6 6 6,第 2 2 2 次需要比较 5 5 5,一直到第 6 6 6 趟比较 1 1 1 次,也就是第 i i i 趟需要比较 7 − i 7-i 7i 次;

4.1.1. 算法实现

#include <iostream>
#include <vector>
using namespace std;
class Solution
{
public:
    bool bubbleSort(vector<int> &arr)
    {
        if (arr.size() == 0)
            return false;
        int i, j, tmp, len = arr.size();
        for (i = 0; i < len - 1; i++)         //趟数,n个数,只需要n-1趟就够了;
            for (j = 0; j < len - 1 - i; j++) //比较次数;
            {
                if (arr[j] > arr[j + 1])
                {
                    tmp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = tmp;
                }
            }
        return true;
    }
};
int main()
{
    Solution sol;
    vector<int> arr{5, 3, 6, 8, 1, 7, 9, 4, 2};
    if (sol.bubbleSort(arr))
        for (int i = 0; i < arr.size(); i++)
            cout << arr[i] << " ";
    cout << endl;
    system("pause");
    return 0;
}
1 2 3 4 5 6 7 8 9
请按任意键继续. . .

4.1.2. 算法改进

#include <iostream>
#include <vector>
using namespace std;
class Solution
{
public:
    bool bubbleSort(vector<int> &arr)
    {
        if (arr.size() == 0)
            return false;
        bool flag = true;
        int i, j, tmp, len = arr.size();
        for (i = 0; i < len - 1; i++) //趟数,n个数,只需要n-1趟就够了;
        {
            for (j = 0; j < len - 1 - i; j++) //比较次数;
            {
                if (arr[j] > arr[j + 1])
                {
                    tmp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = tmp;
                    flag = false;
                }
            }
            if (flag)
                break; //中途结束循环
        }
        return true;
    }
};
int main()
{
    Solution sol;
    vector<int> arr{5, 3, 6, 8, 1, 7, 9, 4, 2};
    if (sol.bubbleSort(arr))
        for (int i = 0; i < arr.size(); i++)
            cout << arr[i] << " ";
    cout << endl;
    system("pause");
    return 0;
}

4.1.3. 算法分析

4.2. 快速排序

►快速排序是由冒泡排序改进而来的,基本思想: 在待排序的那个元素中任取一个元素(通常是第一个元素)作为基准,把该元素放入适当的位置后,数据序列被此元素划分为两部分,所有关键字比该元素关键字小的元素放置在前一部分,所有关键字比它大的元素放置在后一部分,并把元素排在这2部分的中间(称为元素归为),这个过程是一趟快速排序,之后对所有划分出来的两部分分别重复上述过程,直至每部分内只有一个元素或为空为止,简而言之,每趟将表的第一个元素放入适当位置,将表一分为二,对子表按照递推方式继续这种划分,直至划分的子表长为1或者0。

快速排序过程!

4.2.1. 算法实现

#include <iostream>
#include <vector>
using namespace std;
class Solution
{
public:
    void quickSort(vector<int> &arr, int low, int high) //low开始,high结束;
    {
        int i = low, j = high, tmp;
        if (low < high) //归体,看递归树,每次递归划分的视乎区间至少存在2个元素的情况;或者使用low!=high
        {
            tmp = arr[low]; //用区间的第一个元素作为基准;
            while (i != j)  //看做2个指针从两端交替向中间扫描,直至i=j为止;
            {
                while (j > i && arr[j] > tmp)
                    j--;
                arr[i] = arr[j];
                while (i < j && arr[i] < tmp)
                    i++;
                arr[j] = arr[i];
            }
            arr[i] = tmp;
            quickSort(arr, low, i - 1);  //对左区间递归排序;
            quickSort(arr, i + 1, high); //对右区间递归排序;
        }
        //递归出口,不需要任何操作;
    }
};
int main()
{
    Solution sol;
    vector<int> arr{5, 3, 6, 8, 1, 7, 9, 4, 2};
    sol.quickSort(arr, 0, arr.size() - 1);
    for (int i = 0; i < arr.size(); i++)
        cout << arr[i] << " ";
    cout << endl;
    system("pause");
    return 0;
}
1 2 3 4 5 6 7 8 9
请按任意键继续. . .

4.2.2. 算法分析

五. 归并排序

5.1. 算法实现

  • merge():一次二路归并,将2个相邻的有序子序列归并为一个有序序列。空间复杂度为O(high-low+1)(开辟了一个新的空间,用来存放排好序的元素);
  • mergeSort():递归进行;
#include <iostream>
#include <vector>
using namespace std;
class Solution
{
public:
    /*=========1、二路归并,将2个相邻的有序子序列归并为一个有序序列。=========*/
    void merge(vector<int> &arr, int low, int mid, int high)
    {
        int i = low, j = mid + 1, k = 0; //k是R1的下标,i、j分别是第1、2段的下标。
        vector<int> R1(high - low + 1);  //空间复杂度O(high-low+1)
        while (i <= mid && j <= high)
        {
            if (arr[i] <= arr[j]) //将第1段中的记录放到R1中;
            {
                R1[k] = arr[i];
                i++;
                k++;
            }
            else //将第2段中的记录放到R1中;
            {
                R1[k] = arr[j];
                j++;
                k++;
            }
        }
        while (i <= mid) //将第1段中余下部分复制到R1中;
        {
            R1[k] = arr[i];
            i++;
            k++;
        }
        while (j <= high) //将第2段中余下部分复制到R1中;
        {
            R1[k] = arr[j];
            j++;
            k++;
        }
        //将R1复制回R中;
        for (k = 0, i = low; i <= high; k++, i++)
            arr[i] = R1[k];
        R1.shrink_to_fit(); // R1最小化它的容量;
    }
    /*=========2、递归排序 =========*/
    void mergeSort(vector<int> &arr, int low, int high)
    {
        if (low < high) //或者low!=high都可以,只要保证,拆分到单个节点就可以,此时low=high;
        {
            /*不用(upper+low)/2,避免upper+low溢出(因为low+upper都是整形数据,是有界限的)*/
            int mid = low + (high - low) / 2;
            mergeSort(arr, low, mid);
            mergeSort(arr, mid + 1, high);
            merge(arr, low, mid, high);
        }
    }
};
int main()
{
    Solution sol;
    vector<int> arr{5, 3, 6, 8, 1, 7, 9, 4, 2};
    sol.mergeSort(arr, 0, arr.size() - 1);
    for (int i = 0; i < arr.size(); i++)
        cout << arr[i] << " ";
    cout << endl;
    system("pause");
    return 0;
}
1 2 3 4 5 6 7 8 9
请按任意键继续. . .

5.2. 算法分析

六. 基数排序

6.1. 算法实现

6.2. 算法分析

七. 各种内排序总结

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

AI新视界

感谢您的打赏,我会继续努力!

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

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

打赏作者

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

抵扣说明:

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

余额充值