Loading

202008leetcode刷题记录

19. 删除链表的倒数第N个节点

题目要求:
给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点。

思路:
添加一个头结点,以便删除第一个结点。让一个指针先走 n 下,然后两个指针一起走直到后面的指针走到最后一个结点。这时候前面那个指针就指向要删除的结点的前一个结点,就可以删了。

class Solution:
    def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode:
        L = ListNode(0)
        if not head.next:
            return head.next
        L.next = head 
        pre, p = L, L
        while n > 0:
            p = p.next
            n -= 1
        while p.next:
            pre = pre.next
            p = p.next
        pre.next = pre.next.next
        return L.next

17. 电话号码的字母组合

题目要求:
给定一个仅包含数字2-9的字符串,返回所有它能表示的字母组合。给出数字到字母的映射如下(与电话按键相同)。注意1不对应任何字母。

思路:
其实就是返回每个数字对应字母的笛卡尔积,python内置的product就可以实现。

class Solution:
    def letterCombinations(self, digits: str) -> List[str]:
        if not digits:
            return []
        
        hashmap = {
            "2": "abc",
            "3": "def",
            "4": "ghi",
            "5": "jkl",
            "6": "mno",
            "7": "pqrs",
            "8": "tuv",
            "9": "wxyz",
        }

        combinations = [hashmap[digit] for digit in digits]
        return ["".join(_) for _ in itertools.product(*combinations)]

20. 有效的括号

题目要求:
给定一个只包括'(',')','{','}','[',']'的字符串,判断字符串是否有效。

有效字符串需满足:

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

注意空字符串可被认为是有效字符串。

思路:
栈。遇到左括号就进栈,遇到右括号就出栈并判断是否可以匹配。注意栈不空才能出栈。

class Solution:
    def isValid(self, s: str) -> bool:
        stack = []
        hashmap = {'(': ')', '[': ']', '{': '}'}
        for ch in s:
            if ch in hashmap:
            # 遇到左括号,进栈
                stack.append(ch)
            elif not stack:
            # 遇到右括号,如果栈空,说明不匹配
                return False
            elif hashmap[stack.pop()] != ch:
            # 遇到右括号且栈不空,但是左右括号不是一个类型
                return False
        
        return not stack

43. 字符串相乘

题目要求:
给定两个以字符串形式表示的非负整数num1num2,返回num1num2的乘积,它们的乘积也表示为字符串形式。

思路:
模拟法。模拟我们平时用的乘法,就是从个位数开始相乘,最后相加(当然还有进位)。加法的实现可以直接照搬415. 字符串相加的实现。

class Solution(object):
    def multiply(self, num1, num2):
        if num1 == "0" or num2 == "0":
            return "0"
        
        l1, l2 = len(num1), len(num2) 
        if l1 < l2: 
            num1, num2 = num2, num1
            l1, l2 = l2, l1
            
        num2 = num2[::-1]
        ans = "0"
        for i, digit in enumerate(num2):
            tmp = self.StringMultiplyDigit(num1, int(digit)) + "0" * i
            ans = self.addStrings(ans, tmp) #计算res和tmp的和

        return ans
    
    def StringMultiplyDigit(self,string, n):
        s = string[::-1]
        res = []
        for i, char in enumerate(s):
            num = int(char)
            res.append(num * n)
        res = self.CarrySolver(res)
        res = res[::-1]
        return "".join(str(x) for x in res)
        
    def CarrySolver(self, nums):
        i = 0
        while i < len(nums):
            if nums[i] >= 10:
                carrier = nums[i] // 10
                if i == len(nums) - 1:
                    nums.append(carrier)
                else:
                    nums[i + 1] += carrier
                nums[i] %= 10
            i += 1
                    
        return nums
    
    def addStrings(self, num1: str, num2: str) -> str:
        ans = ''
        i, j, carry = len(num1) - 1, len(num2) - 1, 0
        while i > -1 or j > -1:
            a = int(num1[i]) if i > -1 else 0
            b = int(num2[j]) if j > -1 else 0
            res = a + b + carry
            carry = res // 10
            ans = str(res % 10) + ans
            i, j = i - 1, j - 1
        return "1" + ans if carry else ans

