当前课程知识点:数据结构(上) >  第一章 绪论(下) >  (e)迭代与递归 >  01E-8 二分递归:Max2

返回《数据结构(上)》慕课在线视频课程列表

01E-8 二分递归:Max2在线视频

01E-8 二分递归:Max2

下一节:01E-09: Max2:二分递归

返回《数据结构(上)》慕课在线视频列表

01E-8 二分递归:Max2课程教案、知识点、字幕

减而治之和分而治之

都是我们算法设计中的

强有力武器

然而很遗憾

我们前面所举的例子

因为过于简单

这两个方法的优势

并没有得到体现

当然在后续的

章节的学习的过程中

我们可以很清楚地看到这一点

在此呢

我们不妨再举一个例子

来稍微看一下

至少在算法复杂度的常系数方面

在采用了分而治之的策略以后

是有可能有所改进的

我们来看这样一个问题

叫Max2 意思就是说

我们能否在一个

给定的数组区间中

找出最大的两个元素

比如说 我们习惯认为

x1是最大的 x2是次大的

当然在退化的情况下

它们两个可能本身就是相等的

但是一般而言

前者是不会小于后者的

这里呢 采用了

Python的习惯 也就是

我们用lo和hi来表示

这个有效的区间

而且分别用方括号和圆括号

来表示这个区间的

左边界和右边界

究竟是闭的 还是开的

所以准确地讲

这里包含的元素是lo

如果还有足够长的话

以及lo+1 lo+2

一直到最后的一个

并不是hi 而是hi-1

所以 从图示来讲呢

我们一般以后 会画成这样

一个是lo 一直包括后面的

lo+1 lo+2这种彩色的部分

一直到最后这个

你可以认为是hi-1

这个hi呢

并不是我们有效部分

甚至它可能根本就不存在

但是我们在算法设计

和分析的时候呢

不妨把它加在这

后面会看到 这是我们

常用的一种技巧和手段

它可以使得我们思考更加简便

也可以使得我们对程序的理解

更加的准确和精炼

那么这个算法本身并不难

我们这里考量的呢

是要求在整个这个算法过程中

进行元素比较操作的次数

就是比较大小操作的这样的次数

需要尽可能的少

比如说

这就是一个最简明的算法

我们看一下

它大概是由三趟循环构成

第一趟循环

是x1从lo开始 不断地到hi

逐个地扫描

扫描什么呢?

我们通过这样一个比较

每次都对x1

如果有必要做一个更新

使得它总是保存着

或者是指向当前

最大的那个元素

所以经过这样第一个循环之后

我们说x1

必然会在某个位置被发现

比如说在这

接下来呢

后面的两轮循环

实际上是分别在x1的

所有的前驱中

以及它的后缀中进行扫描

从而用相同的策略找出

在除了x1以外的

那些元素中的最大值

当然也就是全局的次大值

我们可以看到这里的比较的方法

都是一样的

这里就不再赘述

我们这里关心的是

它所需要的

刚才我们说的 比较次数

多少次比较呢?

我们可以关注这里头的小于号

这就是比较操作

它们的次数是多少呢?

第一轮循环

总共要比较n-1次

后面两轮的长度 或长或短

但是它们的总数很好判断出来

其实就是在原来的

n-1次比较中

除去了已经被挑选出来的

最大的元素x1

所以应该是n-2

所以我们累计起来

比较操作的总数应该是(2n-3)

而且这样一个算法

无论输入是如何组成的

它都需要做固定次数的循环

确切地讲

总数也是固定的(2n-3)

好情况和最坏情况都是一样的

那么能不能更少呢?

我们来试图

从一个方面进行改进

我们的改进思路

可以用这样一张图来表示

同样是刚才的n个元素

我们可以维护x1和x2两个指针

分别指向当前最大和次大的元素

我们整个的算法过程呢

只是一趟扫描

在经过了初始化之后

在这个扫描过程中

当扫到第i个元素的时候

我们都首先

将它和x2进行比较

如果它比x2大

再进而跟x1做比较

这样的话 我们就有可能会用

这个第i个元素来更新x2

甚至更新x1

在很多情况下

我们希望第i个元素

是小于x2的

在这样的情况下

x1和x2是保持不变的

从而减少一次比较

这样的一个过程

我们可以用

这段代码来具体的实现

我们可以来看一下

第一行 可以认为是一个初始化

因为它首先将x1指向lo

将x2指向lo+1

