《剑指offer》第21-30题

《剑指offer》第21-30题

  • 21 栈的压入、弹出序列
  • 22 从上往下打印二叉树
  • 23 二叉搜索树的后序遍历序列
  • 24 二叉树中和为某一值的路径
  • 25 复杂链表的复制
  • 26 二叉搜索树与双向链表
  • 27 字符串的排列
  • 28 数组中出现超过一半的数字
  • 29 最小的k个数
  • 30 连续子数组的最大和

21 栈的压入、弹出序列

  此题个人解析在另一篇博客,点击跳转栈的压入、弹出序列

22 从上往下打印二叉树

题目描述:
  从上往下打印出二叉树的每个节点,同层节点从左至右打印。
解析及代码

'''
方法一:借助辅助队列
层序遍历
'''
class TreeNode:
    def __init__(self, x):
        self.val = x
        self.left = None
        self.right = None
class Solution1:
    # 返回从上到下每个节点值列表,例:[1,2,3]
    def PrintFromTopToBottom(self, root:TreeNode):
        # write code here
        if not root:
            return []
        queue = [root]
        res = []
        while queue:
            node = queue.pop(0)
            res.append(node.val)
            if node.left != None:
                queue.append(node.left)
            if node.right != None:
                queue.append(node.right)
        return res

23 二叉搜索树的后序遍历序列

题目描述:
  输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出Yes,否则输出No。假设输入的数组的任意两个数字都互不相同。
解析及代码:

'''
后序遍历: 找到根节点 --> 最后一个数字
二叉搜索树: 左子树元素都比root小,右子树元素都比root大
最后递归判断即可
'''
class Solution1:
    def VerifySquenceOfBST(self, sequence):
        # write code here
        if not sequence:
            return False
        return self.CheckVerifySquenceOfBST(sequence)


    def CheckVerifySquenceOfBST(self, sequence):
        #递归终止条件:sequence为空
        if not sequence:
            return True

        root = sequence[-1]
        for i in range(len(sequence)):
            if sequence[i] > root:
                break

        for right in sequence[i:-1]:
            if right < root:
                return False

        return self.CheckVerifySquenceOfBST(sequence[:i]) and self.CheckVerifySquenceOfBST(sequence[i:-1])

24 二叉树中和为某一值的路径

题目描述:
  输入一颗二叉树的根节点和一个整数,按字典序打印出二叉树中结点值的和为输入整数的所有路径。
路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。
解析及代码:

'''
方法一:递归法
DFS
'''
class Solution1:
    # 返回二维列表,内部每个列表表示找到的路径
    def FindPath(self, root, expectNumber):
        # write code here
        if not root:
            return []
        stack = [root]  #存储路径节点
        path = []  #存储路径
        sums = root.val 
        result = self.DFS(root, expectNumber, stack, sums, path)
        return result

    def DFS(self, root, expectNumber, stack, sums, path):
        # 递归终止条件:递归至叶节点 或 只有根节点的情况
        if not root.left and not root.right:
            if sums == expectNumber:
                res = [x.val for x in stack] 
                path.append(res)
                return path
            else:
                return path
        else:  # VLR
            if root.left != None: 
                stack.append(root.left)
                sums += root.left.val
                self.DFS(root.left, expectNumber, stack, sums, path)
                stack.pop() 
                sums -= root.left.val

            if root.right != None: 
                stack.append(root.right)
                sums += root.right.val
                self.DFS(root.right, expectNumber, stack, sums, path)
                stack.pop() 
                sums -= root.right.val

        return sorted(path, key=lambda x: -len(x)) 
'''
方法二:非递归法
BFS
refer:https://www.nowcoder.com/questionTerminal/b736e784e3e34731af99065031301bca?f=discussion
'''
from collections import deque
class Solution2:
    # 返回二维列表,内部每个列表表示找到的路径
    def FindPath(self, root, expectNumber):
        # write code here
        if not root:
            return []
        result = []
        queue = deque([root])
        result.append([root.val])
        while queue:
            length = len(queue)
            for i in range(length):
                r = queue.popleft()
                lis = result.pop(0)
                if r.left:
                    queue.append(r.left)
                    result.append(lis + [r.left.val])
                if r.right:
                    result.append(lis + [r.right.val])
                    queue.append(r.right)
                if not r.left and not r.right:
                    result.append(lis)
        for i in range(len(result))[::-1]:
            if sum(result[i]) != expectNumber:
                result.pop(i)
        result = sorted(result, key=lambda x: len(x), reverse=True)
        return result

25 复杂链表的复制

题目描述:
  输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,
