元组的风暴之最长连续递增序列
小美:前几天老师布置了一道题目:求最长连续递增子序列的长度。我感觉题目并不难,很快就做出来了。老师说我的代码有错误,可我没发现错误啊,我测试了好几组数据都是正确的。
阿福:有这种事情?你把代码发给我看看。
题目1:
求最长连续递增子序列的长度。例如,在元组(1,9,2,5,7,3,4,6,8,0)中最长连续递增子序列为(3,4,6,8),其长度为4。
函数功能:求最长连续递增子序列的长度
函数名:def sub_num(a: tuple) -> int
参数表:a -- 元组。
返回值:返回最长连续递增子序列的长度。
示例:输入a=(1,9,2,5,7,3,4,6,8,0),返回4
代码1:
def sub_num(a: tuple) -> int:
if not a: #a是空元组
return 0
max_len, c = 1,1
for i in range(1, len(a)):
if a[i]> a[i-1]:
c += 1
else:
if c> max_len:
max_len = c
c = 1
return max_len
阿福:让我仔细瞧瞧。哦,我明白了。
小美:是吗?我的代码错在哪里了?
阿福:小美,你的程序一般情况下是能得到正确结果的,但是有一种特殊情况你没有考虑到。
小美:什么特殊情况?
阿福:如果我们把测试数据改成a=(1,9,2,5,7,3,4,6,8,10),你认为应该返回几?
小美:最长连续递增子序列为(3,4,6,8,10),其长度为5。应该返回5。
阿福:没错,应该返回5。你运行一下你的程序,看看返回值是多少?
小美:程序返回3。咦,怎么回事?程序出错了!让我再好好想想。。。。。。哦,我明白了,我没有考虑最后一个元素属于最长连续递增子序列的情况。需要在循环体外面再加一条判断语句,看看是否需要更新max_len的值。
代码2:
def sub_num(a: tuple) -> int:
if not a: #a是空元组
return 0
max_len, c = 1,1
for i in range(1, len(a)):
if a[i]> a[i-1]:
c += 1
else:
if c> max_len:
max_len = c
c = 1
#若最后一个元素属于最长连续递增子序列,则要在循环体外更新max_len的值
if c >max_len:
max_len = c
return max_len
阿福:没错,因为你是在a[i] <=a[i-1]时更新max_len,此时a[i]不属于当前正在处理的递增子序列,所以在循环体外还要做一次判断,若最后一个元素属于最长连续递增子序列,则要在循环体外更新max_len的值。这是正确的方法,但不是唯一的方法,我只要调整一下代码1中循环体内某些语句的位置,就可以避免出错了。
小美:是吗?不需要在循环体外更新max_len的值?
阿福:是的,如果我在a[i] > a[i-1]时更新max_len,则不需要单独判断最后一个元素。
代码3:
def sub_num(a: tuple) -> int:
if not a: #a是空元组
return 0
max_len, c = 1,1
for i in range(1, len(a)):
if a[i]> a[i-1]:
c += 1
if c> max_len:
max_len = c
else:
c = 1
return max_len
古老师:干得漂亮!阿福的代码越来越简明了。小美也不错,一点就通。“求最长连续递增子序列的长度”是一道经典的求最优解问题,使用简单的顺序查找算法就能搞定。我想在此基础上增加点难度,那就是不仅仅要求出最长连续递增子序列的长度,还要输出该子序。如果存在多个子序列长度相同,则输出第一个最长连续递增子序列。
题目2:
求首个最长连续递增子序列。例如,在元组(1,9,2,3,5,6,6,7,8,10)中首个最长连续递增子序列为(2,3,5,6)
函数功能:求首个最长连续递增子序列
函数名:def increasing_subsequence(a:tuple) -> tuple
参数表:a -- 元组。
返回值:返回存储了首个最长连续递增子序列的元组。
示例:输入a=(1,9,2,3,5,6,6,7,8,10),返回(2,3,5,6)
小美:这个不难啊!前面的程序已经求出了最长连续递增子序列的长度,只需要再给出子序列的右边界,就能确定这个子序列了。
阿福:没错,只要在更新最长子序列长度的时候,同时更新一下右边界就行了。
古老师:很好!那你们就增加一个表示右边界的right变量,分别在代码2和代码3的基础上实现新的功能吧。
代码4:
#根据max_len和right来确定子序列
def increasing_subsequence(a: tuple) -> tuple:
if not a: #a是空元组
return ()
max_len, c, right = 1, 1, 0
for i in range(1, len(a)):
if a[i] > a[i-1]:
c += 1
else:
if c > max_len:
max_len, right = c, i - 1
c = 1
#考虑最后一个元素属于最长连续递增子序列的情形
if c > max_len:
max_len, right = c, len(a) - 1
return tuple(a[right-max_len+1:right+1])
代码5:
#根据max_len和right来确定子序列
def increasing_subsequence5(a:tuple) -> tuple:
if not a: #a是空元组
return ()
max_len, c, right = 1, 1, 0
for i in range(1, len(a)):
if a[i] > a[i-1]:
c += 1
if c > max_len:
max_len, right = c, i
else:
c = 1
return tuple(a[right-max_len+1:right+1])
古老师:都做得很好!你们都是根据max_len和right来确定子序列的,那这是唯一的方法吗?
小美:应该不是。也可以根据max_len和left来确定子序列。
阿福:还可以根据left和right来确定子序列。
古老师:完全正确!只要知道了一个子序列的左边界、右边界和长度中的任意两个条件,就可以确定这个子序列。刚才我们已经分析了其中的一种情况,接下来的另外两种情况就交给你们课后思考吧。我先走了,再见!
彩蛋:
阿福:在代码5中我可以直接用i值来更新right的值,如果是设置左边界left,又该怎么更新left的值呢?这还真有点难度呢。
小美:是啊,当a[i] > a[i-1]时,i本身就是递增子序列的右边界,直接用它更新right的值就好了,可是left的值又该取多少呢?
阿福:先让我梳理一下思路。在代码5中max_len和right分别表示最优解的长度和右边界,c和i分别表示当前解的长度和右边界。当c > max_len时,我们需要更新最优解的值。这样的话。。。。。。啊哈,我明白了!
小美:对对对,我也想到了!我们可以同时设置两个变量L和left,分别表示当前解和最优解的左边界。
阿福:没错!这样一来当前解的长度就等于i – L + 1,甚至可以省略变量c了。
小美:是的。要不这次我们换一下,你在代码4的基础上修改,我在代码5的基础上修改。
阿福:好的,各种写法都应该尝试一下。
代码6:
#根据max_len和left来确定子序列
def increasing_subsequence6(a: tuple) -> tuple:
if not a: #a是空元组
return ()
max_len, L, left = 1, 0, 0
for i in range(1, len(a)):
if a[i] <= a[i-1]:
if i - L > max_len:
max_len, left = i - L, L
L = i
#考虑最后一个元素属于最长连续递增子序列的情形
if len(a) - L > max_len:
max_len, left = len(a) - L, L
return tuple(a[left:left+max_len])
代码7:
#根据left和right来确定子序列
def increasing_subsequence7(a: tuple) -> tuple:
if not a: #a是空元组
return ()
L, left, right = 0, 0, 0
for i in range(1, len(a)):
if a[i] <= a[i-1]:
if i - L > right - left:
left, right = L, i - 1
L = i
#考虑最后一个元素属于最长连续递增子序列的情形
if len(a) - 1 - L > right - left:
left, right = L, len(a) - 1
return tuple(a[left:right+1])
代码8:
#根据max_len和left来确定子序列
def increasing_subsequence8(a: tuple) -> tuple:
if not a: #a是空元组
return ()
max_len, L, left = 1, 0, 0
for i in range(1, len(a)):
if a[i] > a[i-1]:
if i - L >= max_len:
max_len, left = i - L + 1, L
else:
L = i
return tuple(a[left:left+max_len])
代码9:
#根据left和right来确定子序列
def increasing_subsequence9(a: tuple) -> tuple:
if not a: #a是空元组
return ()
L, left, right= 0, 0, 0
for i in range(1, len(a)):
if a[i]> a[i-1]:
if i -L > right - left:
left, right = L, i
else:
L = i
return tuple(a[left:right+1])