数据结构:排序(1)

 

排序是数据处理中经常使用的一种重要运算,在计算机及其应用系统中,花费在排序上的时间在系统运行时间中占有很大比重;并且排序本身对推动算法分析的发展也起很大作用。

一、排序的基本概念

1.排序或分类

所谓排序,就是要整理文件中的记录,使之按关键字递增(或递减)次序排列起来。其确切定义如下:

1)被排序对象--文件

  • 被排序的对象--文件由一组记录组成。
  • 记录则由若干个数据项(或域)组成。其中有一项可用来标识一个记录,称为关键字项。该数据项的值称为关键字(Key)。

    注意:
      在不易产生混淆时,将关键字项简称为关键字。
    

2)排序运算的依据--关键字

  • 用来作排序运算依据的关键字,可以是数字类型,也可以是字符类型
  • 关键字的选取应根据问题的要求而定。

2.排序的稳定性

在待排序的文件中,若存在多个关键字相同的记录,经过排序后这些具有相同关键字的记录之间的相对次序保持不变,该排序方法是稳定的;若具有相同关键字的记录之间的相对次序发生变化,则称这种排序方法是不稳定的。

3.排序方法的分类

1)按是否涉及数据的内、外存交换分

在排序过程中,若整个文件都是放在内存中处理,排序时不涉及数据的内、外存交换,则称之为内部排序(简称内排序);反之,若排序过程中要进行数据的内、外存交换,则称之为外部排序。