另一个特殊指针random指向一个随机节点),请对此链表进行深拷贝,并返回拷贝后的头结点。
(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)
解析及代码:

'''
方法一:哈希表法
字典保存位置映射关系
时间和空间复杂度均为:O(n)

'''
class Solution1:
    # 返回 RandomListNode
    def Clone(self, pHead):
        # write code here
        if not pHead:
            return None

        d = {}  # 存储 赋值前--赋值后节点
        nextNode = pHead.next
        pH = RandomListNode(pHead.label)
        d[pHead] = pH
        head = pH

        while nextNode:  # 顺序复制
            pH.next = RandomListNode(nextNode.label)
            pH = pH.next
            d[nextNode] = pH  # 存储对应关系
            nextNode = nextNode.next

        while pHead:  # ramdom
            if pHead.random:
                d[pHead].random = d[pHead.random]
            pHead = pHead.next

        return head


'''
方法二:复制再拆分
略
'''
class Solution2:
    # 返回 RandomListNode
    def Clone(self, pHead):
        # write code here
        if not pHead:
            return None

'''
方法三:双指针
一个遍历、一个构造
'''
class Solution3:
    # 返回 RandomListNode
    def Clone(self, pHead):
        # write code here
        if not pHead:
            return None
        pH = RandomListNode(pHead.label)
        pH.random = RandomListNode(pHead.random.label) if pHead.random else None
        nextNode = pHead.next
        head = pH
        while nextNode:
            head.next = RandomListNode(nextNode.label)
            head.next.random = RandomListNode(nextNode.random.label) if nextNode.random else None
            head = head.next
            nextNode = nextNode.next
        return pH

26 二叉搜索树与双向链表

题目描述:
  输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。
要求不能创建任何新的结点,只能调整树中结点指针的指向。
解析及代码:

'''
方法一: LVR递归
二叉搜索树 ==》 双向链表
 ||            ||
左右节点       向前向后指针
            
'''
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def Convert(self, pRootOfTree):
        # write code here
        if not pRootOfTree:
            return None

        return self.node2linkList(pRootOfTree)

    def node2linkList(self, pRoot):
        if pRoot is None:
            return None
        if not pRoot.left and not pRoot.right:
            return pRoot

        self.node2linkList(pRoot.left)
        left = pRoot.left
        if left != None:
            while left.right:
                left = left.right
            pRoot.left = left
            left.right = pRoot

        self.node2linkList(pRoot.right)
        right = pRoot.right
        if right != None:
            while right.left:
                right = right.left
            pRoot.right = right
            right.left = pRoot

        while pRoot.left:
            pRoot = pRoot.left

        return pRoot

27 字符串的排列

题目描述:
  输入一个字符串,长度不超过9(可能有字符重复),字符只包括大小写字母。
解析及代码:

'''
方法一: 递归法
当前字母 + 当前字母以外字母构成的全排
a,b,c
  - b -c
a
  - c - b
  - a - c 
d
  - c - a
  - a - b
c
  - b - a
'''
class Solution1:
    def Permutation(self, ss):
        # write code here
        if not ss:
            return []
        if len(ss) == 1:#判断条件 递归终止条件
            return ss
        res = []
        for i in range(len(ss)):#循环取元素
            for j in self.Permutation(ss[:i]+ss[i+1:]): #递归取剩余元素
                res.append(ss[i]+j)
        return sorted(list(set(res)))

28 数组中出现超过一半的数字

题目描述:
  数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。
解析及代码:

'''
方法一:内置函数count
O(n)
'''
class Solution1:
    def MoreThanHalfNum_Solution(self, numbers):
        # write code here
        for x in numbers:
            if numbers.count(x) > (len(numbers)/2):
                return x
        return 0
        #强行一行
        # return [x for x in numbers if numbers.count(x) > (len(numbers)/2)][0] if [x for x in numbers if numbers.count(x) > (len(numbers)/2)] else 0

'''
方法二:哈希:字典统计出现次数
O(2n)
'''
class Solution2:
    def MoreThanHalfNum_Solution(self, numbers):
        # write code here
        dict = {} #存储{"元素":"次数"}
        cur = 0
        halfLen = len(numbers) / 2
        while cur < len(numbers):
            try:
                dict[numbers[cur]] += 1
            except KeyError:
                dict[numbers[cur]] = 1
            finally:
                cur += 1
        for key in dict:
            if dict[key] > halfLen :#其实这步可以放到以上的遍历循环中
                return key
        return 0
