力扣算法:有效括号、合并两个有序链表

力扣算法:有效括号、合并两个有序链表

  • 一、有效括号
    • 1、问题
    • 2、提示
    • 3、思路
    • 4、代码
    • 5、时间与空间复杂度
  • 二、合并两个有序链表
    • 1、问题
    • 2、提示
    • 3、思路
    • 4、代码
    • 5、时间与空间复杂度
  • 备注

一、有效括号

1、问题

给定一个只包括 ‘(’,’)’,’{’,’}’,’[’,’]’ 的字符串 s,判断字符串是否有效。有效字符串需满足:

  • 左括号必须用相同类型的右括号闭合。
  • 左括号必须以正确的顺序闭合。

示例1:
输入:s = “()”
输出:true

示例2:
输入:s = “()[]{}”
输出:true

示例 3:
输入:s = “(]”
输出:false

示例 4:
输入:s = “([)]”
输出:false

示例 5:
输入:s = “{[]}”
输出:true

2、提示

  • 1 <= s.length <= 104
  • s 仅由括号 ‘()[]{}’ 组成

3、思路

当开始接触题目时,我们会不禁想到如果计算出左括号的数量,和右括号的数量,如果每种括号左右数量相同,会不会就是有效的括号了呢?

事实上不是的,假如输入是 [{]},每种括号的左右数量分别相等,但不是有效的括号。这是因为结果还与括号的位置有关。

仔细分析我们发现,对于有效的括号,它的部分子表达式仍然是有效的括号,比如 {()[()]} 是一个有效的括号,()[{}] 是有效的括号,[()] 也是有效的括号。并且当我们每次删除一个最小的括号对时,我们会逐渐将括号删除完。比如:
力扣算法:有效括号、合并两个有序链表_第1张图片这个思考的过程其实就是栈的实现过程。因此我们考虑使用栈,当遇到匹配的最小括号对时,我们将这对括号从栈中删除(即出栈),如果最后栈为空,那么它是有效的括号,反之不是。
力扣算法:有效括号、合并两个有序链表_第2张图片

4、代码

class Solution:
    def isValid(self, s: str) -> bool:
        dic = {')': '(', ']': '[', '}': '{'}
        stack = []
        for i in s:
            if stack and i in dic:
                if stack[-1] == dic[i]:
                    stack.pop()
                else:
                    return False
            else:
                stack.append(i)
        return not stack

5、时间与空间复杂度

时间复杂度:O(N)。遍历了一遍字符串。
空间复杂度:O(N)。最坏情况下,假如输入是 (((((((,栈的大小将是输入字符串的长度。

二、合并两个有序链表

1、问题

将两个升序链表合并为一个新的升序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

示例1:
力扣算法:有效括号、合并两个有序链表_第3张图片输入:l1 = [1,2,4], l2 = [1,3,4]
输出:[1,1,2,3,4,4]

示例2:
输入:l1 = [], l2 = []
输出:[]

示例 3:
输入:l1 = [], l2 = [0]
输出:[0]

2、提示

  • 两个链表的节点数目范围是 [0, 50]
  • -100 <= Node.val <= 100
  • l1 和 l2 均按 非递减顺序 排列

3、思路

  • 终止条件:当两个链表都为空时,表示我们对链表已合并完成。
  • 如何递归:我们判断 l1 和 l2 头结点哪个更小,然后较小结点的 next 指针指向其余结点的合并结果。(调用递归)
    力扣算法:有效括号、合并两个有序链表_第4张图片

4、代码

class ListNode:
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next

class Solution:
    def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode:
        if not l1:
            return l2  # 终止条件,直到两个链表都空
        elif not l2:
            return l1
        elif l1.val < l2.val: # 递归调用
            l1.next = self.mergeTwoLists(l1.next, l2)
            return l1
        else:
            l2.next = self.mergeTwoLists(l1, l2.next)
            return l2


if __name__== "__main__":
    # 创建链表
    def build_link(nums):
        li = cur = ListNode(None)
        for i in nums:
            cur.next = ListNode(i)
            cur = cur.next
        return li.next

    nums1 = [1, 2, 4]
    nums2 = [1, 3, 4]

    l1 = build_link(nums1)
    l2 = build_link(nums2)

    # 合并两个有序链表
    test = Solution()
    res = test.mergeTwoLists(l1, l2)
    # 打印
    while res:
        print(res.val, end=' ')
        res = res.next
    print()

5、时间与空间复杂度

时间复杂度
m,n为 l1 和 l2 的元素个数。递归函数每次去掉一个元素,直到两个链表都为空,因此需要调用 R=O(m+n) 次。而在递归函数中我们只进行了 next 指针的赋值操作,复杂度为 O(1),故递归的总时间复杂度为 O(T)=R∗O(1)=O(m+n)。

空间复杂度
对于递归调用 self.mergeTwoLists(),当它遇到终止条件准备回溯时,已经递归调用了 m+n次,使用了 m+n个栈帧,故最后的空间复杂度为 O(m+n)。

点击查看“力扣”给出的:递归“时间复杂度”与“空间复杂度”计算方法。

备注

1、“问题”与“提示”转载于:
力扣(LeetCode)
https://leetcode-cn.com/problems/valid-parentheses
2、解题转载于:
z1m
https://leetcode-cn.com/problems/valid-parentheses/solution/zhu-bu-fen-xi-tu-jie-zhan-zhan-shi-zui-biao-zhun-d/

你可能感兴趣的:(力扣算法,leetcode,算法,python)