80. 删除排序数组中的重复项 II

题目要求:
给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素最多出现两次,返回移除后数组的新长度。不要使用额外的数组空间,你必须在原地修改输入数组并在使用O(1)额外空间的条件下完成。

思路:
双指针。题目里面的最多出现两次,意思就是如果出现了三次,就得删到只剩两次,而不是一次,有点坑。一个指针走得快,一个走得慢。快指针一边走一边对重复元素进行计数,如果重复不超过两次,就放到慢指针那里,超过两次,快指针就接着走。

class Solution:
    def removeDuplicates(self, nums: List[int]) -> int:
        n = len(nums)
        left, right = 1, 1
        cnt = 1
        while right < n:
            if nums[right] == nums[right - 1]:
                cnt += 1
            else:
                cnt = 1
            if cnt < 3:
                nums[left] = nums[right]
                left += 1
            right += 1
               
        return left

88. 合并两个有序数组

题目要求:
给你两个有序整数数组 nums1 和 nums2,请你将 nums2 合并到 nums1 中,使 nums1 成为一个有序数组。

说明:

  • 初始化 nums1 和 nums2 的元素数量分别为 m 和 n 。
  • 你可以假设 nums1 有足够的空间(空间大小大于或等于 m + n)来保存 nums2 中的元素。

思路:
双指针。从前向后遍历,而nums1数组的后面都是0,此时要么将nums2的元素插入进来(就得把nums1中的元素向后移动,代价很大),要么将nums1中的元素与后面的0交换(会打乱顺序,反而使数组变得无序)。于是从前往后遍历只能是牺牲空间,再开辟一个数组用来存储结果。

class Solution(object):
    def merge(self, nums1, m, nums2, n):
        nums1_copy = nums1[:m] 
        nums1[:] = []
        p1, p2 = 0, 0
        while p1 < m and p2 < n: 
            if nums1_copy[p1] < nums2[p2]: 
                nums1.append(nums1_copy[p1])
                p1 += 1
            else:
                nums1.append(nums2[p2])
                p2 += 1

        if p1 < m: 
            nums1[p1 + p2:] = nums1_copy[p1:]
        if p2 < n:
            nums1[p1 + p2:] = nums2[p2:]

如果想要不牺牲空间和速度,可以从后往前遍历。由于nums1之后的0的个数是可以把nums2中的元素全部放入其中的,因此从后往前遍历不会涉及到元素的移动或者交换。

class Solution:
    def merge(self, nums1: List[int], m: int, nums2: List[int], n: int) -> None:
        """
        Do not return anything, modify nums1 in-place instead.
        """
        p1 = m - 1
        p2 = n - 1
        p = len(nums1) - 1 # 用来插入的位置
        while p1 >= 0 and p2 >= 0:
            if nums1[p1] < nums2[p2]:
                nums1[p] = nums2[p2]
                p2 -= 1
            else:
                nums1[p] =  nums1[p1]
                p1 -= 1
            p -= 1
        if p2 > -1:
            nums1[:p2 + 1] = nums2[:p2 + 1]

100. 相同的树

题目要求:
给定两个二叉树,编写一个函数来检验它们是否相同。

如果两个树在结构上相同,并且节点具有相同的值,则认为它们是相同的。

思路:
递归先序遍历。

class Solution:
    def isSameTree(self, p: TreeNode, q: TreeNode) -> bool:
        if not p and not q:
            return True
        elif (not p and q) or (p and not q):
            return False
        elif p.val != q.val:
            return False
        else:
            return self.isSameTree(p.left, q.left) and self.isSameTree(p.right, q.right)

206. 反转链表

题目要求:
反转一个单链表。

思路:
加一个头结点,辅助链表的反转。

