剑指offer | 5题

题1:重建二叉树

题目:输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。

思路:

  • 重建二叉树返回的结果是依次打印出二叉树的根节点,肯定要用到递归的思想
  • 先根据前序list找到根节点以及其索引
  • 然后在前序和中序list中定位到新的子树的前序和中序list
  • 循环终止的条件就是看前序的list是否为空,是否元素为1!

代码:

# -*- coding:utf-8 -*-
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    # 返回构造的TreeNode根节点
    def reConstructBinaryTree(self, pre, tin):
        # 首先根据前序第一个元素作为根节点!
        # 然后根据这个元素将中序的序列变为两部分!
        if len(pre)==0:
            return None
        elif len(pre)==1: # 如果只有1个元素的时候 tin为空 所以取不到索引 单独拿出来!
            return TreeNode(pre[0]) 
        else:
            root=TreeNode(pre[0])
            val=tin.index(pre[0])
            root.left=self.reConstructBinaryTree(pre[1:val+1],tin[:val])
            root.right=self.reConstructBinaryTree(pre[val+1:],tin[val+1:])
        return root

在这里插入图片描述
但是上述还是有点绕,我们来进行拆解:

  • 第一步返回1
def reConstructBinaryTree(self, pre, tin):
        '''
        传入:
        pre = [1,2,4,7,3,5,6,8]
        tin = [4,7,2,1,5,3,8,6]
        '''
        # pre表示前序序列 tin表示中序序列
        # 首先根据前序第一个元素作为根节点!
        # 然后根据这个元素将中序的序列变为两部分!
        if len(pre)==0:
            return None
        elif len(pre)==1: # 如果只有1个元素的时候 tin为空 所以取不到索引 单独拿出来!
            return TreeNode(pre[0])
        else:
            root=TreeNode(pre[0]) # 根节点为前序第一个元素 root = 1
            val=tin.index(pre[0]) # 返回根节点元素对应的索引 val = 3 

            root.left=self.reConstructBinaryTree(pre[1:val+1],tin[:val]) # 这时候树的左子节点为中序中索引的前半部分 
            root.right=self.reConstructBinaryTree(pre[val+1:],tin[val+1:])
            '''
            root.left=self.reConstructBinaryTree(pre=[2,4,7],tin=[4,7,2])
            root.right=self.reConstructBinaryTree(pre=[3,5,6,8],tin=[5,3,8,6])
            '''
        return root  # 返回1

  • 第二步返回2
def reConstructBinaryTree(self, pre, tin):
        '''
        传入:
        pre = [2,4,7]
        tin = [4,7,2]
        '''
        # pre表示前序序列 tin表示中序序列
        # 首先根据前序第一个元素作为根节点!
        # 然后根据这个元素将中序的序列变为两部分!
        if len(pre)==0:
            return None
        elif len(pre)==1: # 如果只有1个元素的时候 tin为空 所以取不到索引 单独拿出来!
            return TreeNode(pre[0])
        else:
            root=TreeNode(pre[0]) # 根节点为前序第一个元素 root = 2
            val=tin.index(pre[0]) # 返回根节点元素对应的索引 val = 2 

            root.left=self.reConstructBinaryTree(pre[1:val+1],tin[:val]) # 这时候树的左子节点为中序中索引的前半部分 
            root.right=self.reConstructBinaryTree(pre[val+1:],tin[val+1:])
            '''
            root.left=self.reConstructBinaryTree(pre=[4,7],tin=[4,7])


            root.right=self.reConstructBinaryTree(pre=[3,5,6,8],tin=[5,3,8,6])
            '''
        return root # 返回2

  • 第三步返回4
def reConstructBinaryTree(self, pre, tin):
        '''
        传入:
        pre = [4,7]
        tin = [4,7]
        '''
        # pre表示前序序列 tin表示中序序列
        # 首先根据前序第一个元素作为根节点!
        # 然后根据这个元素将中序的序列变为两部分!
        if len(pre)==0:
            return None
        elif len(pre)==1: # 如果只有1个元素的时候 tin为空 所以取不到索引 单独拿出来!
            return TreeNode(pre[0])
        else:
            root=TreeNode(pre[0]) # 根节点为前序第一个元素 root = 4
            val=tin.index(pre[0]) # 返回根节点元素对应的索引 val = 0 

            root.left=self.reConstructBinaryTree(pre[1:val+1],tin[:val]) # 这时候树的左子节点为中序中索引的前半部分 
            root.right=self.reConstructBinaryTree(pre[val+1:],tin[val+1:])
            '''
            root.left=self.reConstructBinaryTree(pre=[7],tin=[])

            root.right=self.reConstructBinaryTree(pre=[3,5,6,8],tin=[5,3,8,6])
            '''
        return root # 返回4

  • 第四步返回7

def reConstructBinaryTree(self, pre, tin):
        '''
        传入:
        pre = [7]
        tin = []
        '''
        # pre表示前序序列 tin表示中序序列
        # 首先根据前序第一个元素作为根节点!
        # 然后根据这个元素将中序的序列变为两部分!
        if len(pre)==0:
            return None
        elif len(pre)==1: # 如果只有1个元素的时候 tin为空 所以取不到索引 单独拿出来!
            return TreeNode(pre[0]) # 返回7了 
        else:
            root=TreeNode(pre[0]) # 根节点为前序第一个元素 root = 4
            val=tin.index(pre[0]) # 返回根节点元素对应的索引 val = 0 

            root.left=self.reConstructBinaryTree(pre[1:val+1],tin[:val]) # 这时候树的左子节点为中序中索引的前半部分 
            root.right=self.reConstructBinaryTree(pre[val+1:],tin[val+1:])
            '''
            root.left=self.reConstructBinaryTree(pre=[7],tin=[])

            root.right=self.reConstructBinaryTree(pre=[3,5,6,8],tin=[5,3,8,6])
            '''
        return root 

