Internal Sorting
基本概念
排序:重新排列列表中元素,使表中元素满足按关键字有序的过程。
算法稳定性:待排序表中的两个对应关键字相同的元素Ri和Rj,使用某一排序算法之后,Ri和Rj的相对位置保持不变。
算法是否具有稳定性不是衡量算法优劣的标准,只是对算法性质的描述。
算法的分类
分类标准:排序期间数据元素是否全部存放在内存中
-
内部排序 - 排序期间元素全部存放在内存中
-
外部排序 - 排序期间元素无法在全部同时存放在内存中(必须在排序过程中根据要求不断在内、外存之间移动
一般,内部排序算法在执行过程中都要进行两种操作:比较和移动;
但不是所有的内部排序算法都要基于比较操作(基数排序不基于比较)。
通常将排序算法分为以下五大类:
- 插入排序
- 交换排序
- 选择排序
- 归并排序
- 基数排序
不同的排序算法适合不同的环境,就全面性而言,很难提出一种被认为是最好的算法。
内部算法的性能取决于算法的时间复杂度和空间复杂度,而时间复杂度一般是由比较和移动的次数决定的。
插入排序
基本思想:每次将一个待排序的记录按其关键字大小插入到前面已排好序的子序列中,直到插入完成。
插入排序中的三种重要排序算法:直接插入排序、折半插入算法、希尔排序。
直接插入算法
将元素L(i)
插入到已有序的子序列L[i-1]
中:
- 查找出
L(i)
在L[1...i-1]
中的插入位置k; - 将
L[k...i-1]
中的所有元素依次往后移动一个位置; - 将
L(i)
复制到L(k)
.
实现对L[1...n]
的排序,可将L(2)~L(n)依次插入到前面已排好序的子序列中。
void InsertSort(Elemtype A[], int n) {
int i, j;
for(i = 2; i <= n; i++)
if(A[i] < A[j]) {
//若A[i]关键码大小小于其前驱,将A[i]插入有序表
A[0] = A[i]; //复制为哨兵,A[0]不存放任何元素
for(j = i - 1; A[0] < A[j]; --j)
A[j+1] = A[j]; //向后挪位
A[j + 1] = A[0]; //复制到插入位置
}
}
注意上面“哨兵”的作用
空间效率:O(1)
时间效率:O(n^2)
稳定性:是一个稳定的排序算法(每次插入元素都是从后向前先比较再移动,所以不会出现相同元素相对位置发生变化的情况)
适用性:适用于顺序存储和链式存储的线性表
大部分排序算法都仅适用于顺序存储的线性表
折半插入排序
将比较和移动操作分离:先折半查找出元素的待插入位置,然后统一地移动代插入位置之后的所有元素。
因为折半查找仅适用于顺序表,所以折半插入排序也仅适用于顺序表。
void InsertSort(Elemtype A[], int n) {
for(int i = 0; i <= n + 1; i++) {
A[0] = A[i]; //A[0]暂存到A[0]
int low = 1, high = i - 1; //折半查找
while(low <= high) {
int mid = (low + high) / 2;
if(A[mid] > A[0]) high = mid - 1;
else low = mid + 1;
}
for(int j = i - 1; j >= high + 1; --j) //插入操作
A[j + 1] = A[j];
A[high + 1]