class Solution:
    def reverseList(self, head: ListNode) -> ListNode:
        if not head or not head.next:
            return head
        L = ListNode(0)
        L.next = head
        p = head.next
        while p:
            q = L.next
            L.next = p
            head.next = p.next
            p.next = q
            p = head.next
        
        return L.next

递归。当只剩一个结点时。就不需要反转了,此时结束递归。

class Solution:
    def reverseList(self, head: ListNode) -> ListNode:
        if not head or not head.next:
            return head
        
        newHead = self.reverseList(head.next)
        head.next.next = head
        head.next = None
        return newHead

225. 用队列实现栈

题目要求:
使用队列实现栈的下列操作:

  • push(x) -- 元素 x 入栈
  • pop() -- 移除栈顶元素
  • top() -- 获取栈顶元素
  • empty() -- 返回栈是否为空

思路:
用一个队列来模拟栈的操作。入栈就直接入队列就行;出栈就麻烦一些了,队列是先进先出,也就是说要完成出栈操作得让队尾的元素出队,那就让出队的元素重新入队,直到队头是原来的队尾元素,这时候出队就是出栈了。python的Queue()好像没有获取队头或者队尾元素的方法,要获取栈顶元素,就出栈再重新入栈。判断栈是否为空即判断这个队列是否为空。

from queue import Queue

class MyStack:

    def __init__(self):
        """
        Initialize your data structure here.
        """
        self.que = Queue()


    def push(self, x: int) -> None:
        """
        Push element x onto stack.
        """
        self.que.put(x)


    def pop(self) -> int:
        """
        Removes the element on top of the stack and returns that element.
        """
        n = self.que.qsize()
        while n > 1:
            self.que.put(self.que.get())
            n -= 1
        return self.que.get()


    def top(self) -> int:
        """
        Get the top element.
        """
        x = self.pop()
        self.push(x)
        return x


    def empty(self) -> bool:
        """
        Returns whether the stack is empty.
        """
        return self.que.empty()

232. 用栈实现队列

题目要求:
使用栈实现队列的下列操作:

  • push(x) -- 将一个元素放入队列的尾部。
  • pop() -- 从队列首部移除元素。
  • peek() -- 返回队列首部的元素。
  • empty() -- 返回队列是否为空。

思路:
一个栈肯定是没办法模拟队列的,至少要用到两个栈。stack1用来模拟入队操作,stack2模拟出队操作。入队操作比较简单,直接把元素压入stack1即可;出队操作要分成两种情况,stack2为空时,需要将stack1中的元素逐一弹出并压入stack2,这样一来stack1的栈底元素,即队列的队头就跑到了stack2的栈顶,也就是将stack1翻转了一遍,这时只要弹出stack2的栈顶即为出队操作。当stack2不空时,直接出栈就行了;要返回队头的元素,就返回stack2[-1]或者stack1[0];当两个栈都为空时,队列为空。

class MyQueue:

    def __init__(self):
        """
        Initialize your data structure here.
        """
        self.stack1 = []
        self.stack2 = []


    def push(self, x: int) -> None:
        """
        Push element x to the back of queue.
        """
        self.stack1.append(x)


    def pop(self) -> int:
        """
        Removes the element from in front of queue and returns that element.
        """
        if self.stack2:
            return self.stack2.pop()
        else:
            while self.stack1:
                self.stack2.append(self.stack1.pop())
            
            return self.stack2.pop()


    def peek(self) -> int:
        """
        Get the front element.
        """
        if self.stack2:
            return self.stack2[-1]
        else:
            return self.stack1[0]


    def empty(self) -> bool:
        """
        Returns whether the queue is empty.
        """
        return not self.stack1 and not self.stack2

345. 反转字符串中的元音字母

题目要求:
编写一个函数,以字符串作为输入,反转该字符串中的元音字母。

思路:
双指针。先将字符串转为数组,因为python中字符串是不可变类型。从头尾开始遍历这个数组,遇到元音字母就交换顺序。

