排序(Sort)是计算机内经常进行的一种操作,其目的是将一组“无序”的记录调整为“有序”的记录序列,使之按关键字递增(或递减)依次排列起来。
在排序过程中,若将整个文件都放在内存中处理,排序时不涉及数据的内 外存交换,则称之为内部排序;反之,若排序过程中要进行数据的内外存交换,则称为外部排序。
按策略可将内部排序分为五类:插入排序 选择排序 交换排序 归并排序 分配排序
插入排序(Insertion Sort)的基本思想是:每次将一个待排序的记录,按其关键字大小插入到前面已经排好序的子文件中的适当位置,知道全部记录插入完成为止。插入排序的方法有两种:
直接插入排序和希尔排序
直接插入排序:
排序过程的某一中间时刻,R被划分成两个子区间R[0...i-1](已排好序的有序区)和[i...n-1](当前未排序的部分,无序区).
直接插入排序的基本操作使将当前无序区的第一个记录插入到有序区R[0....i-1]中的适当位置上,使R[0...n]变为有序区。因为这种方法每次使有序区增加1个记录,通常称为增量法。
选择排序(Selection Sort)的基本思想是:每一趟从待排序的记录中选出关键字最小的记录,顺序放在已排好序的记录序列的最后,直到全部记录排序完毕。
常用的选择排序方法有直接选择排序和堆排序。
直接选择排序:
基本操作: 从待排序的记录序列中选择关键码最小或最大的记录并将它与序列中的第1个记录交换位置;然后从不包括第1个位置上的记录序列中选择关键码最小或最大的记录并将它与序列中第2个记录交换位置;如此重复,直到序列中只剩下一个记录为止。具有n个记录的序列要做n-1次排序。
堆排序:
堆排序是在直接排序的基础上借助完全二叉树结构而形成的一种排序方法。从数据结构的观点看,堆排序是完全二叉树的顺序存储结构的应用。
“若对二叉树的根结点从0开始编号,则相应的i号结点的双亲结点的编号为(i-1)2,左孩子的编号为2i+1,右孩子的编号为2i+2.”
“所谓二叉树的顺序存储,就是用一组连续的存储单元存放二叉树中的结点,一般是按照二叉树结点从上到下,从左到右的顺序存储。适合完全二叉树和满二叉树。”
堆分为最大堆和最小堆。最大堆(满足ai>=a2i+1 && ai>=a2i+2的序列)的根结点是堆中关键码最大的结点,最小堆(满足ai<=a2i+1 && ai<=a2i+2的序列)的根结点是堆中关键码最小的结点。
将待排序的记录序列建成一个堆,并借助于堆的性质进行排序的方法称为堆排序。
堆排序的基本思想:设有n个记录,首先将这n个记录按关键码建成堆,将堆顶记录输出,得到n个记录中关键码最大(或最小)的记录;调整剩余的n-1个记录,使之称为一个新堆,再输出堆顶记录;如此反复,当堆中只剩一个元素时,排序结束。
交换排序
交换排序的基本思想是:两两交换待排序记录的关键字,发现两个记录的次序相反时即进行交换,知道没有反序的记录为止。
交换排序方法有:冒泡排序和快速排序
冒泡排序:首先将第1个记录的关键字和第2个记录的关键字进行比较,若前者大于后者,则交换两个记录,然后比较第2个记录与第3个记录的关键字,依次类推,知道第n-1个记录与第n个记录的关键字比较为止。上述过程称为第1趟起泡排序,其结果使得关键字最大的记录被安排在最后一个记录的位置上。然后进行第2趟起泡排序,对前n-1个记录进行同样的排序,使得关键字次大的记录被安排在第n-1个记录的位置上。一般的,第i趟起泡排序从第1个记录到第i个记录依次比较相邻两条记录的关键字,并在逆序是交换相邻记录,其结果使得这i个记录中最大的记录被交换到第i个记录的位置上。
/// <summary> /// 冒泡排序算法 /// </summary> /// <param name="R"></param> public static void BubbleSort(SeqList<int> R) { int i, j; bool exchange; int temp; int n = R.Maxsize; for (i = 1; i < n; i++) { exchange = false; for (j = 0; j < n - i; j++)//对当前无序区域R[0...n-i] { if (R.Data[j] > R.Data[j + 1]) { temp = R.Data[j + 1]; R.Data[j + 1] = R.Data[j]; R.Data[j] = temp; exchange = true; } } if (!exchange) return; } }
归并排序
类似于快速排序算法,其使用的是分治法来排序。归并排序的基本思想是:将两个或两个以上的有序子序列“归并”为一个有序序列。
二路归并排序的基本思想:将n个记录的原始序列看做n个有序子序列,每个子序列的长度为1,然后从第1个子序列开始,把相邻的子序列两两和关,得到n/2个长度为2或1的子序列(当子序列的个数为奇数时,最后一组得到的序列长度为1),我们把这一过程称为一次归并排序,对一次归并排序的n/2个子序列采用上述方法继续顺序成对归并,如此重复,当最后得到长度为n的子序列时,该子序列表示原始序列归并排序后的有序序列。
/// <summary> /// 两两归并 /// </summary> /// <param name="sqList"></param> /// <param name="len"></param> public static void Merge(SeqList<int> sqList, int len) { int m = 0; //临时顺序表中的起始位置 int l1 = 0; //第1个有序表的起始位置 int h1; //第1个有序表的结束位置 int l2; //第2个有序表的起始位置 int h2; //第2个有序表的结束位置 int i = 0; int j = 0; //临时表,用于临时将俩个有序表合并为一个有序表 SeqList<int> temp = new SeqList<int>(sqList.GetLength()); //归并处理 while (l1 + len < sqList.GetLength()) { l2 = l1 + len; //第2个有序表的起始位置 h1 = l2 - 1; //第1个有序表的结束位置 //第2个有序表的结束位置 h2 = (l2 + len - 1 < sqList.GetLength()) ? l2 + len - 1 : sqList.Maxsize - 1; j = l2; i = l1; //两个记录表排序 while ((i <= h1) && (j <= h2)) { if (sqList.Data[i] <= sqList.Data[j]) temp.Data[m++] = sqList.Data[i++]; else temp.Data[m++] = sqList.Data[j++]; } //第1个有序表中个还有记录没有排序完 while (i <= h1) { temp.Data[m++] = sqList.Data[i++]; } //第2个有序表中个还有记录没有排序完 while (j <= h2) { temp.Data[m++] = sqList.Data[j++]; } l1 = h2 + 1; } i = l1; //原顺序表还有记录没有排序完 while (i < sqList.GetLength()) { temp.Data[m++] = sqList.Data[i++]; } //临时顺序表中的记录复制到元顺序表,是原顺序表中的记录有序。 for (i = 0; i < sqList.GetLength(); i++) { sqList.Data[i] = temp.Data[i]; } } /// <summary> /// 归并排序 /// </summary> /// <param name="sqList"></param> public static void MergeSort(SeqList<int> sqList) { int k = 1; //归并增量 while (k < sqList.GetLength()) { Merge(sqList, k); k *= 2; } }
注:本文整理自《数据结构(C#语言版)》!!!