数据结构学习笔记06排序 (快速排序、表排序)

1.快速排序

不稳定

分而治之

找主元pivot,小于主元划分为一个子集,大于主元的划分为一个子集

然后进行递归

最好情况:每次主元正好中分,T(N) = O( NlogN )

选主元 的方法有很多,这里用 取头、中、尾的中位数。

  直接选A[0]为pivot,时间复杂度T ( N ) = O( N ) + T ( N–1 ) = O( N ) + O ( N–1 ) + T( N–2 ) = = O( N ) + O ( N–1 ) + …+ O( 1 ) = O( N^2 )

  随机取pivot:rand()函数 也很费

  取头、中、尾的中位数……

子集划分

i指向8,j指向7.

8>6,i不动,7>6,j--;j指向2,2<6,j不动 交换8和2;

i++,i指向1,1<6,i++;i指向4,4<6,i++;i指向9,9>6,i不动;

j--,j指向5,5<6,j不动,交换5和9;

i++,i指向0,0<6,i++;i指向3,3<6,i++;i指向9,9>6,i不动;

j--,j指向3,3<6,j不动.

i>j,结束,6放到i位置,即6和9交换

如果有元素正好等于pivot怎么办:
  停下来交换:会产生很多不必要的交换。(一串相等的序列)
  不理它,继续移动指针:pivot位置靠近一段,最糟糕会在最右边端,和pivot取A[0]一样,O( N^2 )

  综合考虑,选第一种,停下来交换。

小规模数据的处理
  快速排序的问题
    用递归……
    对小规模的数据(例如N不到100)可能还不如插入排序快
  解决方案
    当递归的数据规模充分小,则停止递归,直接调用简单排序(例如插入排序)
    在程序中定义一个Cutoff的阈值

 1 #include <stdio.h>
 2 typedef int ElementType;
 3
 4 void Swap( ElementType *a, ElementType *b )
 5 {
 6     ElementType t = *a;
 7     *a = *b;
 8     *b = t;
 9 }
10
11 void InsertionSort(ElementType A[], int N)
12 {
13     int i;
14     for (int P = 1; P < N; P++ ) {
15         ElementType temp = A[P];     //取出未排序序列中第一个元素
16         for (i = P; i > 0 && A[i-1] > temp; i-- )
17             A[i] = A[i-1];     //依次与已排序序列中元素比较并右移
18         A[i] = temp;
19     }
20 }
21
22 /* 快速排序 */
23 ElementType Median3( ElementType A[], int Left, int Right )
24 {
25     int Center = (Left+Right) / 2;
26     if ( A[Left] > A[Center] )
27         Swap( &A[Left], &A[Center] );
28     if ( A[Left] > A[Right] )
29         Swap( &A[Left], &A[Right] );
30     if ( A[Center] > A[Right] )
31         Swap( &A[Center], &A[Right] );
32     /* 此时A[Left] <= A[Center] <= A[Right] */
33     Swap( &A[Center], &A[Right-1] ); /* 将基准Pivot藏到右边*/
34     /* 只需要考虑A[Left+1] … A[Right-2] */
35     return  A[Right-1];  /* 返回基准Pivot */
36 }
37
38 void Qsort( ElementType A[], int Left, int Right )
39 { /* 核心递归函数 */
40      int Pivot, Cutoff = 50, Low, High;//阈值的定义
41
42      if ( Cutoff <= Right-Left ) { /* 如果序列元素充分多,进入快排 */
43           Pivot = Median3( A, Left, Right ); /* 选基准 */
44           Low = Left; High = Right-1;
45           while (1) { /*将序列中比基准小的移到基准左边,大的移到右边*/
46                while ( A[++Low] < Pivot ) ;
47                while ( A[--High] > Pivot ) ;
48                if ( Low < High ) Swap( &A[Low], &A[High] );
49                else break;
50           }
51           Swap( &A[Low], &A[Right-1] );   /* 将基准换到正确的位置 */
52           Qsort( A, Left, Low-1 );    /* 递归解决左边 */
53           Qsort( A, Low+1, Right );   /* 递归解决右边 */
54      }
55      else InsertionSort( A+Left, Right-Left+1 ); /* 元素太少,用简单排序 */
56 }
57
58 void QuickSort( ElementType A[], int N )
59 { /* 统一接口 */
60      Qsort( A, 0, N-1 );
61 }
62
63 int main()
64 {
65     int a[] = {34,8,64,51,32,21};
66     QuickSort(a, 6);
67     for(int i = 0; i < 6; i++)
68         printf("%d ",a[i]);
69     return 0;
70 }
71
72 QuickSort