class Solution:
    def reverseVowels(self, s: str) -> str:
        str_list = list(s)
        n = len(str_list)
        left, right = 0, n - 1
        hashmap = {'a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U'}
        while left < right:
            while left < right and str_list[left] not in hashmap:
                left += 1
            while left < right and str_list[right] not in hashmap:
                right -= 1
            str_list[left], str_list[right] = str_list[right], str_list[left]
            left += 1
            right -= 1
            
        return ''.join(str_list)

524. 通过删除字母匹配到字典里最长单词

题目要求:
给定一个字符串和一个字符串字典,找到字典里面最长的字符串,该字符串可以通过删除给定字符串的某些字符来得到。如果答案不止一个,返回长度最长且字典顺序最小的字符串。如果答案不存在,则返回空字符串。

思路:
双指针。题目中说“删除给定字符串的某些字符”,其实不用真的删除,只要用指针,如果不相等就跳过就好了。把字符串字典中的每个字符串拿出来与题中的字符串进行比较,如果可以过删除给定字符串的某些字符来得到,就保存到ans中,在保存之前要比较一下这个答案和之前的ans谁“更加”符合题目的要求,即长度最长且字典顺序最小的字符串。

class Solution:
    def findLongestWord(self, s: str, d: List[str]) -> str:
        ans = ""
        n = len(s)
        for string in d:
            j = 0
            for i in range(n):
                if s[i] == string[j]:
                    j += 1
                if j == len(string):
                    if j > len(ans) or (j == len(ans) and string < ans):
                        ans = string
                    break

        return ans

557. 反转字符串中的单词 III

题目要求:
给定一个字符串,你需要反转字符串中每个单词的字符顺序,同时仍保留空格和单词的初始顺序。

思路:
字符串中没有多余的空格,就以空格为界,将每个单词分开,再将其分别反转。

class Solution:
    def reverseWords(self, s: str) -> str:
        ans = []
        n = len(s)
        start = 0
        for i in range(n):
            if s[i] == ' ':
                ans.append(s[start:i])
                start = i + 1
        
        ans.append(s[start:])
        m = len(ans)
        for i in range(m):
            ans[i] = ans[i][::-1]
        return ' '.join(ans)

633. 平方数之和

题目要求:
给定一个非负整数 c ,你要判断是否存在两个整数 a 和 b,使得\(a^2 + b^2 = c\)

思路:
双指针。

class Solution:
    def judgeSquareSum(self, c: int) -> bool:
        if c == 0:
            return True
        
        lo, hi = 0, int(math.sqrt(c) + 1)
        while lo <= hi:
            if lo ** 2 + hi ** 2 > c:
                hi -= 1
            elif lo ** 2 + hi ** 2 < c:
                lo += 1
            else:
                return True
        
        return False

647. 回文子串

题目要求:
给定一个字符串,你的任务是计算这个字符串中有多少个回文子串。具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被视作不同的子串。

思路:
动态规划。用一个二维数组来记录字符串的子串s[j: i]是否是回文串,当s[j: i]的长度为 1 时,它一定是回文串;当s[i] == s[j]时,如果s[j + 1: i - 1]也是回文串,那么它也是回文串。其余情况都不是回文串。

class Solution:
    def countSubstrings(self, s: str) -> int:
        n = len(s)
        ans = 0
        dp = [[0] * n for _ in range(n)]
        for i in range(n):
            for j in range(i + 1):
                if s[i] == s[j] and (i - j < 2 or dp[j + 1][i - 1]):
                    dp[j][i] = 1
                    ans += 1

        return ans

657. 机器人能否返回原点

题目要求:
在二维平面上,有一个机器人从原点 (0, 0) 开始。给出它的移动顺序,判断这个机器人在完成移动后是否在 (0, 0) 处结束。

移动顺序由字符串表示。字符move[i]表示其第i次移动。机器人的有效动作有 R(右),L(左),U(上)和 D(下)。如果机器人在完成所有动作后返回原点,则返回true。否则,返回false

注意:机器人“面朝”的方向无关紧要。 “R” 将始终使机器人向右移动一次,“L” 将始终向左移动等。此外,假设每次移动机器人的移动幅度相同。

