【算法与数据结构】算法分析 二
不变量是验证算法正确性的有力工具,我们经常会见到并且也是特别重要的便是 循环不变量,这是在程序迭代过程或循环时产生的不变量。循环不变量可以帮助我们分析和建立许多简单排序算法的正确性,下面我们将以此为例进行探讨。
本文将涵盖:
- 使用不变量分析算法
- 强与弱不变量
- 使用这些不变量分析排序算法
- 排序算法的最好,平均,最坏性能
1 简单排序算法
排序算法是最适合用来学习算法分析的例子,你可能已经熟悉一些简单的二次 ( O ( n ² ) ) (O(n²)) (O(n²)) 排序算法,比如
- 冒泡排序 (Bubble Sort)
- 选择排序 (Selection Sort)
- 插入排序 (Insertion Sort)
你可能也对一些更快的 ( O ( n l o g ( n ) ) ) (O(nlog(n))) (O(nlog(n))) 排序算法有所了解。本文我们主要来分析选择排序和插入排序,比较两种排序算法并探索其基本的潜在属性。
1.1 选择排序
选择排序的基本思想基于以下几点
- 将要排序的列表分为两部分
- 第一部分由当前已经排序好的元素组成
- 第二部分由尚未排序的元素组成
- 第一部分 (已排序) 最初是空的,第二部分 (待排序) 则是整个列表
- 我们将这样进行排序
- 在未排序的部分中搜索最小的元素
- 将最小元素与未排序部分的第一个元素互换位置
- 已排序部分现在就多了一个元素
为了更具体的表达,选择排序的实现伪代码如下
function SELECTION_SORT(array[1..n])
for i = 1 to n do
min = i
for j = i + 1 to n do
if array[j] < array[min] then
min = j
swap(array[i], array[min])
让我们试着更有逻辑地理解一下这里发生了什么。在算法的主循环中给定某个 i i i 值时,我们会得到两个子列表
- a r r a y [ 1.. i − 1 ] array[1..i-1] array[1..i−1],已排序部分
- a r r a y [ i . . n ] array[i..n] array[i..n],未排序部分
选择排序背后的关键策略只是说在保持以下不变量的同时使 i i i 逐渐增加。
对于选择排序的主循环中任意 i i i 来说,迭代开始时,以下不变量成立:
- a r r a y [ 1.. i − 1 ] array[1..i-1] array[1..i−1] 是排好序的
- 对于 a r r a y [ 1.. i − 1 ] array[1..i-1] array[1..i−1] 中任意 x 与 a r r a y [ i . . n ] array[i..n] array[i..n] 中任意 y, x ≤ y x ≤ y x≤y
使用这个不变量就能很容易的证明选择排序算法的正确性。
不变量的初始状态
初始当 i = 1 i=1 i=1 时,数组已排序部分 a r r a y [ 1..0 ] array[1..0] array[1..0] 为空,不变量成立。这里为了方便我们一般使用 a r r a y [ 1..0 ] array[1..0] array[1..0] 或者 a r r a y [ n + 1.. n ] array[n+1..n] array[n+1..n] 表示空数组。
不变量维持
主循环的每次迭代我们都会在 i ≤ j ≤ n i≤j≤n i≤