算法导论笔记2.1 - 2.2

本文详细介绍了插入排序算法的执行过程,包括输入输出、伪代码、C++实现及运行结果。并深入探讨了算法分析,阐述了循环不变式的重要性及其证明过程,以及算法在不同情况下的运行时间复杂度。此外,还包含了对练习题目的解析。
摘要由CSDN通过智能技术生成

插入排序

1 算法执行

1.1 输入与输出

Funtion Name: INSERTION-SORT
Input: array of <a1, a2, ..., an>;
Output: array of <a1’, a2’, …, an’>, a1’<=a2’<=…<=an’;

1.2 伪代码:

INSERTION-SORT(A)
for j = 2 to A.length
   key = A[j]
   //Insert A[j] into the sorted sequence A[1…j-1].
   i = j - 1
   while i > 0 and A[i] > key
      A[i + 1] = A[i]
      i = i - 1
   A[i + 1] = key

1.3 C++代码:

#include<iostream>
using namespace std;

template<typename T>
void insertionSort(T* a, int length)
{
   
	T key;
	int i;
	for (int j = 1; j < length; j++)
	{
   
		key = a[j];
		i = j - 1;
		while (i >= 0 && a[i] < key)
		{
   
			a[i + 1] = a[i];
			i--;
		}
		a[i + 1] = key;
	}
}
template<typename T>
void Printf(T* a, int length)
{
   
	cout << "Result after insertionSort:" << endl;
	for (int i = 0; i < length; i++)
	{
   
		cout << a[i] << "\t";
	}
	cout << endl;
}

int main()
{
   
	int a[6] = {
    5,2,4,6,1,3 };
	insertionSort<int>(a, 6);
	Printf<int>(a, 6);
	system("pause");
}

1.4 运行结果:

运行结果

2 算法分析

2.1 过程描述

INSERTION-SORT(A)
for j = 2 to A.length
   key = A[j]
   //Insert A[j] into the sorted sequence A[1…j-1].
   i = j - 1
   while i > 0 and A[i] > key
      A[i + 1] = A[i]
      i = i - 1
   A[i + 1] = key

类似打扑克时(默认手牌按序排列)每次都从牌堆里抓一张牌,然后向手牌里插入,当然,正常我们的插入是直接把牌插在他该插入的地方,而在计算机里是前面的小牌不动,后面的大牌都往后挪一格,给抓到的新牌空出位置。

在这里插入图片描述
(注意,C++和伪代码的数组前者是从0开始而后者是从1开始,以下描述基于Cpp规则)

  1. 第一次循环:j = 0时不必排序,第一张牌直接放手里即可。
  2. 第二次循环:j = 1时进入循环。key = A[1],i = j - 1 = 0,判决i >= 0 且 A[i] > key,满足,故A[1] = A[0],即用5覆盖2,然后i = i - 1 = -1,推出while循环。然后A[0] = 2。此时前两个数2,5就排好序了。
  3. 第三次循环:j = 2时相当于又摸了一张4,手里牌时2和5。key = A[2],i = j - 1 = 1,判决i >= 0且A[i] > key, 满足,故A[2] = A[1],即用5覆盖4。此时前三个数为255,key依旧为4。i = i - 1 = 0返回while循环,此时A[i] > key不满足,退出循环。然后A[i+1] = A[1] = key = 4,即由此确定了4该插入的位置,前三个数排好了。
  4. …以此类推。

每一次循环均可使排好序的前一部分序列多一个数字,以此类推,到最后可以使得整个序列完成排序。这里每一次循环都会出现比上一次循环多一个数的排序序列,称之为这一算法的循环不变式。

2.2 循环不变式

在编写循环时,找到让每次循环都成立的逻辑表达式很重要。这种逻辑表达式称为循环不变式(loop invariant)。循环不变式相当于用数学归纳法证明的“断言”。循环不变式用于证明程序的正确性。

循环不变式必须满足三个性质(即证明过程):

  1. 初始化(Initialization):
    它在循环的第一轮迭代开始之前,应该是正确的。
  2. 保持(Maintenance):
    如果在循环的某一次迭代开始之前它是正确的,那么,在下一次迭代开始之前,它也应该保持正确。
  3. 终止(Termination):
    当循环结束时,不变式给了我们一个有用的性质,它有助于表明算法是正确的。

我们可以和数学归纳法对比来看。数学归纳法的证明过程如下(以自然数为例):

  1. 奠基:即在初始n = 0下成立
  2. 递推:证明在n = m下成立时,n = m + 1同样成立

对比二者的关系,对于循环不变式,我们可以发现其原理和数学归纳法类似。若要证明每一步都满足循环不变式(断言),则可以按照奠基和递推的方式来证明,不过算法肯定是有终止的,而关于自然数的证明大部分是无休止的(自然数无限多),故算法也要满足在循环结束后算法的输出也是正确(书中称为“真”,其含义为循环不变式是一个逻辑表达式,其逻辑值为true)。

在这一算法中,其循环不变式为:

子数组A[1...j - 1]由原来在A[1...j - 1]中的元素构成,但子数组以按序排列

按循环不变式的三条性质证明如下(按伪代码为例):

  1. 初始化:
    在循环的第一轮迭代开始前,即j = 2,此时子数组只有一个元素A[1],自然满足排序和子数组两个约束。
  2. 保持:
    假设循环进行到第k次后,第k + 1次开始之前,循环是正确的,也就是说,此时前k个数A[1…k]是前k个数排好序的数组,也是原A[1…k]的子数组。那么第k + 1次执行后,key = A[k + 1],i = k,以上两个初值赋完后进入while循环。首先对A[1…k]中后面的数进行判断,如果后面的数大,就向后移位,i自减,判断下一个;如果i < 0(全判断完)或A[i] <= key(数据已经找到了该插入的位置)&#x
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值