思路:
模拟法,左右移动次数要相同,上下也是。

class Solution:
    def judgeCircle(self, moves: str) -> bool:
        hrz, vti = 0, 0
        for s in moves:
            if s == 'L':
                hrz += 1
            elif s == 'R':
                hrz -= 1
            
            if s == 'U':
                vti += 1
            elif s == 'D':
                vti -= 1
        
        return hrz == 0 and vti == 0

696. 计数二进制子串

题目要求:
给定一个字符串s,计算具有相同数量0和1的非空(连续)子字符串的数量,并且这些子字符串中的所有0和所有1都是组合在一起的。重复出现的子串要计算它们出现的次数。

思路:
按照题意,这些0和1必须是相邻的,而且0和1的数量必须是相等的。于是只要分段统计0和1的个数,相邻计数中较小的那个就是连续二进制子串的个数。

class Solution:
    def countBinarySubstrings(self, s: str) -> int:
        n = len(s)
        p = pre = ans = cnt = 0
        while p < n:
            cur = s[p]
            while p < n and s[p] == cur:
                p += 1
                cnt += 1
            ans += min(cnt, pre)
            pre = cnt
            cnt = 0

        return ans

925. 长按键入

题目要求:
你的朋友正在使用键盘输入他的名字name。偶尔,在键入字符c时,按键可能会被长按,而字符可能被输入1次或多次。

你将会检查键盘输入的字符typed。如果它对应的可能是你的朋友的名字(其中一些字符可能被长按),那么就返回True

思路:
本来以为很简单,但是发现起始各种条件的限制是挺多的,不能相等了就移动一下了事。

class Solution:
    def isLongPressedName(self, name: str, typed: str) -> bool:
        m, n = len(name), len(typed)
        if m > n:
            return False
        i, j = 0, 0
        while i < m and j < n:
            if name[i] == typed[j]:
                i += 1
                j += 1
            elif j > 0 and typed[j] == typed[j - 1]:
                j += 1
            else:
                return False
        
        while j < n:
            if typed[j] != typed[j - 1]:
                return False
            j += 1
        
        return i == m

930. 和相同的二元子数组

题目要求:
在由若干01组成的数组A中,有多少个和为S的非空子数组。

思路:
前缀和可以算出任意一个切片的子数组的和,再用一个字典来计数,就可以在O(1)的时间内找到相应和的数量。实际上就是求满足S = preSum[j] - preSum[i]的子数组,也即preSum[j] = preSum[i] + S

class Solution(object):
    def numSubarraysWithSum(self, A, S):
        preSum = [0]
        for x in A:
            preSum.append(preSum[-1] + x)
        cnt = collections.Counter()
        ans = 0
        for x in preSum:
            ans += cnt[x]
            cnt[x + S] += 1

        return ans

977. 有序数组的平方

题目要求:
给定一个按非递减顺序排序的整数数组A,返回每个数字的平方组成的新数组,要求也按非递减顺序排序。

思路:
可以直接平方了再放进去排序。

class Solution:
    def sortedSquares(self, A: List[int]) -> List[int]:
        ans = []
        for x in A:
            ans.append(x ** 2)
        ans.sort()
        return ans

用双指针速度会更快一些。由于数组是非降序排列的,因此两边的元素一定是绝对值最大的。于是双指针由两边开始,且新元素也往新数组的后面放。

class Solution:
    def sortedSquares(self, A: List[int]) -> List[int]:
        n = len(A)
        if n == 1:
            return [A[0] ** 2]
        
        ans = [0] * n
        lo, hi = 0, n - 1
        while lo <= hi and n > 0:
            n -= 1
            if abs(A[lo]) > abs(A[hi]):
                ans[n] = A[lo] ** 2
                lo += 1
            else:
                ans[n] = A[hi] ** 2
                hi -= 1
        
        return ans

986. 区间列表的交集

题目要求:
给定两个由一些闭区间组成的列表,每个区间列表都是成对不相交的,并且已经排序。返回这两个区间列表的交集。