后续同理,就不一一展示。

题2:旋转数组的最小数字

把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。 输入一个非减排序的数组的一个旋转,输出旋转数组的最小元素。 例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。 NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。

代码1:直接min函数开挂…

# -*- coding:utf-8 -*-
class Solution:
    def minNumberInRotateArray(self, rotateArray):
        # write code here
        if len(rotateArray) == 0:
            return 0
        else:
            return min(rotateArray)

在这里插入图片描述
代码2:采用二分查找的思路!

但下述代码在牛客上是通过不了,LeetCode是可以的。。。说时间超出了!

# -*- coding:utf-8 -*-
class Solution:
    def minNumberInRotateArray(self, rotateArray):
        # write code here
        # 采用二分查找
        l, r = 0, len(rotateArray)-1 # 设两个指针
        while l <= r:
            mid = (l + r) // 2
            if rotateArray[l] < mid:
                # 如果左指针小于中值 则移动左指针
                l = mid
            elif rotateArray[r] > mid:
                # 如果右指针大于中值 则移动右指针
                r = mid
            else:
                return rotateArray[r]

在这里插入图片描述
思路:运用图形的方式 大概分为以下几步:
在这里插入图片描述
在这里插入图片描述

题3 求1+2+3+…+n

求1+2+3+…+n,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。

代码1:直接使用sum + range

# -*- coding:utf-8 -*-
class Solution:
    def Sum_Solution(self, n):
        # write code here
        return sum(range(1,n+1))

代码2:终止递归采用逻辑与的短路特性

# -*- coding:utf-8 -*-
class Solution:
    def Sum_Solution(self, n):
        # write code here
        return n and n + self.Sum_Solution(n-1)

题4:跳台阶

一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)。

思路:

  • 其实就是求多少个1和多少个2求和加起来等于n,顺序一模一样才算相同的跳法,否则两次。
  • 递归?怎么递归?
  • 很朴素的想法,设一个list=[1,2] 然后每次从里面随机选一个数,一直加起来等于n 但是问题是这只是一种跳法,另外什么时候循环终止呢?都是问题!
  • 简单试验了一下:
    • 1级台阶:1种跳法
    • 2级台阶:2种跳法
    • 3级台阶:3种跳法
    • 4级台阶:5种跳法
    • 5级台阶:8种跳法
    • 咦,貌似发现了规律,就是符合斐波那契数列!那为什么是这样呢?先不管!首先写代码!
# -*- coding:utf-8 -*-
class Solution:
    def jumpFloor(self, number):
        # write code here
        # 注意边界条件
        if number <= 0:
        	return 0
        	
        alist = [1,2]
        if number == 1:
            return 1
        elif number == 2:
            return 2
        else:
            for i in range(2, number):
                alist.append(alist[i-1] + alist[i-2])
            return alist[number-1]

那为什么会是斐波那契数列呢?

  • 首先我们考虑最简单的情况。如果只有1级台阶,那么显然只一种跳法。如果有2级台阶,那就有两种跳法:一种是分两次跳,每次跳1级;另一种是一次跳2级。这个没问题 上面就是这么做的!
  • 接着,我们来讨论一般情况。我们把n级台阶时的跳法看成是n的函数,记为f(n)。当n>2时,第一次跳的时候就有两种不同的选择:
    • 一是第一次只跳1级,此时跳法数目等于后面剩下的n-1级台阶的跳法数目,即为f(n-1);
    • 另外一种选择是跳一次跳2级,此时跳法数目等于后面剩下的n-2级台阶的跳法数目,即为f(n-2)。
    • 因此n级台阶的不同跳法的总数f(n)=f(n-1)+f(n-2)。分析到这里,我们不难看出这实际上就是斐波那契数列了。
  • 牛逼的思路!

题5:变态跳台阶

一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。

思路:

  • 首先也是小样本测试例子,在上面基础上再补充就好:
    • 1级台阶:1种跳法
    • 2级台阶:2种跳法
    • 3级台阶:3+1=4种跳法
    • 4级台阶:5+3=8种跳法
    • 5级台阶:8+8=16种跳法
    • 咦,发现了规律是2的平方?为什么呢?先写代码!
# -*- coding:utf-8 -*-
class Solution:
    def jumpFloorII(self, number):
        # write code here
        import math
        # 注意边界条件
        if number <= 0:
        	return 0
        
        return math.pow(2,number-1)

至于为什么,直接数据归纳法?牛逼。。。

题6:矩形覆盖问题

我们可以用2×1的小矩形横着或者竖着去覆盖更大的矩形。请问用n个2×1的小矩形无重叠地覆盖一个2×n的大矩形,总共有多少种方法?

  • 本质还是斐波那契数列
  • 小样本测试来看。
    • n=1,1竖 = 1
    • n=2,2竖 + 2横 = 2
    • n = 3,3竖 + 2横1竖 + 1竖2横 = 3
    • n = 4,4竖 + 2横2竖 + 2竖2横 + 1竖2横1竖 + 4横 = 5
    • 符合斐波那契!
# -*- coding:utf-8 -*-
class Solution:
    def jumpFloor(self, number):
        # write code here
        # 注意边界条件
        if number <= 0:
        	return 0
        	
        alist = [1,2]
        if number == 1:
            return 1
        elif number == 2:
            return 2
        else:
            for i in range(2, number):
                alist.append(alist[i-1] + alist[i-2])
            return alist[number-1]
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值