• 数据结构拾遗——排序(时间复杂度O(nlogn))


    之前几个排序时间复杂度是n方,接下来这几个速度就要比较快了

    ShellSort.h

     1 #pragma once
     2 #include "swap.h"
     3 #include <vector>
     4 using namespace std;
     5 template <class T>
     6 void ShellSort(vector<T> &v) {
     7     int increment = v.size(), j;
     8     do 
     9     {
    10         increment = increment / 3 + 1;
    11         for (auto i = increment; i < v.size(); i++)
    12         {
    13             if (v[i] < v[i - increment])
    14             {
    15                 T tmp = v[i];
    16                 j = i;
    17                 for (; j - increment >= 0 && tmp < v[j - increment]; j -= increment)
    18                 {
    19                     v[j] = v[j - increment];
    20                 }
    21                 v[j] = tmp;
    22             }
    23         }
    24     } while (increment > 1);
    25 
    26 }

    希尔排序

    它的做法是:

    将间隔的元素看作一个子序列,对其进行插入排序

    缩小间隔,继续对子序列排序

    直到间隔为1

    比如10个元素

    将147 10,258,369分别插入排序

    然后对13579,2468 10分别插入排序

    最后整体插入排序

    HeapSort.h

     1 #pragma once
     2 #include "swap.h"
     3 #include <vector>
     4 using namespace std;
     5 template <class T>
     6 void HeapAdjust(vector<T> &v, int s, int m) {
     7     T tmp;
     8     tmp = v[s];
     9     for (auto i = 2 * (s + 1) - 1; i < m; i = (i + 1) * 2 - 1)
    10     {
    11         if (i < m - 1 && v[i] < v[i + 1])
    12             i++;
    13         if (tmp >=v[i])
    14             break;
    15         v[s] = v[i];
    16         s = i;
    17     }
    18     v[s] = tmp;
    19 }
    20 
    21 template <class T>
    22 void HeapSort(vector<T> &v) {
    23     for (int i = v.size() / 2 - 1; i >= 0; i--)
    24         HeapAdjust(v, i, v.size());
    25     for (int i = v.size() - 1; i > 0; i--)
    26     {
    27         swapLHW(v, 0, i);
    28         HeapAdjust(v, 0, i);
    29     }
    30 }

    堆排序

    将数据看成一个堆

    堆是具有下列性质的完全二叉树:

    每个节点的值都大于等于其左右孩子的节点,且右孩子大于等于左孩子,叫做大顶堆

    每个节点的值都小于等于其左右孩子的节点,且右孩子小于等于左孩子,叫做小顶堆

    我们这里用大顶堆

    总体思想是,

    1. 先用HeapAdjust函数将数据修改为大顶堆

    2. 然后把第一个(最大的一个)与最后一个互换

    3. 排除最后一个,重新进行1步骤,直到只剩一个元素为止

    HeapAdjust函数的流程为

    假设输入的序列s到m中,除了s以为其他都复合大顶堆

    则调整s与子树的位置,使s也符合大顶堆

    1. 将s与其左右孩子对比,找到最大的那个

    2. 比较如果s小于孩子,则继续找孩子的孩子,直到s大于某个节点的孩子

    3.s与该节点换位

    MergingSort.h

     1 #pragma once
     2 #include "swap.h"
     3 #include <vector>
     4 using namespace std;
     5 template <class T>
     6 void Merge(vector<T> &v1, vector<T> &v2, int s, int m, int t) {
     7     int i = s, j = m + 1,k = s;
     8     for (; i <= m && j <= t;)
     9     {
    10         if (v1[i] <= v1[j])
    11             v2[k++] = v1[i++];
    12         else
    13             v2[k++] = v1[j++];
    14     }
    15     if (i <= m)
    16         for (; i <= m; i++)
    17             v2[k++] = v1[i];
    18     if (j <= t)
    19         for (; j <= t; j++)
    20             v2[k++] = v1[j];
    21     
    22 }
    23 
    24 template <class T>
    25 void MSort(vector<T> &v1, vector<T> &v2, int s, int t ) {
    26     int m;
    27     vector<T> vTmp(v1, size);
    28     if (s == t)
    29         v2[s] = v1[s];
    30     else
    31     {
    32         m = (s + t) / 2;
    33         MSort(v1, vTmp, s, m);
    34         MSort(v1, vTmp, m + 1, t);
    35         Merge(vTmp, v2, s, m, t);
    36     }
    37 }
    38 
    39 template <class T>
    40 void MergingSort(vector<T> &v) {
    41     MSort(v, v, 1, v.size());
    42 }
    43 
    44 template <class T>
    45 void MergePass(vector<T> &v1, vector<T> &v2, int s, int n) {
    46     int i = 0;
    47     while (i <= n - 2 * s + 1)    //i+2*s-1 <= n
    48     {
    49         Merge(v1, v2, i, i + s - 1, i + 2 * s - 1);    //因为i本身算1个
    50         i += 2 * s;
    51     }
    52     if (i < n - s + 1)    //i+s-1<n
    53     {
    54         Merge(v1, v2, i, i + s - 1, n);
    55     }
    56     else
    57     {
    58         for (auto j = i; j <= n; j++)
    59         {
    60             v2[j] = v1[j];
    61         }
    62     }
    63 }
    64 
    65 template <class T>
    66 void MergingSort2(vector<T> &v) {
    67     vector<T> Vtmp(v.size());
    68     int k = 1;
    69     while (k < v.size())
    70     {
    71         MergePass(v, Vtmp, k, v.size()-1);
    72         k *= 2;
    73         MergePass(Vtmp, v, k, v.size()-1);
    74         k *= 2;
    75     }
    76 }

    归并排序

    首先是一个递归结构

    Msort函数是一个递归函数

    它的目的是将输入的数列(v1)的第s个到第t个进行归并排序,放入v2

    流程是:

    建立一个临时变量tmp

    如果s和t是相同的,则将v1[s]存入v2[s]

    否则将s与t的数列拆成两段(一半)递归调用Msort,将结果存入tmp

    递归返回后使用merge函数将s到t这个子序列按照从小到大的顺序插入v2

    所以归并排序的整体思路是

    先将数列分成2个一组的子序列

    将每组子序列排序

    接着将2个一组的子序列合并为4个一组的子序列,并排序

    一直到将整个数列合并为一组

    merge函数的目的是将输入的数列中的s到m,与m+1到t两个部分合并为一个有序的部分

    所以i和j依次代表遍历两个部分的变量

    比较v[i]与v[j]

    哪个小,推入到新序列,并将i或者j递增

    直到i到达m或者j到达t

    最后检查一下如果前后两段数组还有剩余,则都插入新序列后面

    不过如果使用递归,空间复杂度太高

    所以还提供了一个非递归的MergingSort2

    只需要一个等大的临时变量tmp

    设置一个子序列长度k,先为1

    接着while循环,条件是k小于数组长度

    然后调用MergePass函数

    该函数将v中的元素按照k个为一组,将相邻的2组排序归并为一组

    比如k为1,则将1,2排序归并为1组,2,3排序归并为1组……

     然后k*=2

    再调用MergePass

    最后k*=2

    一次循环结束

    MergePass函数的目的是将v1中的元素按照s个为一组,每两组归并为1组,存入v2,n为数列长度

    首先建立v1的下标i=0

    然后是while循环,直到下标i > n - 2 * s + 1退出循环

    这里需要说明一下,如果i > n - 2 * s + 1,则i+2*s-1>n,下标将越界

    在条件范围内,调用merge函数,将v1中的i到i+s-1与i+s到i+2s归并然后存入v2

    i += 2 * s

    循环结束后,可能会剩下一些元素没有归并

    那么分两种情况:

    1. 如果i < n - s + 1说明i+s-1<n,也就是说剩下元素大于s个

    可以再进行一次归并,只是归并的第一个部分是s个,第二个要小于s

    2. 如果i >= n - s + 1说明i+s-1>=n,也就是说剩下的元素小于等于s个

    不够一次归并,所以直接将剩下的元素插入v2的尾部

    QuickSort.h

     1 #pragma once
     2 #include "swap.h"
     3 #include <vector>
     4 using namespace std;
     5 template <class T>
     6 int Partition(vector<T> &v, int low, int high) {
     7     int key = v[low];
     8     while (low < high)
     9     {
    10         while (low < high && v[high] >= key)
    11         {
    12             high--;
    13         }
    14         swapLHW(v, low, high);
    15         while (low < high && v[low] <= key)
    16         {
    17             low++;
    18         }
    19         swapLHW(v, low, high);
    20     }
    21     return low;
    22 }
    23 
    24 template <class T>
    25 void QSort(vector<T> &v,int low, int high) {
    26     int mid;
    27     if (low < high)
    28     {
    29         mid = Partition(v, low, high);
    30         QSort(v, low, mid - 1);
    31         QSort(v, mid + 1, high);
    32     }
    33 
    34 }
    35 
    36 template <class T>
    37 void QuickSort(vector<T> &v) {
    38     QSort(v, 0, v.size() - 1);
    39 }

    快速排序

    也是一个递归的过程

    Qsort将递归调用自身

    Qsort函数的目的是将输入序列v的第low个元素放置于这个序列中low到high这个子序列中的正确的位置mid

    然后递归调用low,mid和mid+1和high

    所以快速排序的流程就是

    将待排序的数列中第一个元素放置于他合适的位置

    该元素将数列分成两部分

    其左侧所有元素都小于等于它,其右侧所有元素都大于等于它

    左侧部分继续将第一个元素放在正确的位置

    右侧部分同样

    最后将每个元素都放在了正确的位置

    partition函数是将输入的序列的第low个元素放置于这个序列中low到high这个子序列中的正确的位置,并返回这个位置

    具体流程是

    while循环直到low不小于high

    将v[low]保存在tmp中

    比较v[high]的值与tmp的值,如果tmp小则将high递减,直到tmp大于v[high]

    将v[high]与v[low]交换

    比较v[low]的值与tmp的值,如果tmp大则将low递增,直到tmp小于v[low]

    将v[high]与v[low]交换

    循环后返回low

     

    图片来源:大话数据结构,侵删

  • 相关阅读:
    简单的方法爬取b站dnf视频封面步骤解释
    ROS讲座 关于ROS2和Gazebo C++ in Open Source Robotics
    深圳3分钟完成港澳签注 24小时自助办证服务攻略
    如何建立数据平台?看上市公司的选择!
    从开发转型到技术总监的迷茫
    计算机控制技术课程解释与问题答疑
    深度剖析 | 基于大数据架构的BI应用
    Android系统开机启动流程及init进程浅析
    经验分享 | 如何搭建企业管理驾驶舱
    android 修改framework下资源文件后如何编译
  • 原文地址:https://www.cnblogs.com/linhaowei0389/p/6721292.html
Copyright © 2020-2023  润新知