QuickSort

2.表排序

间接排序

 

定义一个指针数组(下标)作为“表”(table) 

如果仅要求按顺序输出,则输出:A[ table[0] ], A[ table[1] ], ……, A[ table[N-1] ]

物理排序

 

N个数字的排列由若干个独立的环组成

用temp记录初值 ,每次换位子修改table值,用if ( table[i] == i )判断一个环的结束

复杂度

  最好情况:初始即有序
  最坏情况:
  有[N / 2]向下取整 个环,每个环包含2个元素
  需要[N / 2]向下取整 次元素移动
  T = O( m N ) ,m 是每个A元素的复制时间。

3.基数排序

桶排序:假设我们有N 个学生,他们的成绩是0到100之间的整数(于是有M = 101 个不同的成绩值)。如何在线性时间内将学生按成绩排序?

   T(N, M) = O( M+N )

  当M>>N时,桶排序不合算

次位优先LSB

假设我们有N = 10 个整数,每个整数的值在0到999之间(于是有M = 1000 个不同的值)。

输入序列: 64, 8, 216, 512, 27, 729, 0, 1, 343, 125
用“次位优先”(Least Significant Digit)  T=O(P(N+B))

多关键字的排序

一副扑克牌是按2种关键字排序的

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3
 4 typedef int ElementType;
 5
 6 /* 基数排序 - 次位优先 */
 7
 8 /* 假设元素最多有MaxDigit个关键字,基数全是同样的Radix */
 9 #define MaxDigit 4
10 #define Radix 10
11
12 /* 桶元素结点 */
13 typedef struct Node *PtrToNode;
14 struct Node {
15     int key;
16     PtrToNode next;
17 };
18
19 /* 桶头结点 */
20 struct HeadNode {
21     PtrToNode head, tail;
22 };
23 typedef struct HeadNode Bucket[Radix];
24
25 int GetDigit ( int X, int D )
26 { /* 默认次位D=1, 主位D<=MaxDigit */
27     int d, i;
28
29     for (i=1; i<=D; i++) {
30         d = X % Radix;
31         X /= Radix;
32     }
33     return d;
34 }
35
36 void LSDRadixSort( ElementType A[], int N )
37 { /* 基数排序 - 次位优先 */
38      int D, Di, i;
39      Bucket B;
40      PtrToNode tmp, p, List = NULL;
41
42      for (i=0; i<Radix; i++) /* 初始化每个桶为空链表 */
43          B[i].head = B[i].tail = NULL;
44      for (i=0; i<N; i++) { /* 将原始序列逆序存入初始链表List */
45          tmp = (PtrToNode)malloc(sizeof(struct Node));
46          tmp->key = A[i];
47          tmp->next = List;
48          List = tmp;
49      }
50      /* 下面开始排序 */
51      for (D=1; D<=MaxDigit; D++) { /* 对数据的每一位循环处理 */
52          /* 下面是分配的过程 */
53          p = List;
54          while (p) {
55              Di = GetDigit(p->key, D); /* 获得当前元素的当前位数字 */
56              /* 从List中摘除 */
57              tmp = p; p = p->next;
58              /* 插入B[Di]号桶尾 */
59              tmp->next = NULL;
60              if (B[Di].head == NULL)
61                  B[Di].head = B[Di].tail = tmp;
62              else {
63                  B[Di].tail->next = tmp;
64                  B[Di].tail = tmp;
65              }
66          }
67          /* 下面是收集的过程 */
68          List = NULL;
69          for (Di=Radix-1; Di>=0; Di--) { /* 将每个桶的元素顺序收集入List */
70              if (B[Di].head) { /* 如果桶不为空 */
71                  /* 整桶插入List表头 */
72                  B[Di].tail->next = List;
73                  List = B[Di].head;
74                  B[Di].head = B[Di].tail = NULL; /* 清空桶 */
75              }
76          }
77      }
78      /* 将List倒入A[]并释放空间 */
79      for (i=0; i<N; i++) {
80         tmp = List;
81         List = List->next;
82         A[i] = tmp->key;
83         free(tmp);
84      }
85 }
86
87 int main()
88 {
89     int a[] = {34,8,64,51,32,21};
90     LSDRadixSort(a, 6);
91     for(int i = 0; i < 6; i++)
92         printf("%d ",a[i]);
93     return 0;
94 }

LSD

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3
 4 typedef int ElementType;
 5
 6 /* 基数排序 - 主位优先 */
 7
 8 /* 假设元素最多有MaxDigit个关键字,基数全是同样的Radix */
 9
