》》 插入排序是一种简单直观的排序方法,其基本思想在于每次将一个待排序的记录,按其关键字
大小插入到前面已经排好序的子序列中,直到全部记录插入完成。
1、直接插入排序
》》 直接插入排序进行的两项工作:
1)、从前面有序的子表中查找出待插入元素应该被插入的位置
2)、给插入位置腾出空间,将待插入元素复制到表中的插入位置
注意:这种排序方法是边比较边移动元素。
补充:这种方法在排序的过程中,向有序表中逐个地插入元素的操作进行了 n -1 趟 ,
每趟操作都分为“比较关键字”和“移动元素”
》》 直接插入排序图解
说明1:直接插入排序法是从“无序段”的 第 i 位置开始取数,将取到的数放入 [ 0 ...i-1] 段中合适的位置,使得
[ 0 ... i ] 段中的数据依次是有序的。
》》 直接插入排序的代码示例:
// 注释:下面的案例中使用 A[0] 作为“哨兵”,其实是为了复制被取出的元素,这样
// 将前面的元素后移的时候,不会因为元素被覆盖导致数据的丢失
void InsertSort(ElemType A[] , int n){
int i , j ;
// 说明1:第0个位置不放任何数据,A[0] 将要作为“哨兵”
// 说明2:从第 2 个位置开始取数据,直到取到最后一位
for(i = 2 ; i <= n ; i++){
// 如果当前取到的元素比它的前驱元素值小,执行if内部的操作
if( A[i].key < A[i-1].key ){
// 将 A[0] 位置的值作为“哨兵”
A[0] = A[i];
// j 记录的是依次被比较的数据的下标
for(j = i -1 ; A[0].key < A[j].key ; --j ){
// 如果哨兵的值比当前 j 坐标下的值小,那么当前 j 位置的值
// 需要向后移动一位,空出当前 j 位置的空间,同时 j 指针向前
// 移动一位
A[j+1] = A[j];
}
// 当哨兵的值不小于第 j 位置的值,上面的 for 循环将退出,
// 此时,j 指针后一位的位置是空着的,那么就把“哨兵的值”复制到
// j + 1 位置
A[j+1] = A[0]
}
}
}
2、折半插入排序【对直接插入排序算法的改进】
》》 基本思想:将比较和移动操作分离出来,即先折半查找出元素的待插入位置,然后再统一地移动待插入
位置之后的所有元素。
》》 折半插入排序的代码示例:
void InsertSort(ElemType A[] , int n){
// i 表示被取出的元素的位置
// j 表示移动元素的开始位置
// low , high , mid 三个指针都是为了使用折半查找而设置的三个值
int i , j , low , high , mid ;
// A[0] 位置上不存放任何数据,用来作“哨兵”
// 从第 2 个位置开始取数
for( i = 2 ; i <= n ; i++ ){
// 将 A[i] 中的元素暂时存入 A[0] 中
A[0] = A[i];
// 下面的代码是为了查找合适的插入位置,使用折半查找法
low = 1 , high = i -1 ;
while( low < high ){
// 取中间点
mid = (low+high)/2;
// 将 A[0] 中的值与 中间点的值,进行比较
// 如果中间点的值大于即将插入的值,那么插入的位置一定在左半子表,
// 将范围缩小到左半子表
if( A[mid].key > A[0].key ){
high = mid -1 ;
}else{
// 如果中间点的值不大于即将插入的值,那么插入的位置一定在右半子表,
// 将范围缩小到右半子表
low = mid + 1 ;
}
}
// 上面的 while 循环退出的时候,low == high
// 即,被插入的位置就是 high 指针所指向的位置
// 将 high 位置之后的所有元素后移一位【包括high 位置的元素也后移一位】,
// 空出 high 位置的空间
for( j = i -1 ; j >= high ; --j){
A[j+1] = A[j];
}
// 将 A[0] 中的元素插入到 high 的位置
A[high] = A[0];
}
}
3、希尔排序【分段多次使用直接插入排序】
》》希尔排序的基本思想:先将待排序表分割成若干个形如 L[ i, i+ d , i + 2d , ...., i + kd ] 的“特殊”子表,分别
进行直接插入排序,当整个表中元素已经呈现“基本有序”时,再对全体记录进行一次直接排序。