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

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

ShellSort.h

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

希尔排序

它的做法是:

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

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

直到间隔为1

比如10个元素

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

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

最后整体插入排序

HeapSort.h

 1 #pragma once
 2 #include "swap.h"
 3 #include <vector>
 4
 5 template <typename 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 < v.size(); i += (i + 1) * 2 - 1)
10     {
11         if (i < m && 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 <typename T>
22 void HeapSort(vector<T> &v) {
23     for (auto i = v.size() / 2; i >= 0; i--)
24         HeapAdjust(v, i, v.size());
25     for (auto i = v.size(); i > 0; i--)
26     {
27         swapLHW(v, 1, i);
28         HeapAdjust(v, 1, i - 1);
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
 5 template <typename 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; k++)
 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 < n)
19         for (; j <= t; j++)
20             v2[k] = v1[j];
21
22 }
23
24 template <typename 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 <typename T>
40 void MergingSort(vector<T> &v) {
41     MSort(v, v, 1, v.size());
42 }
43
44 template <typename 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 <typename 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());
72         k *= 2;
73         MergePass(Vtmp, v, k, v.size());
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
 5 template <typename 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 <typename 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 <typename T>
37 void QuickSort(vector<T> &v) {
38     QSort(v, 1, 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

时间: 2024-10-11 12:48:12

数据结构拾遗——排序(时间复杂度O(nlogn)的相关文章

数据结构-各类排序算法总结[结局]

各类排序算法总结 五.分配类排序->基数排序: 基数排序是一种借助于多关键码排序的思想,是将单关键码按基数分成"多关键码"进行排序的方法.基数排序属于"低位优先"排序法,通过反复进行分配与收集操作完成排序. 对于数字型或字符型的单关键字,可以看成是由多个数位或多个字符构成的多关键字, 此时可以采用这种"分配-收集"的办法进行排序,称作基数排序法.其好处是不需要进行关键字间的比较. 例如:对下列这组关键字{278, 109, 063, 930

数据结构之排序

1.排序的分类 在待排序的文件中,若存在多个关键字相同的记录,经过排序后这些具有相同关键字的记录之间的相对次序保持不变,该排序方法是稳定的:若具有相同关键字的记录之间的相对次序发生变化,则称这种排序方法是不稳定的. 稳定的排序 时间复杂度 空间复杂度 冒泡排序 最差和平均是O(n2),最好是O(n)    1  双向冒泡排序 最差和平均是O(n2),最好是O(n)  1 插入排序 最差和平均是O(n2),最好是O(n)   1 归并排序  最差.平均.最好都是O(nlogn) O(n)  桶排序

数据结构-各类排序算法总结[续]

各类排序算法总结 三.交换类排序[接上] 2.快速排序 快速排序是通过比较关键码.交换记录,以某个记录为界(该记录称为支点),将待排序列分成两部分.其中,一部分所有记录的关键码大于等于支点记录的关键码,另一部分所有记录的关键码小于支点记录的关键码.我们将待排序列按关键码以支点记录分成两部分的过程,称为一次划分.对各部分不断划分,直到整个序列按关键码有序. 如果每次划分对一个元素定位后,该元素的左侧子序列与右侧子序列的长度相同,则下一步将是对两个长度减半的子序列进行排序,这是最理想的情况! [算法

数据结构-各类排序算法总结

各类排序算法总结 一. 排序的基本概念 排序(Sorting)是计算机程序设计中的一种重要操作,其功能是对一个数据元素集合或序列重新排列成一个按数据元素某个项值有序的序列. 有 n 个记录的序列{R1,R2,-,Rn},其相应关键字的序列是{K1,K2,-,Kn},相应的下标序列为1,2,-,n.通过排序,要求找出当前下标序列1,2,-, n 的一种排列p1,p2, -,pn,使得相应关键字满足如下的非递减(或非递增)关系,即:Kp1≤Kp2≤-≤Kpn,这样就得到一个按关键字有序的记录序列{R

数据结构 - 希尔排序(Shell&amp;#39;s Sort) 具体解释 及 代码(C++)

数据结构 - 希尔排序(Shell's Sort) 具体解释 及 代码(C++) 本文地址: http://blog.csdn.net/caroline_wendy/article/details/24363981 希尔排序(Shell's Sort), 又称"缩小增量排序"(Diminishing Increment Sort), 是插入排序的一种. 主要思想是: 先将整个记录, 通过间隔分成若干个子序列, 分别进行插入排序, 待整个序列基本有序时, 再进行一次插入排序. 由于插入排

【数据结构】——排序算法——3.1、选择排序

      [数据结构]--排序算法--3.1.选择排序 一.先上维基的图: 分类 排序算法 数据结构 数组 最差时间复杂度 О(n2) 最优时间复杂度 О(n2) 平均时间复杂度 О(n2) 最差空间复杂度 О(n) total, O(1)auxiliary 二.描述: 选择算法算是最直观的一个了.每次在队列里抽取一个极大(或极小)值进行排列.每次都需要遍历未被抽取的元素队列. 三.Java程序: static void selection_sort(int[] unsorted) { for

【数据结构】——排序算法——1.1、直接插入排序

插入算法很多,无论是在内功修炼,各种笔试面试都是相当有用的.接下来,将陆续将各种排序算法进行练习: 主要分为以下几个部分(其他后面学习补充): 一.插入类排序:1.直接插入排序:2.折半插入排序:3.希尔shell排序: 二.交换类排序:1.冒泡排序 :2.快速排序: 三.选择类排序:1.简单选择: 2.堆排序: 本人多使用Java--开始吧! 首先推荐1.维基百科<排序算法>词条,图文并茂,很形象!2.学习博文<维基百科上的算法和数据结构链接很强大>,资料很多,保存学习! [数据

《C算法.第1卷,基础、数据结构、排序和搜索(第三版)》pdf

下载地址:网盘下载 内容简介  · · · · · · <C算法>介绍了当今最重要的算法,共分3卷,<C算法(第1卷):基础.数据结构.排序和摸索>是第1卷.第1卷分4部分.共16章.第一部分"基础知识"(第1-2章)介绍了基本算法分析原理.第二部分"数据结构"(第3-5章)讲解算法分析中必须掌握的数据结构知识.主要包括基本数据结构.抽象数据结构.递归和树.第三部分"排序"(第6-11章)按章节顺序分别讨论了基本排序方法(

Java数据结构与排序

一.引子:想要给ArrayList排序却发现没有排序方法?你有两种选择:        1.换用TreeSet:     2.使用Collection.sort(List<T> list) / Collection.sort(List<T> list, Comparator<? super T> c)方法. 二.展开分析Java的几种主要数据结构及其排序方法: LinkedList 高效操作元素 TreeSet  不重复,有序 HashSet 不重复,快速查找 Hash