也就是 最开始的前面两个元素

并且对它们做一次比较判断

如果它们按照刚才的

那个约定是逆序的

我们就将它们交换好了

所以经过这样初始化以后

可以确认x1和x2

确实是当前已经扫描过的范围

也就是前两个元素中的

最大者和次大者

而接下来呢 这样一个循环

确实像刚才所说的那样

会从第三个元素开始 逐一地递增

每当指向一个元素Ai以后

我们都首先将Ai拿出来

和x2做比较

而只有当它胜出了x2之后

我们才会将x2更新为i

并且进而用这个更新的数值去和

最大的那个元素x1做比较

并且在确实有必要的时候

才将二者交换位置

从而实现当前最大元素

和次大元素的更新

同样提醒同学们注意

这里的两个隐藏的配对的

else else语句

它们虽然是看不见的

但是它们的语义意味着什么

希望同学们要能够了解

好了 关于这个算法的正确性

我们就分析到这

接下来 我们最主要的是

要来分析 这样的一个算法的性能

或者复杂度 是否有所改进

我们可以来看一下

同样 如果只关注

比较的次数的话

这个比较是确定的 是一次

可以暂时放在一边

最重要的是

这个循环中间的比较次数

这个时候 循环的长度是n-2

但是每步循环的内部

所需执行的比较次数却是不定的

这里有好和坏两种情况

最好的情况只执行这一次

就通过刚才所说的那个隐含的else

我们可以认为它是一个continue

直接又接着去执行下一次循环去了

而在最坏的情况下呢

有可能执行完了这一次

并且命中了if这个条件

从而进入到下一个if判断

换句话说 在最坏的情况下

我们每次循环

需要进行地不是一次

而是两次比较

所以我们把这个归纳下来

说 在最好的情况下

除了最初的初始化

需要一次比较之外

剩下的(n-2)步

各自只需要一次比较

累计而言 总共不过n-1次比较

我们说这个非常好

比我们刚才的2n-3

有很大的改进

几乎少了一半

但是我们刚才也说到了

在最坏的情况下

每次循环的内部 实际上

需要执行两次比较

我们可以看到

这样一合计以后

结果和刚才是完全一样的

换而言之 就最坏情况而言

这样的一个算法

这样一个结果

并没有实质的改进

那么能否从实质上

对这样的一个算法进行改进

使得它即便在最坏的情况下

也严格地少于2n-3次比较呢

我们说可以

为此我们就需要用到

刚才介绍的分而治之的策略

数据结构(上)课程列表:

第零章

-选课之前

--写在选课之前

--宣传片

-考核方式

--考核方式

-OJ系统说明

--关于OJ

--1-注册与登录

--2-界面与选课

--3-提交测试

-关于课程教材与讲义

--课程教材与讲义

-关于讨论区

--关于讨论区

-微信平台

--html

-PA晋级申请

--PA晋级

--MOOC --> THU 晋级申请专区

--THU --> CST 晋级申请专区

--编程作业不过瘾?且来清华试比高!

第一章 绪论(上)

-(a)计算

--01-A-1: 计算

--01a-2: 绳索计算机

--01a-3: 尺规计算机

--01a-4: 算法

--01a-5 : 有穷性

--演示

--01a-6 : 好算法

--(a)计算--作业

-(b)计算模型

--01b-1: 性能测度

--01b-2: 问题规模

--01b-3: 最坏情况

--01b-4: 理想模型

--01b-5: 图灵机

--01b-6: 图灵机实例

--01b-7: RAM模型

--01b-8: RAM实例

-(b)计算模型--作业

-(c)大O记号

--01c-1: 主流长远

--01c-2: 大O记号

--01c-3: 高效解

--01c-4 : 有效解

--01c-5 : 难解

--01c-6: 2−Subset

--01c-7: 增长速度

-(c)大O记号--作业

第一章 绪论(下)

-(d)算法分析

--01d-1: 算法分析

--01d-2: 级数

--01d-3: 循环

--01d-4: 实例:非极端元素+起泡排序

--01d-5: 正确性的证明

--01d-6: 封底估算-1

--01d-7: 封底估算-2

-(d)算法分析--作业

-(e)迭代与递归

--01-E-1: 迭代与递归

--01-E-2: 减而治之

--01-E-3: 递归跟踪

--01-E-4: 递推方程

--01-E-5: 数组倒置

--01-E-6: 分而治之

--01-E-7: 二分递归:数组求和