10 #define MaxDigit 4
11 #define Radix 10
12
13 /* 桶元素结点 */
14 typedef struct Node *PtrToNode;
15 struct Node{
16     int key;
17     PtrToNode next;
18 };
19
20 /* 桶头结点 */
21 struct HeadNode {
22     PtrToNode head, tail;
23 };
24 typedef struct HeadNode Bucket[Radix];
25
26 int GetDigit ( int X, int D )
27 { /* 默认次位D=1, 主位D<=MaxDigit */
28     int d, i;
29
30     for (i=1; i<=D; i++) {
31         d = X%Radix;
32         X /= Radix;
33     }
34     return d;
35 }
36
37 void MSD( ElementType A[], int L, int R, int D )
38 { /* 核心递归函数: 对A[L]...A[R]的第D位数进行排序 */
39      int Di, i, j;
40      Bucket B;
41      PtrToNode tmp, p, List = NULL;
42      if (D==0) return; /* 递归终止条件 */
43
44      for (i=0; i<Radix; i++) /* 初始化每个桶为空链表 */
45          B[i].head = B[i].tail = NULL;
46      for (i=L; i<=R; i++) { /* 将原始序列逆序存入初始链表List */
47          tmp = (PtrToNode)malloc(sizeof(struct Node));
48          tmp->key = A[i];
49          tmp->next = List;
50          List = tmp;
51      }
52      /* 下面是分配的过程 */
53      p = List;
54      while (p) {
55          Di = GetDigit(p->key, D); /* 获得当前元素的当前位数字 */
56          /* 从List中摘除 */
57          tmp = p; p = p->next;
58          /* 插入B[Di]号桶 */
59          if (B[Di].head == NULL) B[Di].tail = tmp;
60          tmp->next = B[Di].head;
61          B[Di].head = tmp;
62      }
63      /* 下面是收集的过程 */
64      i = j = L; /* i, j记录当前要处理的A[]的左右端下标 */
65      for (Di=0; Di<Radix; Di++) { /* 对于每个桶 */
66          if (B[Di].head) { /* 将非空的桶整桶倒入A[], 递归排序 */
67              p = B[Di].head;
68              while (p) {
69                  tmp = p;
70                  p = p->next;
71                  A[j++] = tmp->key;
72                  free(tmp);
73              }
74              /* 递归对该桶数据排序, 位数减1 */
75              MSD(A, i, j-1, D-1);
76              i = j; /* 为下一个桶对应的A[]左端 */
77          }
78      }
79 }
80
81 void MSDRadixSort( ElementType A[], int N )
82 { /* 统一接口 */
83     MSD(A, 0, N-1, MaxDigit);
84 }
85
86 int main()
87 {
88     int a[] = {34,8,64,51,32,21};
89     MSDRadixSort(a, 6);
90     for(int i = 0; i < 6; i++)
91         printf("%d ",a[i]);
92     return 0;
93 }

MSD

时间: 2024-07-31 13:32:26

数据结构学习笔记06排序 (快速排序、表排序)的相关文章

数据结构学习笔记06排序 (冒泡、插入、希尔)

前提void X_Sort ( ElementType A[], int N ) 大多数情况下,为简单起见,讨论从小大的整数排序 N是正整数 只讨论基于比较的排序(> = < 有定义) 只讨论内部排序 稳定性:任意两个相等的数据,排序前后的相对位置不发生改变 1.冒泡排序 (从小到大排序) 物理意义:大泡泡往下沉,小泡泡往上冒 每次比较相邻两个泡泡,符合条件,交换位置,每一轮比较完,最大的泡泡沉到最底下. 最好情况:顺序T = O( N )最坏情况:逆序T = O( N^2 ) 稳定 1 #i

【Java数据结构学习笔记之一】线性表的存储结构及其代码实现

应用程序后在那个的数据大致有四种基本的逻辑结构: 集合:数据元素之间只有"同属于一个集合"的关系 线性结构:数据元素之间存在一个对一个的关系 树形结构:数据元素之间存在一个对多个关系 图形结构或网状结构:数据元素之间存在多个对多个的关系 对于数据不同的逻辑结构,计算机在物理磁盘上通常有两种屋里存储结构 顺序存储结构 链式存储结构 本篇博文主要讲的是线性结构,而线性结构主要是线性表,非线性结构主要是树和图. 线性表的基本特征: 总存在唯一的第一个数据元素 总存在唯一的最后一个数据元素 除

数据结构学习笔记——线性表的应用

