内部排序 (二):插入排序 (直接插入排序、折半插入排序、希尔排序)

作为数据结构的课程笔记,以便查阅。如有出错的地方,还请多多指正!

目录

  • 基本概念
  • 直接插入排序 Straight Insertion Sort
    • 排序过程
    • 算法实现
    • 算法评价
      • T ( n ) T(n) T(n)
      • S ( n ) S(n) S(n)
      • 是否稳定
  • 折半插入排序 Binary Insertion Sort
    • 排序过程
    • 算法实现
    • 算法评价
      • T(n)
      • S(n)
      • 是否稳定
  • 希尔排序 Shell Sort
    • 排序过程
    • 算法实现
    • 算法评价
      • T ( n ) T(n) T(n)
      • S ( n ) S(n) S(n)
      • 是否稳定

基本概念

  • 内部排序:待排序记录存放在内存
  • 外部排序:待排序记录数量很大,内存无法一次容纳全部记录,在排序过程中需要访问外存
  • 稳定的/不稳定的排序方法

直接插入排序 Straight Insertion Sort

排序过程

思路

  • 将序列中第 1 个记录看成是一个有序子序列,然后从第 2 个记录开始,逐个进行插入,直至整个序列有序

具体实现:

  • 待排序记录为R[i] ,将其与R[j] (j=i-1)进行比较——
    R[i]≧R[j]——R[i]位置不变
    R[i]——①将R[i]放在“监视哨”的位置;②R[j]后移,j--,直到R[0]≧R[j];③将R[0]放置于R[j+1]的位置

内部排序 (二):插入排序 (直接插入排序、折半插入排序、希尔排序)_第1张图片

算法实现

void Straight_insertion_sort(SqList_t* list)
{
	for (int i = 2; i <= list->len; ++i)
	{
		int j = i - 1;

		if (list->rec[i].key < list->rec[j].key)
		{
			list->rec[0] = list->rec[i];

			do {
				list->rec[j + 1] = list->rec[j];
				--j;
			} while (list->rec[j].key > list->rec[0].key);

			list->rec[j + 1] = list->rec[0];
		}
	}
}

算法评价

T ( n ) T(n) T(n)

若待排记录为从小到大排列 (正序)

  • 比较次数: n − 1 n-1 n1
  • 移动次数: 0 0 0

若待排记录为从大到小排列

  • 比较次数: ∑ i = 2 n i = ( n + 2 ) ( n − 1 ) 2 \sum_{i=2}^n i = \frac {(n+2)(n-1)}{2} i=2ni=2(n+2)(n1)
  • 移动次数: ∑ i = 2 n ( i + 1 ) = ( n + 4 ) ( n − 1 ) 2 \sum_{i=2}^n (i+1)= \frac {(n+4)(n-1)}{2} i=2n(i+1)=2(n+4)(n1)

若待排序记录是随机的,取平均值

  • 比较及移动次数均约为 n 2 4 \frac {n^2}{4} 4n2

∴ T ( n ) = O ( n 2 ) \therefore T(n)=O(n^2) T(n)=O(n2)

S ( n ) S(n) S(n)

S ( n ) = O ( 1 ) S(n)=O(1) S(n)=O(1)

是否稳定

  • 稳定

折半插入排序 Binary Insertion Sort

排序过程

思路:

  • 由于直接插入排序的基本操作是在一个有序表中进行查找和插入,因此查找的过程可以用折半查找来实现

具体实现:

  • 待排序记录为R[i],将其与R[j] (j=i-1)进行比较:

R[i]≧R[j]R[i]位置不变
R[i]

  • R[i]移至监视哨
  • low=1,high=i-1mid=(low+high)/2
  • R[i]R[mid]比较,进行折半查找,直到low>high。将i之前lowlow之后的元素后移
  • R[0]插入到low所指示的位置

算法实现