--01E-8 二分递归:Max2

--01E-09: Max2:二分递归

-(e)迭代与递归--作业

-(xc)动态规划

--01XC-1: 动态规划

--01XC-2: Fib():递推方程

--01XC-3: Fib():封底估算

--01XC-4: Fib():递归跟踪

--01XC-5: Fib():迭代

--01XC-6: 最长公共子序列

-- 演示

--01XC-7: LCS:递归

--01XC-8: LCS:理解

--01XC-9: LCS:复杂度

--01XC-A: LCS:动态规划

-(xc)动态规划--作业

-本章测验--作业

第二章 向量(上)

-(a)接口与实现

--02A-1 接口与实现

--02A-2 向量ADT

--02A-3 接口操作实例

--02A-4 构造与析构

--02A-5 复制

-(a)接口与实现--作业

-(b)可扩充向量

--02B-1 可扩充向量

--02B-2 动态空间管理

--02B-3 递增式扩容

--02B-4 加倍式扩容

--02B-5 分摊复杂度

-(b)可扩充向量--作业

-(c)无序向量

--02C-1 概述

--02C-2: 循秩访问

--02C-3 插入

--02C-4 区间删除

--02C-5 单元素删除

--02C-6 查找

--02C-7 唯一化

--02C-8 遍历

-(c)无序向量--作业

-(d1)有序向量:唯一化

--02D1-1 有序性

--02D1-2 唯一化(低效版)

--02D1-3 复杂度(低效版)

--02D1-4 唯一化(高效版)

--02D1-5 实例与分析(高效版)

-(d1)有序向量:唯一化--作业

-(d2)有序向量:二分查找

--02D2-1 概述

--02D2-2 接口

--02D2-3 语义

--02D2-4 原理

--02D2-5 实现

--02D2-6 实例

--02D2-7 查找长度

-(d2)有序向量:二分查找--作业

第二章 向量(下)

-(d3)有序向量:Fibonacci查找

--02D3-1 构思

--02D3-2 实现

--02D3-3 实例

--02D3-4 最优性

-(d3)有序向量:Fibonacci查找--作业

-(d4)有序向量:二分查找(改进)

--02D4-1 构思

--02D4-2 版本B

--02D4-3 语义

--02D4-4 版本C

--02D4-5 正确性

-(d4)有序向量:二分查找(改进)--作业

-(d5)有序向量:插值查找

--02D5-1 原理

--02D5-2 实例

--02D5-3 性能分析

--02D5-4 字宽折半

--02D5-5 综合对比

-第二章 向量(下)--(d5)有序向量:插值查找

-(e)起泡排序

--02 E-1 构思

--02E-2 改进

--02E-3 反例

--02E-4 再改进

--02E-5 综合评价

-(e)起泡排序--作业

-(f)归并排序

--02F-1 归并排序:构思

--02F-2 归并排序:主算法

--02F-3 二路归并:实例

--02F-4 二路归并:实现

--02F-5 二路归并:正确性

--02F-6 归并排序:性能分析

-(f)归并排序--作业

-本章测验--作业

第三章 列表

-(a)接口与实现

--03A-1 从静态到动态

--03A-2 从向量到列表

--03A-3 从秩到位置

--03A-4 实现

-(a)接口与实现--作业

-(b)无序列表

--03B-1 循秩访问

--03B-2 查找

--03B-3 插入与复制

--03B-4 删除与析构

--03B-5 唯一化

-(b)无序列表--作业

-(c)有序列表

--03C-1 唯一化·构思

--03C-2 唯一化·实现

--03C-3 查找

-(c)有序列表--作业

-(d)选择排序

--03D-1 构思

--03D-2 实例

--03D-3 实现

--03D-4 推敲

--03D-5 selectMax()

--03D-6 性能

-(d)选择排序--作业

-(e)插入排序

--03E-1 经验

--03E-2 构思

--03E-3 对比

--03E-4 实例

--03E-5 实现

--03E-6 性能分析

--03E-7 平均性能

--03E-8 逆序对

-(e)插入排序--作业

-(xd)习题辅导:LightHouse

--03X D 习题辅导:LightHouse

-本章测验--作业

第四章 栈与队列

- (a)栈接口与实现

--04A-1 栈

--04A-2 实例

--04A-3 实现

- (a)栈接口与实现--作业

-(c1)栈应用:进制转换

--04C1-1 应用

--04C1-2 算法