(形式上,闭区间[a, b](其中a <= b)表示实数x的集合,而a <= x <= b。两个闭区间的交集是一组实数,要么为空集,要么为闭区间。例如,[1, 3] 和 [2, 4] 的交集为 [2, 3]。)

思路:
双指针。两个指针分别指向两个区间列表中的一个区间,两个闭区间中较大的左边界作为交集的左边界,较小的右边界作为交集的右边界(要注意,指针指向的两个区间可能是不相交的,因此输出时需要检查左右边界的大小关系);对于右边界较小的区间,它不可能再与别的区间有交集,因此指针后移,这就是指针更新的规则。

class Solution:
    def intervalIntersection(self, A: List[List[int]], B: List[List[int]]) -> List[List[int]]:
        m, n = len(A), len(B)
        i = j = 0
        ans = []
        while i < m and j < n:
            start = max(A[i][0], B[j][0])
            if A[i][1] <= B[j][1]:
                end = A[i][1]
                i += 1
            else:
                end = B[j][1]
                j += 1
            
            if start <= end:
                ans.append([start, end])

        return ans

剑指 Offer 09. 用两个栈实现队列

题目要求:
用两个栈实现一个队列。队列的声明如下,请实现它的两个函数appendTaildeleteHead,分别完成在队列尾部插入整数和在队列头部删除整数的功能。(若队列中没有元素,deleteHead操作返回-1)

思路:
跟232一样,就是多个判断。

class CQueue:

    def __init__(self):
        self.stack1 = []
        self.stack2 = []


    def appendTail(self, value: int) -> None:
        self.stack1.append(value)


    def deleteHead(self) -> int:
        if self.stack2:
            return self.stack2.pop()
        elif self.stack1:
            while self.stack1:
                self.stack2.append(self.stack1.pop())
            return self.stack2.pop()
        else:
            return -1

剑指 Offer 22. 链表中倒数第k个节点

题目要求:
输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。例如,一个链表有6个节点,从头节点开始,它们的值依次是1、2、3、4、5、6。这个链表的倒数第3个节点是值为4的节点。

思路:
双指针。

class Solution:
    def getKthFromEnd(self, head: ListNode, k: int) -> ListNode:
        L = ListNode(0)
        L.next = head
        pre = q = L
        while k > 0:
            q = q.next
            k -= 1
        while q:
            pre = pre.next
            q = q.next
        return pre

面试题 10.01. 合并排序的数组

题目要求:
给定两个排序后的数组 A 和 B,其中 A 的末端有足够的缓冲空间容纳 B。编写一个方法,将 B 合并入 A 并排序。初始化 A 和 B 的元素数量分别为 m 和 n。

思路:
双指针。跟前面的88题是一个思路的。

class Solution:
    def merge(self, A: List[int], m: int, B: List[int], n: int) -> None:
        """
        Do not return anything, modify A in-place instead.
        """
        while m > 0 and n > 0:
            if A[m - 1] >= B[n - 1]:
                m -= 1
                A[m + n] = A[m]
            else:
                n -= 1
                A[m + n] = B[n]

        if n > 0:
            A[:n] = B[:n]

面试题 10.09. 排序矩阵查找

题目要求:
给定M×N矩阵,每一行、每一列都按升序排列,请编写代码找出某元素。

思路:
从右上角开始遍历矩阵,如果目标值大于当前元素,说明只可能在其下方找到目标值(当然也包括左下方),就往下走一行;如果目标值小于当前元素,说明不可能在当前列找到它了,往左走一列。

class Solution:
    def searchMatrix(self, matrix: List[List[int]], target: int) -> bool:
        if not matrix or not matrix[0]:
            return False

        m, n = len(matrix), len(matrix[0])
        row, col = 0, n - 1
        while row < m and col > -1:
            if target == matrix[row][col]:
                return True
            elif target < matrix[row][col]:
                col -= 1
            elif target > matrix[row][col]:
                row += 1
        
        return False
posted @ 2020-08-31 17:48  达芬骑驴  阅读(137)  评论(0编辑  收藏  举报