分治策略:将原问题划分为n个规模较小而结构与原问题相似的子问题;递归的解决这些子问题,然后再合并这些结果,得到原问题的解。
分治模式在每一层递归上都有三个步骤:
分解:将原问题分解为一系列子问题
解决:递归的解决个子问题,若子问题足够小,则直接求解
合并:将子问题的结果合并成原问题的解
合并排序:
分解:将n个元素分成等两份
解决:用合并排序法对两个子序列递归的排序
合并两个已排序的子序列以得到排序结果
对子序列排序时,长度为1时递归结束。
算法分析:对一个算法所需要的资源进行预测。(内存、通讯带宽或计算机硬件等资源偶尔会是我们主要关心的)通常资源是指我们希望测度的计算时间。
实现算法之前确定实现技术的模型:通用的单处理器、随机存取机(RAM),该模型中的指令是一条一条执行的,没有并发操作。
RAM模型包含了计算机中的常见指令:
算数指令:加法、减法、乘法、除法、取余、向下取整、向上取整指令
数据移动指令:装入、存储、复制指令
控制指令:条件和非条件转移、子程序调用和返回指令
输入规模:与具体问题有关,有时指的是输入中的元素个数,如果是图,度量标准是边和节点数。
运行时间:在特定输入时,所执行的基本操作数或(步数)。
插入排序的算法分析:
INSERTION-SORT(A) cost times
1 for j <— 2 to length[A] c1 n
2 do key <— A[j] c2 n-1
3//insert A[j] into the sorted sequence A[1...j-1] 0 n-1
4 i <— j-1 c4 n-1
5 while i > 0 and A[i] > key //降序排列 c5 t(j) j从2到n求和
6 do A[i+1] <— A[i] c6 {t(j)-1} j从2到n求和
7 i <— i-1 c7 {t(j)-1} j从2到n求和
8 A[i+1] <— key c8 n-1
如果给定的数据已经排好序,出现最佳的情况,最终结果是一个n的线性函数。O(n)
如果给定的是一个逆序的序列,出现最坏的时间效率,结果是n的二次函数。O(n^2)
一般考察算法的最坏情况运行时间(大致情况看,一般最坏时间效率和平均情况效率是相同输入规模的函数,运行时间是一样的。)
增长的量级:
运行时间的增长率,只考虑公式的最次高项,并忽略最高次项的常数系数。(如输入规模足够大的时候,O(n2)效率比O(n3)好。)
这是一个对少量元素进行排序的有效算法。
伪代码:
INSERTION-SORT(A)
1 for j <— 2 to length[A]
2 do key <— A[j]
3 //insert A[j] into the sorted sequence A[1...j-1]
4 i <— j-1
5 while i > 0 and A[i] > key //降序排列
6 do A[i+1] <— A[i]
7 i <— i-1
8 A[i+1] <— key
对于上面算法,数据保存在数组中,输入的各个数字是原地排序的,不需要额外的存储空间,仅仅只有几个变量存储在数组之外。
当算法执行到后面的时候,发现后面的数少于前面的j-1个数据中的m个数据的时候,需要将前面m个数据依次往后移动,空出j-1-m的位置给该数。
循环不变式与插入算法的正确性
循环不变式正确性
初始化:它在循环的第一轮迭代开始之前,应该是正确的。
保持:如果在循环的某一次迭代之前它是正确的,那么下一次迭代开始之前,它应该保持正确。
终止:当循环结束时,不变式给了我们一个有用地方性质,它有助于表明算法是正确的。
伪代码中的部分约定
指针不指向任何对象,将其赋值为NIL
参数采用按值传递方式:被调用的过程会收到参数的一个副本。如果他对某个参数赋值的话,主调过程是看不见这一变动的。当对象被传递时,实际传 递的是一个指向该对象数据的指针,而对象的各个域则不被拷贝。
布尔运算符,and和or都具有短路能力。