注意: ① 内排序适用于记录个数不很多的小文件
      ② 外排序则适用于记录个数太多,不能一次将其全部记录放人内存的大文件。`

2)按策略划分内部排序方法  

可以分为五类:插入排序、选择排序、交换排序、归并排序和分配排序。

3.排序算法分析

1)排序算法的基本操作

  大多数排序算法都有两个基本的操作:

  • 比较两个关键字的大小;
  • 改变指向记录的指针或移动记录本身。

2)待排文件的常用存储方式

  • 以顺序表(或直接用向量)作为存储结构

    排序过程:对记录本身进行物理重排(即通过关键字之间的比较判定,将记录移到合适的位置)

  • 以链表作为存储结构   排序过程:无须移动记录,仅需修改指针。通常将这类排序称为链表(或链式)排序;

  • 用顺序的方式存储待排序的记录,但同时建立一个辅助表(如包括关键字和指向记录位置的指针组成的索引表)

    排序过程:只需对辅助表的表目进行物理重排(即只移动辅助表的表目,而不移动记录本身)。适用于难于在链表上实现,仍需避免排序过程中移动记录的排序方法。

3)排序算法性能比较

评价排序算法好坏的标准主要有两条:

① 执行时间和所需的辅助空间 

② 算法本身的复杂程度

二、插入排序

1.直接插入排序

  • 基本思想:把n个待排序的元素看成为一个有序表和一个无序表,开始时有序表中只包含一个元素,无序表中包含有n-1个元素,排序过程中每次从无序表中取出第一个元素,将它插入到有序表中的适当位置,使之成为新的有序表,重复n-1次可完成排序过程。
  • 算法描述:动画演示过程

2.希尔排序

  • 基本思想:先将整个待排元素序列分割成若干个子序列(由相隔某个“增量”的元素组成的)分别进行直接插入排序,然后依次缩减增量再进行排序,待整个序列中的元素基本有序(增量足够小)时,再对全体元素进行一次直接插入排序。

  • 算法描述:动画演示过程

三、交换排序

1.冒泡排序

  • 基本思想:设待排序n个元素存放在数组a[n]中,无序区范围初始为(a(0),a(1),a(2),...,a[n-1]),冒泡排序方法是在当前无序区内,从最上面的元素a[0]开始,对每两个相邻的元素a[i+1]和ai进行比较,且使值较小的元素换至值较大的元素之上(若a[i]>a[i+1],则a[i]和a[i+1]的值互换),这样经过一趟冒泡排序后,假设最后下移的元素为a[k],则无序区中值较大的几个元素到达下端并从小到大依次存放在a[k+1],a[k+2],...a[n-1]中,这样无序区范围变为(a[0],a[1],a[2],...,a[k])。在当前无序区内进行下一趟冒泡排序。这个过程一直到某一趟排序中不出现元素交换的动作,排序结束。整个排序过程最多执行n-1遍。这种排序方法是通过相邻元素之间的比较与交换,使值较小的元素逐渐从后部移向前部(从下标较大的单元移向下标较小的单元),就象水底下的气泡一样逐渐向上冒。故称为冒泡排序法。
  • 算法描述:动画演示过程

2.快速排序

  • 基本思想: ①分解:在R[low..high]中任选一个记录作为基准(Pivot),以此基准将当前无序区划分为左、右两个较小的子区间R[low..pivotpos-1)和R[pivotpos+1..high],并使左边子区间中所有记录的关键字均小于等于基准记录(不妨记为pivot)的关键字pivot.key,右边的子区间中所有记录的关键字均大于等于pivot.key,而基准记录pivot则位于正确的位置(pivotpos)上,它无须参加后续的排序。 ②求解:通过递归调用快速排序对左、右子区间R[low..pivotpos-1]和R[pivotpos+1..high]快速排序。③组合:因为当"求解"步骤中的两个递归调用结束时,其左、右两个子区间已有序。对快速排序而言,"组合"步骤无须做什么,可看作是空操作。
  • 算法描述:动画演示过程

三、选择排序

1.直接选择排序

  • 基本思想:①初始状态:无序区为R[1..n],有序区为空。②第1趟排序:在无序区R[1..n]中选出关键字最小的记录R[k],将它与无序区的第1个记录R[1]交换,使R[1..1]和R[2..n]分别变为记录个数增加1个的新有序区和记录个数减少1个的新无序区。 ……③第i趟排序:第i趟排序开始时,当前有序区和无序区分别为R[1..i-1]和Ri..n。该趟排序从当前无序区中选出关键字最小的记录R[k],将它与无序区的第1个记录R[i]交换,使R[1..i]和R[i+1..n]分别变为记录个数增加1个的新有序区和记录个数减少1个的新无序区。
  • 算法描述:动画演示过程

2.堆排序

  • 基本思想堆排序(HeapSort)是一树形选择排序。堆化数组后,第一次将A[0]与A[n - 1]交换,再对A[0…n-2]重新恢复堆。第二次将A[0]与A[n – 2]交换,再对A[0…n - 3]重新恢复堆,重复这样的操作直到A[0]与A[1]交换。由于每次都是将最小的数据并入到后面的有序区间,故操作完成后整个数组就有序了。

  • 算法描述:动画演示过程

  1 ///Name:Tree
  2 ///Author:JA
  3 ///Date:2015-3-11
  4 
  5 
  6 
  7 ///对顺序表L作直接插入排序
  8 void InsertSort(SqList *L){
  9     for (i = 2; i <= L.length;++i)
 10     if (LT(L.r[i].key, L.[r - 1].key)){        //将L.r[i]插入有序集表
 11         L.r[0] = L.r[i];
 12         L.r[i] = L.r[i - 1];
 13         for (j = i - 2; LT(L.r[0].key, L.r[j].key); --j)
 14             L.r[j + 1] = L.r[j];             //记录后移
 15         L.r[j + 1] = L.r[j];                 //插入到正确位置
 16     }
 17 }//InsertSort
 18 
 19 //希尔排序
 20 void ShellPass(SeqList R,int d)
 21 {//希尔排序中的一趟排序,d为当前增量
 22     for (i = d + 1; i <= n; i++) //将R[d+1..n]分别插入各组当前的有序区
 23     if (R[i].key<R[i - d].key){
 24         R[0] = R[i]; j = i - d; //R[0]只是暂存单元,不是哨兵
 25         do {//查找R[i]的插入位置
 26             R[j + d]; = R[j]; //后移记录
 27             j = j - d; //查找前一记录
 28         } while (j>0 && R[0].key < R[j].key);
 29         R[j + d] = R[0]; //插入R[i]到正确的位置上
 30     } //endif
 31 } //ShellPass
 32 void  ShellSort(SeqList R)
 33 {
 34     int increment = n; //增量初值,不妨设n>0
 35     do {
 36         increment = increment / 3 + 1; //求下一增量
 37         ShellPass(R,increment);  //一趟增量为increment的Shell插入排序
 38     } while (increment>1)
 39 } //ShellSort
 40 
 41 //冒泡排序
 42 void BubbleSort(SeqList R)
 43 { //R(l..n)是待排序的文件,采用自下向上扫描,对R做冒泡排序
 44     int i,j;
 45         Boolean exchange; //交换标志
 46     for (i = 1; i<n; i++){ //最多做n-1趟排序
 47         exchange = FALSE; //本趟排序开始前,交换标志应为假
 48         for (j = n - 1; j >= i; j--) //对当前无序区R[i..n]自下向上扫描
 49         if (R[j + 1].key<R[j].key){//交换记录
 50             R[0] = R[j + 1]; //R[0]不是哨兵,仅做暂存单元
 51                 R[j + 1] = R[j];
 52                 R[j] = R[0];
 53                 exchange = TRUE; //发生了交换,故将交换标志置为真
 54         }
 55         if (!exchange) //本趟排序未发生交换,提前终止算法
 56             return;
 57     } //endfor(外循环)
 58 } //BubbleSort
 59 
 60 //快速排序
 61 void QuickSort(SeqList R,,int low,int high)
 62 { //对R[low..high]快速排序
 63     int pivotpos; //划分后的基准记录的位置
 64     if (low<high){//仅当区间长度大于1时才须排序
 65         pivotpos = Partition(R,low,high); //对R[low..high]做划分
 66         QuickSort(R,low,pivotpos - 1); //对左区间递归排序
 67         QuickSort(R,pivotpos + 1,high); //对右区间递归排序
 68     }
 69 } //QuickSort
 70 int Partition(SeqList R,int i,int j)
 71 {//调用Partition(R,low,high)时,对R[low..high]做划分,
 72     //并返回基准记录的位置
 73     ReceType pivot = R[i]; //用区间的第1个记录作为基准 '
 74     while (i<j){ //从区间两端交替向中间扫描,直至i=j为止
 75         while (i < j&&R[j].key >= pivot.key) //pivot相当于在位置i上
 76             j--; //从右向左扫描,查找第1个关键字小于pivot.key的记录R[j]
 77         if (i < j) //表示找到的R[j]的关键字<pivot.key
 78             R[i++] = R[j]; //相当于交换R[i]和R[j],交换后i指针加1
 79         while (i < j&&R[i].key <= pivot.key) //pivot相当于在位置j上
 80             i++; //从左向右扫描,查找第1个关键字大于pivot.key的记录R[i]
 81         if (i<j) //表示找到了R[i],使R[i].key>pivot.key
 82             R[j--] = R[i]; //相当于交换R[i]和R[j],交换后j指针减1
 83     } //endwhile
 84     R[i] = pivot; //基准记录已被最后定位
 85     return i;
 86 } //partition
 87 
 88 //直接选择排序
 89 void SelectSort(SeqList R)
 90 {
 91     int i,j,k;
 92     for (i = 1; i<n; i++){//做第i趟排序(1≤i≤n-1)
 93         k = i;
 94         for (j = i + 1; j <= n; j++) //在当前无序区R[i..n]中选key最小的记录R[k]
 95         if (R[j].key<R[k].key)
 96             k = j; //k记下目前找到的最小关键字所在的位置
 97         if (k != i){ //交换R[i]和R[k]
 98             R[0] = R[i]; R[i] = R[k]; R[k] = R[0]; //R[0]作暂存单元
 99         } //endif
100     } //endfor
101 } //SeleetSort
102 
103 //堆排序
104 void HeapSort(SeqIAst R)
105 { //对R[1..n]进行堆排序,不妨用R[0]做暂存单元
106     int i;
107     BuildHeap(R); //将R[1-n]建成初始堆
108     for (i = n; i > 1; i--){ //对当前无序区R[1..i]进行堆排序,共做n-1趟。
109         R[0] = R[1]; R[1] = R[i]; R[i] = R[0]; //将堆顶和堆中最后一个记录交换
110             Heapify(R,1,i - 1); //将R[1..i-1]重新调整为堆,仅有R[1]可能违反堆性质
111     } //endfor
112 } //HeapSort
View Code

3/13/2015 2:35:47 PM

原文地址:https://www.cnblogs.com/joeaaron007/p/4335306.html