第 五 章 减治法
算 分 析 与 设 计
西南科技大学
折半插入排序
直接插入排序算法简洁,易理解,容易 实现。当序列中的记录值较小时,它是 较佳的排序方法。但是,通常待排记录 的数量很大,此时直接插入排序就不适 用了。 对有序表采用折半查找,其性能优于顺 序查找,所以直接插入排序算法中的 “搜索”操作,从减少“比较”次数着 眼,可在区间[0,i-1]上进行折半查找来 实现。
下面用减治法对这个问题进行求解。当n≤2时,识别 出最重和最轻的金块,一次比较就足够了。当n>2时, 可先把这袋金块平分成两个小袋A和B。然后分别找出 在A和B中最重和最轻的金块。设A中最重和最轻的金 块分别为HA与LA,以此类推,B中最重和最轻的金块 分别为HB和LB。通过比较HA和HB及LA和LB ,可以找 到所有金块中最重和最轻的。若n>2,则递归地应用 减治法。 设f(n)为使用分而治之方法所需要的比较次数。假设n 是2的幂。当n>2时,f(n)=2f(n/2)+2;当n=2时,f(n)为 1 。使用迭代方法可知f(n) =3n/2-2。使用减治法比逐 个比较的方法少用了25%的比较次数。
算 分 析 与 设 计
西南科技大学
约瑟夫斯问题的解
对约瑟夫斯问题,当n=2a+b时,有公式表示如下: L(n)=1+2b。
其中的b值不小于0 。
算 分 析 与 设 计
当把b=n-2a代入后,上述公式就转化为: L(n)=1+2n-21+[log2n]。
其中方括号表示取整数,n表示总人数。
约瑟夫斯问题(当n=41)的计算步骤: 因n=2a+b,此a最大取5,这时2a=32,所以b=9 。 把b=9代入L(n),故在这里L(41)=19。因此站在19号位上 就不会死了。
算 分 析 与 设 计
西南科技大学
减常量减治法
减常量减治法的 典型例子就是插 入排序,它运用 的是减一技术。
算 分 析 与 设 计
西南科技大学
直接插入排序
通常将一个记录A[i](i=2,3,…,n)插入 直接插入排序与打扑克时整理手上的牌 到当前的有序表,使得插入后仍保证该 非常类似。摸来的第1张牌无须整理,此 区间里的记录是按关键字有序的操作称 后每次从桌上的牌(无序区)中摸最上面的 第i-1趟直接插入排序。在排序过程的某 1张并插入左手的牌(有序区)中正确的位 一中间时刻,A被划分成两个子区间 置上。为了找到这个正确的位置,须自 A[1..i-1](有序区)和a[i..n](无序区)。 左向右(或自右向左)将摸来的牌与左手中 直接插入排序的基本操作是将当前无序 已有的牌逐一比较。 区的第1个记录R[i]插人到有序区R[1..i-1] 中适当的位置上,使R[1..i]变为新的有序 区。
约瑟夫斯问题,是以弗拉瓦斯.约瑟夫斯的名 字命名的,他是一个著名的犹太历史学家,参 加并记录了公元66-70年犹太人反抗罗马 的起义,约瑟夫斯做为一个将军,设法守住了 裘达伯特的堡垒达47天之久,但是在城市沦 陷了以后,他和40名死硬的将士在附近的一 个洞穴中避难。在那里,这些叛乱者表决说 “要投降毋宁死”。于是,约瑟夫斯建议没个 人轮流杀死他旁边的人,而这个顺序是有抽签 决定的,约瑟夫斯有预谋地拿到了他想要的那 个签~而没死~♤ ♤
算 分 析 与 设 计
西南科技大学
一趟折半插入排序伪代码
binpass(R[],i) {temp=R[i]; //temp是监视哨,且是 是监视哨, 是监视哨 且是R[i]的副本 的副本 S=1; j=i-1; //s和j分别指示折半查找区间 和 分别指示折半查找区间 while s<=j 的下界和上界 {m=(s+j)/2; // 在 有 序 区 R[1..i-1] 中 if temp<r[m] j=m-1; 进行折半查找以确定 else s=m+1;} R[i]的插入位置 的插入位置 for (k=i-1;k<= j+1;k--) R[k+1]=R[k]; //记录后移 记录后移 R[j+1]=temp;}
算 分 析 与 设 计
西南科技大学
约瑟夫斯问题( 约瑟夫斯问题(二)
这个著名问题在西方被称为约瑟夫斯(Josephus)问 题,在日本被称为继子立问题,在中国的民间传 说以八仙落座的形式表述。均为同一类问题不同 方面,可谓是浅入深出(浅到小学生兴趣很浓, 深到大学生有研究课题)。 与约瑟夫斯问题类似的问题很多。例如:用1 到6 摆成一个圆圈。先取1,然后每数k 枚棋子就取出 第k枚棋子。要是取出的顺序刚好是1,2,3,4, 5,6。问k=?
算 分 析 与 设 计
西南科技大学
减常数因子减治法
减常数因子减治法的一个 典型算法就是折半查找 (Bin_Search)。它搜索 一个排序好的数组,将查 找目标与数组的中间位置 的元素相比,比它大则递 归查找数组的左边,反之 亦然。这个每次迭代都将 问题减小为原来的1/2。 折半查找每次都消去一个 常数因子2,因此其时间 效率为O(logn)。
算法中引进附加记录temp有两个作 算法中引进附加记录temp有两个作 temp 其一是进入查找循环之前, 用:其一是进入查找循环之前,它 保存了a[i]的值, a[i]的值 保存了a[i]的值,使得不致于因记 录的后移而丢失a[i]中的内容; a[i]中的内容 录的后移而丢失a[i]中的内容;其 二是在while循环监视”下变量j while循环监视 二是在while循环监视”下变量j是 //从第二个记录起进行插入 //从第二个记录起进行插入 否越界,一旦越界( j<1), 否越界,一旦越界(即j<1),a[0] 自动控制While循环的结束, While循环的结束 自动控制While循环的结束,从而 避免了在While While循环中每一次都要 避免了在While循环中每一次都要 检测j是否越界( 检测j是否越界(即省略了循环条件 //将第 个记录赋值给第j+1 将第j //将第j个记录赋值给第j+1 j>=1”)。因此我们把称为“ “j>=1”)。因此我们把称为“监 个记录, 个记录,直至关键字不大 视 这种技巧, 哨”于待插入记录的关键字 。这种技巧,使得测试循环条 件的时间大约减少一半, 件的时间大约减少一半,对于记录 //将第 将第i //将第i个记录插入 数较大的文件, 数较大的文件,节约的时间相当可 观。
西南科技大学
第 五 章 减治法
什么是减治法?
就是在处理问题的过程中,不断减小被处理 的问题规模的方法。
算 分 析 与 设 计
减治法的设计思想
减治技术通过建立原问题实例的解和同样问 题较小实例的解的关系,并利用这种关系, 从顶而下(递归地)或从底而上(非递归地) 解决问题。一般情况下,每次算法迭代,都 消去一个常量和一个常数因子。
算 分 析 与 设 计
西南科技大学
直接插入排序的时间复杂度
整个排序过程为进行n-1趟插入,即:先 将序列中的第1个记录看成是一个有序的 子序列,然后从第2个记录起逐个进行插 入,直至整个序列变成按从小到大排列 为止。 直接插入排序的时间复杂性为0(n2)。从 空间来看,它只需要一个记录的辅助故 空间复杂度为0(1).
仅仅通过一次重量的比较,就可以判断伪币是否存在。
算 分 析 与 设 计
西南科技大学
金块问题
有一个老板有一袋金块。每个月将有两 名雇员会因其优异的表现分别被奖励一 个金块。按规矩,排名第一的雇员将得 到袋中最重的金块,排名最后的雇员将 得到袋中最轻的金块。如果每个月都有 新的金块周期性的加入袋中,则每个月 都必须找出最轻和最重的金块。假设有 一台比较重量的仪器,我们希望用最少 的比较次数找出最轻和最重的金块。
算 分 析 与 设 计
西南科技大学
实现过程
设n=8,有下列8个数,要求从小到大排序,每 次插入时数据的变化如下: 初始状态 : [7] 2 5 1 9 6 8 3 i=1监视哨(2) : [2 7] 5 1 9 6 8 3 i=2监视哨(5) : [2 5 7] 1 9 6 8 3 i=3监视哨(1) : [1 2 5 7] 9 6 8 3 i=4监视哨(9) : [1 2 5 7 9] 6 8 3 i=5监视哨(6) : [1 2 5 6 7 9] 8 3 i=6监视哨(8) : [1 2 5 6 7 8 9] 3 i=7监视哨(3) : [1 2 3 5 6 7 8 9]
算 分 析 与 设 计
西南科技大学
俄式乘法☺ 俄式乘法☺
算法思想:两个A和B数相乘,把数A每 次除以2,直到为0为止,另一个数B则不 断加倍,若第数A未除尽时,则数B应加 上自己。 7×8的计算步骤: 7 8 3 16+ 8 1 32+ 16 + 8
算 分 析 与 设 计
西南科技大学
约瑟夫斯问题( 约瑟夫斯问题(一)
西南科技大学
常见的减治法算法
最常见的减治法有:
减常量减治法,典型的就是插入排序,问题 规模每次都减1;还有计算a的n次方,可以计 算a的n-1次方,再变成计算a的n-2次方等等。 减常数因子减治法,典型的就是折半查找, 每次都把n/2个元素删去;还有约瑟夫斯问题 约瑟夫斯问题。 约瑟夫斯问题 减可变规模减治法法,典型的就是选择问题, 每次问题规模的减小量都在1到n之间;还有 二叉树的查找。
算 分 析 与 设 计
西南科技大学
直接插入排序的另一种写法
insertionsort(a,n) int *a,n {int i,j,temp; for (i=1;i<=n-1;i++) temp=a[i]; j=i-1; while temp<a[j] {a[j+1]=a[j]; j=j-1;} a[j+1]=temp }
算 分 析 与 设 计
西南科技大学
直接插入排序实现方法
减一技术下,该方法遵循的思路是:假设对较 小数组 A[0..n-2]排序问题已经解决了,得到一 个大小为n-1的有序数组。然后将要排序的第n 个元素,插入到数组的适合位置上,得到大小 为n的有序数组 A[0..n-1]。伪代码如下: void InsertionSort(a[]) {for(i=1;i<n-1;i++) //从第二个记录起进行插入 for (j=i-1; j>=0;j--) if a[j+1]-(a[j]) < 0 Swap(a[j+1], a[j]); }