1.56k likes | 1.7k Views
第七章 内排序. 大纲. 7.1 基本概念 7.2 三种O (n 2 ) 的简单排序 插入排序 直接插入排序 二分法插入排序 冒泡排序 选择排序 7.3 Shell 排序. 大纲(续). 7.4 基于分治法的排序 快速排序 归并排序 7.5 堆排序 7.6 分配排序和基数排序 7.7 各种排序算法的理论和实验时间代价 7.8 排序问题的下限. 7.1 基本概念. 记录 (Record) :结点,进行排序的基本单位 关键码 (Key) :唯一确定记录的一个或多个域 排序码 (Sort Key) : 作为排序运算依据的一个 或多个域
E N D
大纲 • 7.1 基本概念 • 7.2 三种O(n2)的简单排序 • 插入排序 • 直接插入排序 • 二分法插入排序 • 冒泡排序 • 选择排序 • 7.3 Shell排序
大纲(续) • 7.4 基于分治法的排序 • 快速排序 • 归并排序 • 7.5 堆排序 • 7.6 分配排序和基数排序 • 7.7 各种排序算法的理论和实验时间代价 • 7.8 排序问题的下限
7.1基本概念 • 记录(Record):结点,进行排序的基本单位 • 关键码(Key):唯一确定记录的一个或多个域 • 排序码(Sort Key):作为排序运算依据的一个 或多个域 • 序列(Sequence):线性表,由记录组成的集合
7.1基本概念(续) • 排序(Sorting)—将序列中的记录按照排序码特定的顺序排列起来,即排序码域的值具有不减(或不增)的顺序。
排序问题 • 给定一个序列R ={r1, r2, …,rn},其排序码分别为k ={k1, k2, …,kn} • 排序的目的就是将R中的记录按照特定的顺序重新排列,形成一个新的有序序列R’= {r’1, r’2, …,r’n} • 相应排序码为k’ ={k’1, k’2, …,k’n} • 其中k’1≤k’2≤…≤k’n或k’1≥k’2≥…≥k’n,前者称为不减序,后者称为不增序。
7.1基本概念(续) • 内排序(Internal Sorting):整个排序过程中所有的记录都可以直接存放在内存中 • 外排序(External Sorting):内存无法容纳所有记录,排序过程中还需要访问外存
7.1基本概念(续) • “正序”序列 :待排序序列正好符合排序要求 • “逆序” 序列 :把待排序序列逆转过来,正好符合排序要求
7.1基本概念(续) • “稳定的”(stable)排序算法 :如果存在多个具有相同排序码的记录,经过排序后这些记录的相对次序仍然保持不变 。
排序算法的分类 • 简单排序 • 插入排序(Insert sort) • 直接选择排序(Selection sort) • 冒泡排序(Bubble sort) • Shell排序(Shell sort)
排序算法的分类(续) • 分治排序 • 快速排序(Quicksort) • 归并排序(Mergesort) • 堆排序(Heapsort) • 分配排序 (Binsort)
排序算法的衡量标准 • 时间代价:记录的比较和交换次数 • 空间代价 • 算法的复杂度
总的排序类 template <class Record,class Compare> class Sorter{ //总排序类 protected: //交换数组中的两个元素 static void swap(Record Array[],int i,int j); public: //对数组Array进行排序 void Sort(Record Array[],int n); //输出数组内容 void PrintArray(Record array[], int n); };
Compare类 • Compare类是用来比较记录的排序码,把它单独定义成模板参数,是为了方便针对不同类型的排序码进行比较。为了简便起见, 我们只讨论整数排序的例子。
int_intCompare 类 class int_intCompare{ //比较两个整型记录大小 public: static bool lt(int x,int y) {return x<y;} static bool eq(int x,int y) {return x==y;} static bool gt(int x,int y) {return x>y;} static bool le(int x,int y) {return x<=y;} static bool ge(int x,int y) {return x>=y;} };
swap函数 template <class Record,class Compare> void Sorter<Record,Compare>:: swap(Record Array[],int i,int j) { //交换数组中的两个元素 Record TempRecord = Array[i]; Array[i] = Array[j]; Array[j] = TempRecord; }
PrintArray函数 template <class Record,class Compare> void Sorter<Record,Compare>:: PrintArray(Record Array[], int n) { //输出数组内容 for(int i=0;i<n;i++) cout<<Array[i]<<" "; cout<<endl; }
7.2 三种O(n2)的简单排序 • 插入排序(Insert Sort) • 冒泡排序(Bubble Sort) • 选择排序 (Selection Sort)
7.2.1插入排序 • 算法思想: • 逐个处理待排序的记录,每个新记录都要与前面那些已排好序的记录进行比较,然后插入到适当的位置。
插入排序类 template <class Record,class Compare> class InsertSorter:public Sorter<Record,Compare>{};
直接插入排序 template <class Record,class Compare> class StraightInsertSorter:public InsertSorter<Record,Compare> { //直接插入排序类 public: void Sort(Record Array[],int n); };
template <class Record,class Compare> void StraightInsertSorter<Record,Compare>:: Sort(Record Array[], int n) { //Array[]为待排序数组,n为数组长度 for (int i=1; i<n; i++) // 依次插入第i个记录 { //依次与前面的记录进行比较,发现逆置就交换 for (int j=i;j>0;j--){ if (Compare::lt(Array[j], Array[j-1])) swap(Array, j, j-1); else break; //此时i前面记录已排序 } } }
算法分析 • 稳定 • 空间代价:Θ(1) • 时间代价: • 最佳情况:n-1次比较,0次比较,Θ(n) • 最差情况:比较和交换次数为 • 平均情况:Θ(n2)
优化的插入排序算法 template <class Record,class Compare> class ImprovedInsertSorter:public InsertSorter<Record,Compare> { //优化的插入排序类 public: void Sort(Record Array[],int n); };
template <class Record,class Compare> void ImprovedInsertSorter<Record,Compare>:: Sort(Record Array[], int n) { //Array[]为待排序数组,n为数组长度 Record TempRecord; // 临时变量 // 依次插入第i个记录 for (int i=1; i<n; i++) { TempRecord=Array[i]; //从i开始往前寻找记录i的正确位置 int j = i-1;
//将那些大于等于记录i的记录后移 while ((j>=0) && (Compare::lt(TempRecord, Array[j]))) { Array[j+1] = Array[j]; j = j - 1; } //此时j后面就是记录i的正确位置,回填 Array[j+1] = TempRecord; } }
二分法插入排序 • 算法思想: • 在插入第i个记录时,前面的记录已经是有序的了,可以用二分法查找第i个记录的正确位置 。
template <class Record,class Compare> class BinaryInsertSorter:public InsertSorter<Record,Compare> { //二分法插入排序类 public: void Sort(Record Array[],int n); };
template <class Record,class Compare> void BinaryInsertSorter<Record,Compare>:: Sort(Record Array[], int n) { //Array[]为待排序数组,n为数组长度 Record TempRecord; //临时变量 //记录已排好序序列的左、右、中位置 int left,right,middle; //依次插入第i个记录 for (int i=1;i<n;i++) { //保存当前待插入记录 TempRecord = Array[i];
//记录已排好序序列的左右位置 left = 0; right = i-1; //开始查找待插入记录的正确位置 while(left <= right) { //中间位置 middle = (left+right)/2; //如果待插入记录比中间记录小, // 就在左一半中查找, // 否则在右一半中查找 if (Compare::lt(TempRecord,Array[mi ddle])) right = middle-1;
else left = middle+1; } //将前面所有大于当前待插入记录的记录后移 for(int j = i-1; j >= left; j --) Array[j+1] = Array[j]; //将待插入记录回填到正确位置 Array[left] = TempRecord; } }
算法分析 • 稳定 • 空间代价:Θ(1) • 时间代价: • 插入每个记录需要Θ(log i)次比较 • 最多移动i+1次 ,最少2次(移动临时记录) • 因此最佳情况下总时间代价为 Θ(nlog n) ,最差和平均情况下仍为Θ(n2)
7.2.2 冒泡排序 • 算法思想: • 不停地比较相邻的记录,如果不满足排序要求,就交换相邻记录,直到所有的记录都已经排好序。
冒泡排序类 template <class Record,class Compare> class BubbleSorter:public Sorter<Record,Compare> { //冒泡排序类 public: void Sort(Record Array[],int n); };
冒泡排序算法 template <class Record,class Compare> void BubbleSorter<Record,Compare>:: Sort(Record Array[], int n) { //冒泡排序,Array[]为待排数组,n为数组长度 //第i个记录冒泡 for (int i=1; i<n; i++) //依次比较相邻记录,如果发现逆置,就交换 for (int j=n-1; j>=i; j--) if (Compare::lt(Array[j], Array[j-1])) swap(Array, j, j-1); }
算法分析 • 稳定 • 空间代价:Θ(1) • 时间代价 : • 比较次数 : • 交换次数最多为Θ(n2),最少为0,平均为Θ(n2)。 • 最大,最小,平均时间代价均为Θ(n2)。
优化的冒泡排序 • 改进:检查每次冒泡过程中是否发生过交换,如果没有,则表明整个数组已经排好序了,排序结束。 • 避免不必要的比较
优化的冒泡排序 template <class Record,class Compare> class ImprovedBubbleSorter:public Sorter<Record,Compare> { //优化的冒泡排序类 public: void Sort(Record Array[],int n); };
template <class Record,class Compare> void ImprovedBubbleSorter<Record,Compare>:: Sort(Record Array[], int n) { //Array[]为待排序数组,n为数组长度 bool NoSwap; // 是否发生交换的标志 for (int i=1; i<n; i++) { NoSwap = true; // 标志初始为真 for (int j=n-1; j>=i; j--)
if (Compare::lt(Array[j], Array[j-1])) { //如果发生了交换, //标志变为假 swap(Array, j, j-1); NoSwap = false; } // 如果没发生过交换, // 表示已排好序,结束算法 if (NoSwap) return; } }
算法分析 • 稳定 • 空间代价为Θ(1) • 时间代价: • 最小时间代价为Θ(n):最佳情况下只运行第一轮循环 • 其他情况下时间代价仍为Θ(n2)
7.2.3 直接选择排序 • 算法思想: • 找出剩下的未排序记录中的最小记录,然后直接与数组中第i个记录交换 ,比冒泡排序减少了移动次数
直接选择排序 template <class Record,class Compare> class StraightSelectSorter:public Sorter<Record,Compare> { //直接选择排序类 public: void Sort(Record Array[],int n); };
template <class Record,class Compare> void StraightSelectSorter<Record,Compare>:: Sort(Record Array[], int n) { //Array[]为待排序数组,n为数组长度 // 依次选出第i小的记录, // 即剩余记录中最小的那个 for (int i=0; i<n-1; i++) { // 首先假设记录i就是最小的 int Smallest = i; // 开始向后扫描所有剩余记录 for (int j=i+1;j<n; j++)
// 如果发现更小的记录,记录它的位置 if (Compare::lt(Array[j], Array[Smallest])) Smallest = j; //将第i小的记录放在数组中第i个位置 swap(Array, i, Smallest); } }
算法分析 • 不稳定 • 空间代价:Θ(1) • 时间代价 : • 比较次数:Θ(n2),与冒泡排序一样 • 交换次数:n-1 • 总时间代价:Θ(n2)