--04C1-3 实现

-第四章 栈与队列--(c1)栈应用:进制转换

-(c2)栈应用:括号匹配

--04C2-1 实例

--04C2-2 尝试

--04C2-3 构思

--04C2-4 实现

--04C2-5 反思

--04C2-6 拓展

-(c2)栈应用:括号匹配--作业

-(c3)栈应用:栈混洗

--04C3-1 混洗

--04C3-2 计数

--04C3-3 甄别

--04C3-4 算法

--04C3-5 括号

-第四章 栈与队列--(c3)栈应用:栈混洗

-(c4)栈应用:中缀表达式求值

--04C4-1 把玩

--04C4-2 构思

--04C4-3 实例

--04C4-4 算法框架

--04C4-5 算法细节

--04C4−6A 实例A

--04C4−6B 实例B

--04C4−6C 实例C

--04C4-6D 实例D

-(c4)栈应用:中缀表达式求值--作业

-(c5)栈应用:逆波兰表达式

--04C5-1 简化

--04C5-2 体验

--04C5-3 手工

--04C5-4 算法

-第四章 栈与队列--(c5)栈应用:逆波兰表达式

-(d)队列接口与实现

--04D-1 接口

--04D-2 实例

--04D-3 实现

-第四章 栈与队列--本章测验

第五章 二叉树

-(a)树

--05A-1 动机

--05A-2 应用

--05A-3 有根树

--05A-4 有序树

--05A-5 路径+环路

--05A-6 连通+无环

--05A-7 深度+层次

-(a)树--作业

-(b)树的表示

--05B-1 表示法

--05B-2 父亲

--05B-3 孩子

--05B-4 父亲+孩子

--05B-5 长子+兄弟

-第五章 二叉树--(b)树的表示

-(c)二叉树

--05C-1 二叉树

--05C-2 真二叉树

--05C-3 描述多叉树

-(c)二叉树--作业

-(d)二叉树实现

--05D-1 BinNode类

--05D-2 BinNode接口

--05D-3 BinTree类

--05D-4 高度更新

--05D-5 节点插入

-(d)二叉树实现--作业

-(e1)先序遍历

--05E1-1 转化策略

--05E1-2 遍历规则

--05E1-3 递归实现

--05E1-4 迭代实现(1)

--05E1-5 实例

--05E1-6 新思路

--05E1-7 新构思

--05E1-8 迭代实现(2)

--05E1-9 实例

-(e1)先序遍历--作业

-(e2)中序遍历

--05E2-1 递归

--05E2-2 观察

--05E2-3 思路

--05E2-4 构思

--05E2-5 实现

--05E2-6 实例

--05E2-7 分摊分析

-第五章 二叉树--(e2)中序遍历

-(e4)层次遍历

--05E4-1 次序

--05E4-2 实现

--05E4-3 实例

-第五章 二叉树--(e4)层次遍历

-(e5)重构

--05E5-1 遍历序列

--05E5-2 (先序|后序)+中序

--05E5-3 (先序+后序)x真

-(e5)重构--作业

-本章测验--作业

第六章 图

-(a)概述

--06A-1 邻接+关联

--06A-2 无向+有向

--06A-3 路径+环路

-(a)概述--作业

-(b1)邻接矩阵

--06B1-1 接口

--06B1-2 邻接矩阵+关联矩阵

--06B1-3 实例

--06B1-4 顶点和边

--06B1-5 邻接矩阵

--06B1-6 顶点静态操作

--06B1-7 边操作

--06B1-8 顶点动态操作

--06B1-9 综合评价

-(b1)邻接矩阵--作业

-(c)广度优先搜索

--06C-1 化繁为简

--06C-2 策略

--06C-3 实现

--06C-4 可能情况

--06C-5 实例

--06C-6 多连通

--06C-7 复杂度

--06C-8 最短路径

-(c)广度优先搜索--作业

-(d)深度优先搜索

--06D-1 算法

--06D-2 框架

--06D-3 细节

--06D-4 无向图

--06D-5 有向图

--06D-6 多可达域

--06D-7 嵌套引理

-(d)深度优先搜索--作业

-第六章 图--本章测验

01E-8 二分递归:Max2笔记与讨论

也许你还感兴趣的课程:

© 柠檬大学-慕课导航 课程版权归原始院校所有,
本网站仅通过互联网进行慕课课程索引,不提供在线课程学习和视频,请同学们点击报名到课程提供网站进行学习。