数据结构学习笔记——线性表的应用 线性表的应用 线性表的自然连接 计算任意两个表的简单自然连接过程讨论线性表的应用.假设有两个表A和B,分别是m1行.n1列和m2行.n2列,它们简单自然连接结果C=A*B(i==j),其中i表示表A中列号,j表示表B中的列号,C为A和B的笛卡儿积中满足指定连接条件的所有记录组,该连接条件为表A的第i列与表B的第j列相等. 如:         1 2 3                3 5 A  =  2 3 3         B =  1 6       

小猪的数据结构学习笔记(二)

小猪的数据结构学习笔记(二) 线性表中的顺序表 本节引言: 在上个章节中,我们对数据结构与算法的相关概念进行了了解,知道数据结构的 逻辑结构与物理结构的区别,算法的特性以及设计要求;还学了如何去衡量一个算法 的好坏,以及时间复杂度的计算!在本节中我们将接触第一个数据结构--线性表; 而线性表有两种表现形式,分别是顺序表和链表;学好这一章很重要,是学习后面的基石; 这一节我们会重点学习下顺序表,在这里给大家一个忠告,学编程切忌眼高手低,看懂不代表自己 写得出来,给出的实现代码,自己要理解思路,自己

小猪的数据结构学习笔记(四)

小猪的数据结构学习笔记(四) 线性表之静态链表 --转载请注明出处:coder-pig 本章引言: 在二,三中中我们分别学习了顺序表中的线性表与单链表,线性表有点类似于 我们前面所学的数组,而单链表使用的最多的是指针,这里问个简单的问题, 如果是在以前没有指针的话,前辈先人们怎么实现单链表呢?大家思考下! 没有指针,那么用什么来代替呢?前辈先人们非常机智,想出了使用下标+游标的方式 来实现单链表的效果!也就是今天要讲的--静态链表! 当然你也可以直接跳过本章,因为有了单链表就没有必要用静态链表了

小猪的数据结构学习笔记(五)

小猪的数据结构学习笔记(五) 线性表之--循环链表                           --转载请注明出处:coder-pig 循环链表知识点归纳: 相关代码实现: ①判断是否为空表: ②单循环链表的存储结构 其实和单链表的结构是一样的! /*定义循环链表的存储结构*/ typedef struct Cir_List { int data; struct Cir_List *next; }Lnode; ③初始化循环单链表 代码如下: //1.循环链表的初始化 //表示一个元素,如

数据结构学习笔记之栈

栈(stack)  是限定仅在表尾进行插入或删除操作的线性表.因此,对栈来说,表尾端有其特殊含义,称为栈项(top),相应地,表头端称为栈底(bottom).不含元素的空表称为空栈. 栈有两种存储表示方法:顺序栈和链栈.顺序栈,即栈的顺序存储结构是利用一组地址连续的存储单元依次存放自栈底到栈顶的数据元素,同时附设指针top指示栈顶元素在顺序栈中的位置.通常的习惯做法是以top=0表示空栈,鉴于C语言中数组的下标约定从0开始,则当以C作描述语言时,如此设定会带来很大不便:另一方面,由于栈在使用过程

数据结构学习笔记(1)-数据结构与算法

基本概念和术语 1.数据  数据元素  数据对象   数据结构 数据:在计算机科学中是指所有能输入到计算机中并被计算机程序处理的符号的总称. 数据元素:是数据的基本单位,在计算机程序中通常作为一个整体进行考虑和处理. 数据对象:是性质相同的数据元素的集合.是数据的一个子集. 数据结构:是相互之间存在一种或多种特定关系的数据元素的集合. 2.数据结构 数据结构分为逻辑结构和物理结构 2.1逻辑结构 逻辑结构表示数据之间的相互关系.通常有四种基本结构: 集合:结构中的数据元素除了同属于一种类型外,别

小猪的数据结构学习笔记(三)

小猪的数据结构学习笔记(三) 线性表之单链表 本章引言: 上一节中我们见识了第一个数据结构--线性表中的顺序表; 当你把操作的代码自己写几遍就会有点感觉了,如果现在让你写顺序表的 插入算法,你能够想出大概的代码么?如果可以,那么你就可以进入新的章节了; 否则,还是回头看看吧!在本节,我们将迎来线性表的链式表示--单链表 单链表和顺序表有什么优势和劣势呢?单链表的头插法和尾插法有什么不同呢? 请大家跟随笔者的脚步来解析线性表中的单链表把! 本节学习路线图 路线图解析: ①先要理解顺序表和单链表各自