'''
方法三:排序法
先排序(顺便复习排序算法)。也可直接sort()...
超过了一半,中间的数一定 就是需要的数,统计个数
'''
class Solution3:
    def MoreThanHalfNum_Solution(self, numbers):
        # write code here
        numbers.sort()
        if not numbers:#空返回0
            return 0
        if len(numbers) < 2:#长度1,返回唯一元素
            return numbers[0]

        halfLen =  len(numbers)//2+1 #中间位置 9对应5 10对应6(其实可5,可6)
        for i in range(halfLen):#遍历0-中间位置
            if numbers[i:halfLen+i] == [numbers[i]] * (halfLen):
                return numbers[i]
        return 0


'''
方法四:摩尔投票法 leetcode第169题
这个方法很骚,一开始没想到,看了题解,分享给大家

遍历数组过程中保存两个值:
一个是数组中某一数字,另一个是次数。
遍历到下一个数字时,若与保存数字相同,则次数加1,反之减1。若次数=0,则保存下一个数字,次数重新设置为1。
由于要找的数字出现的次数比其他数字之和还多,那么要找的数字肯定是最后一次把次数设置为1的数字。
最后再判断一下这个数是不是出现超过 len(numbers)//2
'''
class Solution4:
    def MoreThanHalfNum_Solution(self, numbers):
        # write code here
        count = 1 #保存次数
        num = numbers[0] #保存数值
        for i in range(1,len(numbers)):
            if count == 0:#次数为0,保存新数字
                num  = numbers[i]
            if num == numbers[i]:#相等,次数+1
                count += 1
            else: #不同,次数减1
                count -= 1

        #计算获取数的个数 大于 len(numbers)//2返回该数,否则返回0
        count = 0#统计个数
        for i in numbers:
            if i == num:
                count += 1
        return num if count > len(numbers)//2 else 0

29 最小的k个数

题目描述:
  输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,。
解析及代码:

'''
总体思路都是排序后,取前n个(从小到大排),或者后n个(从大到小排)
方法一: sorted() + 切片...
'''
class Solution1:
    def GetLeastNumbers_Solution(self, tinput, k):
        # write code here
        return sorted(tinput)[:k] if len(tinput) >= k else []
'''
其他方法:参考专栏十大排序
'''
class Solution2:
    def GetLeastNumbers_Solution(self, tinput, k):
        # write code here
        pass

30 连续子数组的最大和

题目描述:
  HZ偶尔会拿些专业问题来忽悠那些非计算机专业的同学。今天测试组开完会后,他又发话了:在古老
的一维模式识别中,常常需要计算连续子向量的最大和,当向量全为正数的时候,问题很好解决。但是,
如果向量中包含负数,是否应该包含某个负数,并期望旁边的正数会弥补它呢?例如:{6,-3,-2,7,-15,1,2,2},
连续子向量的最大和为8(从第0个开始,到第3个为止)。给一个数组,返回它的最大连续子序列的和,
你会不会被他忽悠住?(子向量的长度至少是1)
解析及代码:

'''
方法一:DP
desc :https://www.nowcoder.com/questionTerminal/459bd355da1549fa8a49e350bf3df484?f=discussion

dp[i]表示以元素array[i]结尾的最大连续子数组和.
以[6,-3,-2,7,-15,1,2,2]为例
可以发现,
dp[0] = 6
dp[1] = 3
dp[2] = 1
dp[3] = 7
以此类推,会发现
dp[i] = max{dp[i-1]+array[i],array[i]}.
'''
class Solution1:
    def FindGreatestSumOfSubArray(self, array):
        # write code here
        if not array:
            return None
        if len(array) < 2:
            return array[0]

        n = len(array)
        dp = [i for i in array]
        print(dp)
        for i in range(1, n):
            print(max(dp[i - 1] + array[i], array[i]),dp[i - 1] + array[i], array[i])
            dp[i] = max(dp[i - 1] + array[i], array[i])

        return max(dp)



'''
方法二:暴力法
O(n^2)
dictSum = {"sum":[起始下标,结束下标]}
max(dictSum)获取相应的下标
数组短小可行,LeetCode上给了很长数组就直接超时了
'''
class Solution2:
    def FindGreatestSumOfSubArray(self, array):
        # write code here
        if not array:
            return None
        if len(array) < 2:
            return array[0]

        dictSum = {} #定义字典存储和 与 相应下标
        for i in range(len(array)):#range包头不包尾
            for j in range(i+1,len(array)+1):#因为切片包头不包尾,其实j作为尾部可以无限大
                try:
                    dictSum[sum(array[i:j])] = [i,j]
                except KeyError:
                    dictSum[sum(array[i:j])] =[i,j]
                # print(dictSum)
        index = dictSum[max(dictSum)]
        return sum(array[index[0]:index[1]])

你可能感兴趣的:(数据结构与算法,剑指offer第21-30题)