void Binary_insert(SqList_t* list, Rec_t* data, int len)
{
	int low = 1;
	int high = len - 1;
	int mid;

	while (low <= high)
	{
		mid = (low + high) / 2;

		if (list->rec[mid].key > (*data).key)  //注意使排序算法保持稳定性
		{
			high = mid - 1;
		}
		else {
			low = mid + 1;
		}
	}

	for (int i = len; i > low; --i)
	{
		list->rec[i] = list->rec[i - 1];
	}
	list->rec[low] = *data;
}

void Binary_insertion_sort(SqList_t* list)
{
	for (int i = 2; i <= list->len; ++i)
	{
		if (list->rec[i].key < list->rec[i - 1].key)
		{
			list->rec[0] = list->rec[i];
			Binary_insert(list, &list->rec[0], i);
		}
	}
}

算法评价

T(n)

减少了比较次数,但移动次数不变

∴ T ( n ) = O ( n 2 ) \therefore T(n)=O(n^2) T(n)=O(n2)

S(n)

S ( n ) = O ( 1 ) S(n)=O(1) S(n)=O(1)

是否稳定

  • 稳定

希尔排序 Shell Sort

排序过程

分析:

  • 直接插入排序:当待排序列为正序时,时间复杂度可提高到 O ( n ) O(n) O(n)。待排序列基本有序 (满足 R [ i ] ≥ m a x 1 ≤ j < i { R [ j ] } R[i]\geq{max}_{1\leq j < i}\{R[j]\} R[i]max1j<i{R[j]} 的记录较多) 时,直接插入排序的效率会大大提高
  • 直接插入排序 O ( n 2 ) O(n^2) O(n2) 的时间复杂度,在 n n n 很小时效率也比较高

思路:

  • 因此,根据上述分析可以做出如下改进:先将整个待排序记录分割成若干个子序列分别进行直接插入排序,待整个序列中的记录“基本有序”时,再对全体记录进行一次直接插入排序

排序过程:

  • 先取一个正整数 d 1 < n d_1d1<n,把所有相隔 d 1 d_1 d1 的记录放一组,组内进行直接插入排序
  • d 2 < d 1 d_2d2<d1,重复上述分组和排序操作
  • d 3 < d 2 d_3d3<d2,……,直至 d t d_t dt=1,即所有记录放进一个组中排序为止
  • 增量序列取法: d 1 > d 2 > . . . > d t = 1 d_1>d_2>...>d_t=1 d1>d2>...>dt=1,且无除1以外的公因子
    内部排序 (二):插入排序 (直接插入排序、折半插入排序、希尔排序)_第2张图片

算法实现

//进行一趟插入排序
void Shell_insert(SqList_t* list, int d)
{
	for (int i = 1 + d; i <= list->len; ++i)
	{
		int j = i - d;

		if (list->rec[i].key < list->rec[j].key)
		{
			list->rec[0] = list->rec[i];

			do {
				list->rec[j + d] = list->rec[j];
				j -= d;
			} while (j > 0 && list->rec[j].key > list->rec[0].key);

			list->rec[j + d] = list->rec[0];
		}
	}
}

void Shell_sort(SqList_t* list)
{
	int d[] = { 5, 3, 1 };

	for (int i = 0; i < 3; ++i)
	{
		Shell_insert(list, d[i]);
	}
}

算法评价

T ( n ) T(n) T(n)

  • T ( n ) T(n) T(n) 为所取增量序列的函数。目前还没有求得一种最好的增量序列; 但有研究指出,增量序列为 d k = 2 t − k + 1 − 1 d_k=2^{t-k+1}-1 dk=2tk+11 ( t t t 为排序趟数) 时, T ( n ) = O ( n 1.5 ) T(n)=O(n^{1.5}) T(n)=O(n1.5) T ( n ) T(n) T(n) 下界可达 O ( n log ⁡ 2 n ) O(n\log_2n) O(nlog2n)

S ( n ) S(n) S(n)

S ( n ) = O ( 1 ) S(n)=O(1) S(n)=O(1)

是否稳定

  • 不稳定

你可能感兴趣的:(数据结构与算法,算法,数据结构,排序算法,插入排序)