美文网首页
LeetCode-Tencent Top50

LeetCode-Tencent Top50

作者: inspiredhss | 来源:发表于2020-02-24 07:48 被阅读0次

    237. 删除链表中的节点

    class Solution:
        def deleteNode(self, node):
            node.val = node.next.val
            node.next = node.next.next
    

    104. 二叉树的最大深度

    class Solution:
        def maxDepth(self, root):
            stack = []
            if root is not None:
                stack.append((1, root))        
            depth = 0
            while stack != []:
                current_depth, root = stack.pop()
                if root is not None:
                    depth = max(depth, current_depth)
                    stack.append((current_depth + 1, root.left))
                    stack.append((current_depth + 1, root.right))        
            return depth
    
    class Solution:
        def maxDepth(self, root):
            if root is None: 
                return 0 
            else: 
                left_height = self.maxDepth(root.left) 
                right_height = self.maxDepth(root.right) 
                return max(left_height, right_height) + 1 
    

    292. Nim 游戏

    class Solution:
        def canWinNim(self, n: int) -> bool:
            # """解法一,动态规划"""
            if n <= 3:
                return True
            dp = [False] * n
            dp[0], dp[1], dp[2] = True, True, True
            for i in range(3, n):
                if not dp[i - 1] or not dp[i - 2] or not dp[i - 3]:
                    dp[i] = True
            return dp[-1]
    
        def canWinNim(self, n: int) -> bool:
            """解法二,空间优化版的动归"""
            if n<=3:
                return True
            dp1,dp2,dp3=True,True,True
            for i in range(3,n):
                if not dp1 or not dp2 or not dp3:
                    dp=True
                else:
                    dp=False
                dp1, dp2, dp3 = dp2, dp3, dp
            return dp
    
        def canWinNim(self, n: int) -> bool:
            # """解法三,其实只要保证你你一直你一直面对的是4的倍数就行"""
            return n%4!=0
    

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

    # 557. 反转字符串中的单词 III
    # 输入: "Let's take LeetCode contest"
    # 输出: "s'teL ekat edoCteeL tsetnoc" 
    class Solution:
        def reverseWords(self, s: str) -> str:
            return ' '.join(s.split(' ')[::-1])[::-1]
    

    344. 反转字符串

    # 344. 反转字符串
    # 输入:["h","e","l","l","o"]
    # 输出:["o","l","l","e","h"]
    
    class Solution:
        def reverseString(self, s: List[str]) -> None:
            left, right = 0, len(s) - 1
            while left < right:
                s[left], s[right] = s[right], s[left]
                left, right = left + 1, right - 1
    

    206. 反转链表

    class Solution(object):
        def reverseList(self, head):
            # 申请两个节点,pre和 cur,pre指向None
            pre = None
            cur = head
            # 遍历链表
            while cur:
                # 记录当前节点的下一个节点
                tmp = cur.next
                # 然后将当前节点指向pre
                cur.next = pre
                # pre和cur节点都前进一位
                pre = cur
                cur = tmp
            return pre
            
    class Solution(object):
        def reverseList(self, head):
            # 递归终止条件是当前为空,或者下一个节点为空
            if(head==None or head.next==None):
                return head
            # 这里的cur就是最后一个节点
            cur = self.reverseList(head.next)
            # 这里请配合动画演示理解
            # 如果链表是 1->2->3->4->5,那么此时的cur就是5
            # 而head是4,head的下一个是5,下下一个是空
            # 所以head.next.next 就是5->4
            head.next.next = head
            # 防止链表循环,需要将head.next设置为空
            head.next = None
            # 每层递归函数都返回cur,也就是最后一个节点
            return cur
    

    169. 多数元素

    # 169. 多数元素
    # 大小为 n 的数组==>多数元素; 出现次数大于 ⌊ n/2 ⌋ 的元素。
    
    # 摩尔投票法
    # 众数出现的次数>>其他数字出现次数之和
    # 初始化res=0count=0
    # 遍历数组:
    # 若count==0,则将res更新为nums[i],并令count=1
    # 否则:
    # 若nums[i]==res,令count+=1
    # 若nums[i]!=res,令count-=1
    # 若最终count>0,则返回res
    class Solution:
        def majorityElement(self, nums: List[int]) -> int:
            n=len(nums)
            res=0
            count=0
            for i in range(n):
                if(count==0):
                    res=nums[i]
                    count=1
                else:
                    if(nums[i]==res): count+=1
                    else: count-=1
            if(count>0): return res
    
    # 哈希表
    class Solution:
        def majorityElement(self, nums: List[int]) -> int:
            hash={}
            n=len(nums)
            if(n==1):
                return nums[0]
            max_count=n//2
            for i in range(n):
                if(nums[i] not in hash):
                    hash[nums[i]]=1
                else:
                    hash[nums[i]]+=1
                    if(hash[nums[i]]>max_count):
                        return nums[i]
    

    21. 合并两个有序链表

    # Definition for singly-linked list.
    # 21. 合并两个有序链表
    # 输入:1->2->4, 1->3->4
    # 输出:1->1->2->3->4->4
    class Solution:
        def mergeTwoLists(self, l1, l2):
            prehead = ListNode(-1)
            prev = prehead
            while l1 and l2:
                if l1.val <= l2.val:
                    prev.next = l1
                    l1 = l1.next
                else:
                    prev.next = l2
                    l2 = l2.next            
                prev = prev.next
            prev.next = l1 if l1 is not None else l2
            return prehead.next
    

    122. 买卖股票的最佳时机 II

    # 算法流程:
    # 遍历整个股票交易日价格列表 price,策略是所有上涨交易日都买卖(赚到所有利润),所有下降交易日都不买卖(永不亏钱)。
    # 设 tmp 为第 i-1 日买入与第 i 日卖出赚取的利润,即 tmp = prices[i] - prices[i - 1] ;
    # 当该天利润为正 tmp > 0,则将利润加入总利润 profit;当利润为0 或为负,则直接跳过;
    # 遍历完成后,返回总利润 profit。
    
    # 时间复杂度 O(N) : 只需遍历一次price;
    # 空间复杂度 O(1) : 变量使用常数额外空间。
    
    class Solution:
        def maxProfit(self, prices: List[int]) -> int:
            profit = 0
            for i in range(1, len(prices)):
                tmp = prices[i] - prices[i - 1]
                if tmp > 0: profit += tmp
            return profit
    

    9. 回文数

        # 方法二: 将int转化成str类型: 双指针 (指针的性能一直都挺高的)
        # 复杂度: O(n)
        def isPalindrome(x: int) -> bool:
            lst = list(str(x))
            L, R = 0, len(lst)-1
            while L <= R:
                if lst[L] != lst[R]:
                    return  False
                L += 1
                R -= 1
            return True
    

    160. 相交链表

    # 一:两个指针分别指向两个链表头部,一起向前走直到其中一个到达末端,另一个与末端距离则是两链表的长度差。 再通过长链表指针先走的方式消除长度差,最终两链表即可同时走到相交点。
    # 换个方式消除长度差: 拼接两链表。
    # 设长-短链表为C,短-长链表为D,则当C走到长短链表交接处时,D走在长链表中,且与长链表头距离为长度差;
    # 当 ha == hb 时跳出,返回即可
    # 找到两个单链表相交的起始节点
    class Solution:
        def getIntersectionNode(self, headA, headB):
            ha, hb = headA, headB
            while ha != hb:
                ha = ha.next if ha else headB
                hb = hb.next if hb else headA
            return ha
    

    121. 买卖股票的最佳时机

    class Solution:
        def maxProfit(self, prices: List[int]) -> int:
            if len(prices) == 0:
                return 0
            prev = prices[0]
            max_profit = 0
            max_here = 0
            for t in prices[1:]:
                x = t - prev
                prev = t
                max_here = max_here + x if max_here > 0 else x
                max_profit = max(max_profit, max_here)
            return max_profit
    

    217. 存在重复元素

    class Solution:
        def containsDuplicate(self, nums: List[int]) -> bool:
            return len(nums) != len(set(nums))
    

    155. 最小栈

    class MinStack:
    
        # 辅助栈和数据栈不同步
        # 关键 1:辅助栈的元素空的时候,必须放入新进来的数
        # 关键 2:新来的数小于或者等于辅助栈栈顶元素的时候,才放入(特别注意这里等于要考虑进去)
        # 关键 3:出栈的时候,辅助栈的栈顶元素等于数据栈的栈顶元素,才出栈,即"出栈保持同步"就可以了
    
        def __init__(self):
            # 数据栈
            self.data = []
            # 辅助栈
            self.helper = []
    
        def push(self, x):
            self.data.append(x)
            # 关键 1 和关键 2
            if len(self.helper) == 0 or x <= self.helper[-1]:
                self.helper.append(x)
    
        def pop(self):
            # 关键 3:【注意】不论怎么样,数据栈都要 pop 出元素
            top = self.data.pop()
    
            if self.helper and top == self.helper[-1]:
                self.helper.pop()
            return top
    
        def top(self):
            if self.data:
                return self.data[-1]
    
        def getMin(self):
            if self.helper:
                return self.helper[-1]
    

    53. 最大子序和

    # 在整个数组或在固定大小的滑动窗口中找到总和或最大值或最小值的问题可以通过动态规划(DP)在线性时间内解决。
    # 有两种标准 DP 方法适用于数组:
    # 常数空间,沿数组移动并在原数组修改。
    # 线性空间,首先沿 left->right 方向移动,然后再沿 right->left 方向移动。 合并结果。
    # 我们在这里使用第一种方法,因为可以修改数组跟踪当前位置的最大和。
    # 下一步是在知道当前位置的最大和后更新全局最大和。
    
    # 时间复杂度:O(N)。只遍历了一次数组。
    # 空间复杂度:O(1),使用了常数的空间。
    
    class Solution:
        def maxSubArray(self, nums: 'List[int]') -> 'int':
            n = len(nums)
            max_sum = nums[0]
            for i in range(1, n):
                if nums[i - 1] > 0:
                    nums[i] += nums[i - 1] 
                max_sum = max(nums[i], max_sum)
            return max_sum
    

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

    class Solution:
        def removeDuplicates(self, nums: List[int]) -> int:
            for i in range(len(nums))[::-1]:
                if i-1 != -1:
                    if nums[i-1] == nums[i]:
                        del nums[i] 
            return len(nums)
            
    def main():
        n = [1]
        a=Solution()
        print(Solution.removeDuplicates(a,n))
    
    if __name__ == '__main__':
        main()
    
    # Ps:
    # python的GC也就是垃圾回收机制:
    # 由于python都是引用,而python有GC机制,所以,del语句作用在变量上,而不是数据对象上。
    # 将a和b del之后,1的引用计数仍然为1,所以不会被清除。
    #  a=1       # 对象 1 被 变量a引用,对象1的引用计数器为1
    #  b=a       # 对象1 被变量b引用,对象1的引用计数器加1 = 2
    #  c=a       #1对象1 被变量c引用,对象1的引用计数器加1 = 3
    #  del a     #删除变量a,解除a对1的引用
    #  del b     #删除变量b,解除b对1的引用
    #  print(c)  #最终变量c仍然引用1
    

    70. 爬楼梯

    class Solution:
        def climbStairs(self, n: int) -> int:
            climb = {}
            
            climb[0] = 0
            climb[1] = 1
            climb[2] = 2
            
            for i in range(3,n+1):
                climb[i] = climb[i-1] + climb[i-2]
            return climb[n]
    

    231. 2的幂

    class Solution:
        def isPowerOfTwo(self, n):
            if n == 0:
                return False
            while n % 2 == 0:
                n /= 2
            return n == 1
    

    141. 环形链表

    class Solution:
        def hasCycle(self, head: ListNode) -> bool:
            if(head == None or head.next == None):
                return False
            node1 = head
            node2 = head.next
            while(node1 != node2):
                if(node2 == None or node2.next == None):
                    return False
                node1 = node1.next
                node2 = node2.next.next
            return True
    

    20. 有效的括号

    class Solution:
        def isValid(self, s):
            # The stack to keep track of opening brackets.
            stack = []
    
            # Hash map for keeping track of mappings. This keeps the code very clean.
            # Also makes adding more types of parenthesis easier
            mapping = {")": "(", "}": "{", "]": "["}
    
            # For every bracket in the expression.
            for char in s:
    
                # If the character is an closing bracket
                if char in mapping:
    
                    # Pop the topmost element from the stack, if it is non empty
                    # Otherwise assign a dummy value of '#' to the top_element variable
                    top_element = stack.pop() if stack else '#'
    
                    # The mapping for the opening bracket in our hash and the top
                    # element of the stack don't match, return False
                    if mapping[char] != top_element:
                        return False
                else:
                    # We have an opening bracket, simply push it onto the stack.
                    stack.append(char)
    
            # In the end, if the stack is empty, then we have a valid expression.
            # The stack won't be empty for cases like ((()
            return not stack
    

    14. 最长公共前缀

    class Solution:
        def longestCommonPrefix(self, strs):
            if len(strs) == 0:
                return ''
            for i in range(len(strs[0])):
                c = strs[0][i]
                for j in range(1,len(strs)):
                    if i == len(strs[j]) or strs[j][i] != c:
                        return strs[0][0:i]
            return strs[0]
    

    7. 整数反转

    # 算法思路: 为对当前数取对 10 的余数,再一项项填入res尾部,即可完成 intint 翻转。
    # 边界情况处理: intint 取值范围为,如果翻转数字溢出,则立即返回 0 。
    # Python: 存储数字理论上是无限长度,因此每次计算完后判断res与of的大小关系即可;
    # Java: 数字计算会溢出,因此要判断res和of / 10的大小关系(即确定再添 11 位是否会溢出)。
    # Python的坑: 由于Python的 // 操作是向下取整,导致正负数取余 % 操作结果不一致,因此需要将原数字转为正数操作。
    # 代码:
    class Solution:
        def reverse(self, x: int) -> int:
            y, res = abs(x), 0
            of = (1 << 31) - 1 if x > 0 else 1 << 31
            while y != 0:
                res = res * 10 + y % 10
                if res > of: return 0
                y //= 10
            return res if x > 0 else -res
    

    59. 螺旋矩阵 II

    class Solution:
        def generateMatrix(self, n: int) -> List[List[int]]:
            l, r, t, b = 0, n - 1, 0, n - 1
            mat = [[0 for _ in range(n)] for _ in range(n)]
            num, tar = 1, n * n
            while num <= tar:
                for i in range(l, r + 1): # left to right
                    mat[t][i] = num
                    num += 1
                t += 1
                for i in range(t, b + 1): # top to bottom
                    mat[i][r] = num
                    num += 1
                r -= 1
                for i in range(r, l - 1, -1): # right to left
                    mat[b][i] = num
                    num += 1
                b -= 1
                for i in range(b, t - 1, -1): # bottom to top
                    mat[i][l] = num
                    num += 1
                l += 1
            return mat
    

    回溯问题

    经典的回溯算法解决的问题很多,如八皇后、0-1背包问题、图的着色、旅行商问题、全排列问题。

    78. 子集
    # 给定不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
    class Solution:
        def subsets(self, nums: List[int]) -> List[List[int]]:
            res = []
            n = len(nums)  
            def helper(i, tmp):
                res.append(tmp)
                for j in range(i, n):
                    helper(j + 1,tmp + [nums[j]] )  #子串拼接
            helper(0, [])
            return res 
    

    90. 子集 II

    # 给定一个可能包含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
    # 说明:解集不能包含重复的子集。
    class Solution:
        def subsetsWithDup(self, nums: List[int]) -> List[List[int]]:
            if not nums:
                return []
            n = len(nums)
            res = []
            nums.sort()
            # 思路1
            def helper1(idx, n, temp_list):
                if temp_list not in res:
                    res.append(temp_list)
                for i in range(idx, n):
                    helper1(i + 1, n, temp_list + [nums[i]])
            # 思路2
            def helper2(idx, n, temp_list):
                res.append(temp_list)
                for i in range(idx, n):
                    if i > idx and  nums[i] == nums[i - 1]:
                        continue
                    helper2(i + 1, n, temp_list + [nums[i]])
    
            helper2(0, n, [])
            return res
    

    39. 组合总和

    # 给定一个无重复元素的数组 candidates 和一个目标数 target ,
    # 找出 candidates 中所有可以使数字和为 target 的组合。
    # candidates 中的数字可以无限制重复被选取。
    class Solution:
        def combinationSum(self, candidates, target):
            if not candidates: return []
            if min(candidates) > target: return []
            candidates.sort()
            res = []
    
            def helper(candidates, target, temp_list):
                if target == 0: res.append(temp_list)
                if target < 0: return
                for i in range(len(candidates)):  # 可以重复出现多次
                    if candidates[i] > target: break
                    helper(candidates[i:], target - candidates[i], temp_list + [candidates[i]])
            helper(candidates,target,[])
            return res
    

    46. 全排列

    # 给定一个没有重复数字的序列,返回其所有可能的全排列。
    class Solution:
        def permute(self, nums: List[int]) -> List[List[int]]:
            if not nums:
                return
            res = []
            n = len(nums)
            visited = [0] * n
            # def helper1(temp_list,length):
            #     if length == n:
            #         res.append(temp_list)
            #     for i in range(n):
            #         if visited[i] :
            #             continue
            #         visited[i] = 1
            #         helper1(temp_list+[nums[i]],length+1)
            #         visited[i] = 0
            def helper2(nums,temp_list,length):
                if length == n:
                    res.append(temp_list)
                for i in range(len(nums)):
                    helper2(nums[:i]+nums[i+1:],temp_list+[nums[i]],length+1)
            # helper1([],0)
            helper2(nums, [], 0)
            return res
    

    47. 全排列 II

    # 给定一个可包含重复数字的序列,返回所有不重复的全排列。
    class Solution:
        def permuteUnique(self, nums: List[int]) -> List[List[int]]:
            if not nums:
                return []
            nums.sort()
            n = len(nums)
            visited = [0] * n
            res = []
    
            def helper1(temp_list, length):
                # if length == n and temp_list not in res:
                #   res.append(temp_list)
                if length == n:
                    res.append(temp_list)
                for i in range(n):
                    if visited[i] or (i > 0 and nums[i] == nums[i - 1] and not visited[i - 1]):
                        continue
                    visited[i] = 1
                    helper1(temp_list + [nums[i]], length + 1)
                    visited[i] = 0
    
            # def helper2(nums, temp_list, length):
            #     if length == n and temp_list not in res:
            #         res.append(temp_list)
            #     for i in range(len(nums)):
            #         helper2(nums[:i] + nums[i + 1:], temp_list + [nums[i]], length + 1)
    
            helper1([],0)
            # helper2(nums, [], 0)
            return res
    

    7. 整数反转

    # 算法思路: 为对当前数取对 10 的余数,再一项项填入res尾部,即可完成 intint 翻转。
    # 边界情况处理: intint 取值范围为,如果翻转数字溢出,则立即返回 0 。
    # Python: 存储数字理论上是无限长度,因此每次计算完后判断res与of的大小关系即可;
    # Java: 数字计算会溢出,因此要判断res和of / 10的大小关系(即确定再添 11 位是否会溢出)。
    # Python的坑: 由于Python的 // 操作是向下取整,导致正负数取余 % 操作结果不一致,因此需要将原数字转为正数操作。
    # 代码:
    class Solution:
        def reverse(self, x: int) -> int:
            y, res = abs(x), 0
            of = (1 << 31) - 1 if x > 0 else 1 << 31
            while y != 0:
                res = res * 10 + y % 10
                if res > of: return 0
                y //= 10
            return res if x > 0 else -res
    
    # Tips:
    # <<    左移动运算符:运算数的各二进位全部左移若干位,由 << 右边的数字指定了移动的位数,高位丢弃,低位补0。    
    # a << 2 输出结果 240 ,二进制解释: 1111 0000
    # >>    右移动运算符:把">>"左边的运算数的各二进位全部右移若干位,>> 右边的数字指定了移动的位数 
    # a >> 2 输出结果 15 ,二进制解释: 0000 1111
    

    88. 合并两个有序数组

    class Solution:
        def merge(self, nums1: List[int], m: int, nums2: List[int], n: int) -> None:
            # two get pointers for nums1 and nums2
            p1 = m - 1
            p2 = n - 1
            # set pointer for nums1
            p = m + n - 1
            
            # while there are still elements to compare
            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
            
            # add missing elements from nums2
            nums1[:p2 + 1] = nums2[:p2 + 1]
    
    class Solution:
        def productExceptSelf(self, nums: List[int]) -> List[int]:        
            # The length of the input array 
            length = len(nums)        
            # The answer array to be returned
            answer = [0]*length
            # answer[i] contains the product of all the elements to the left
            # Note: for the element at index '0', there are no elements to the left,
            # so the answer[0] would be 1
            answer[0] = 1
            for i in range(1, length):
                # answer[i - 1] already contains the product of elements to the left of 'i - 1'
                # Simply multiplying it with nums[i - 1] would give the product of all 
                # elements to the left of index 'i'
                answer[i] = nums[i - 1] * answer[i - 1]
            # R contains the product of all the elements to the right
            # Note: for the element at index 'length - 1', there are no elements to the right,
            # so the R would be 1
            R = 1;
            for i in reversed(range(length)):
                # For the index 'i', R would contain the 
                # product of all elements to the right. We update R accordingly
                answer[i] = answer[i] * R
                R *= nums[i]
            return answer
    

    215. 数组中的第K个最大元素

    # 未排序的数组中找到第 k 个最大的元素
    class Solution:
        def findKthLargest(self, nums, k):
            def partition(left, right, pivot_index):
                pivot = nums[pivot_index]
                # 1. move pivot to end
                nums[pivot_index], nums[right] = nums[right], nums[pivot_index]             
                # 2. move all smaller elements to the left
                store_index = left
                for i in range(left, right):
                    if nums[i] < pivot:
                        nums[store_index], nums[i] = nums[i], nums[store_index]
                        store_index += 1
                # 3. move pivot to its final place
                nums[right], nums[store_index] = nums[store_index], nums[right]            
                return store_index
            
            def select(left, right, k_smallest):
                if left == right:       # If the list contains only one element,
                    return nums[left]   # return that element
                
                # select a random pivot_index between 
                pivot_index = random.randint(left, right)     
                                
                # find the pivot position in a sorted list   
                pivot_index = partition(left, right, pivot_index)
                
                # the pivot is in its final sorted position
                if k_smallest == pivot_index:
                     return nums[k_smallest]
                # go left
                elif k_smallest < pivot_index:
                    return select(left, pivot_index - 1, k_smallest)
                # go right
                else:
                    return select(pivot_index + 1, right, k_smallest)
    
            # kth largest is (n - k)th smallest 
            return select(0, len(nums) - 1, len(nums) - k)
    

    230. 二叉搜索树中第K小的元素

    # 递归转换为迭代,这样可以加快速度,因为这样可以不用遍历整个树,可以在找到答案后停止。
    class Solution:
        def kthSmallest(self, root: TreeNode, k: int) -> int:
            stack = []
            while True:
                while root:
                    stack.append(root)
                    root = root.left
                root = stack.pop()
                k -= 1
                if not k:
                    return root.val
                root = root.right
    

    230. 二叉搜索树中第K小的元素

    # 递归转换为迭代,这样可以加快速度,因为这样可以不用遍历整个树,可以在找到答案后停止。
    class Solution:
        def kthSmallest(self, root: TreeNode, k: int) -> int:
            stack = []
            while True:
                while root:
                    stack.append(root)
                    root = root.left
                root = stack.pop()
                k -= 1
                if not k:
                    return root.val
                root = root.right
    

    89. 格雷编码

    class Solution:
        def grayCode(self, n: int) -> List[int]:
            res, head = [0], 1
            for i in range(n):
                for j in range(len(res) - 1, -1, -1):
                    res.append(head + res[j])
                head <<= 1
            return res
    

    236. 二叉树的最近公共祖先

    class Solution:
        def __init__(self):
            self.ans = None
        def lowestCommonAncestor(self, root, p, q):
            def recurse_tree(current_node):
                if not current_node:
                    return False
                left = recurse_tree(current_node.left)
                right = recurse_tree(current_node.right)
                mid = current_node == p or current_node == q
                # If any two of the three flags left, right or mid become True.
                if mid + left + right >= 2:
                    self.ans = current_node   #递归子树里第一个满足俩公共的节点
                # Return True if either of the three bool values is True.
                return mid or left or right
            # Traverse the tree
            recurse_tree(root)
            return self.ans
    
    class Solution:
        def lowestCommonAncestor(self, root, p, q):
            # Stack for tree traversal
            stack = [root]
            # Dictionary for parent pointers
            parent = {root: None}
            while p not in parent or q not in parent:
                node = stack.pop()
                # While traversing the tree, keep saving the parent pointers.
                if node.left:
                    parent[node.left] = node
                    stack.append(node.left)
                if node.right:
                    parent[node.right] = node
                    stack.append(node.right)
            ancestors = set()
            # Process all ancestors for node p using parent pointers.
            while p:
                ancestors.add(p)
                p = parent[p]
            # The first ancestor of q which appears in
            # p's ancestor set() is their lowest common ancestor.
            while q not in ancestors:
                q = parent[q]
            return q
    

    215. 数组中的第K个最大元素

    class Solution:
        def findKthLargest(self, nums: List[int], k: int) -> int:
            return heapq.nlargest(k, nums)[-1]
    
    
    class Solution:
        def findKthLargest(self, nums, k):
            def partition(left, right, pivot_index):
                pivot = nums[pivot_index]
                # 1. move pivot to end
                nums[pivot_index], nums[right] = nums[right], nums[pivot_index]           
                # 2. move all smaller elements to the left
                store_index = left
                for i in range(left, right):
                    if nums[i] < pivot:
                        nums[store_index], nums[i] = nums[i], nums[store_index]
                        store_index += 1
                # 3. move pivot to its final place
                nums[right], nums[store_index] = nums[store_index], nums[right]  
                return store_index
            
            def select(left, right, k_smallest):
                if left == right:       # If the list contains only one element,
                    return nums[left]   # return that element
                # select a random pivot_index between 
                pivot_index = random.randint(left, right)     
                # find the pivot position in a sorted list   
                pivot_index = partition(left, right, pivot_index)
                # the pivot is in its final sorted position
                if k_smallest == pivot_index:
                     return nums[k_smallest]
                # go left
                elif k_smallest < pivot_index:
                    return select(left, pivot_index - 1, k_smallest)
                # go right
                else:
                    return select(pivot_index + 1, right, k_smallest)
            # kth largest is (n - k)th smallest 
            return select(0, len(nums) - 1, len(nums) - k)
    # 时间复杂度 : 平均情况 O(N),最坏情况 O(N2)。
    # 空间复杂度 : O(1)。
    

    142. 环形链表 II

    class Solution:
        def detectCycle(self, head):
            visited = set()
            node = head
            while node is not None:
                if node in visited:
                    return node
                else:
                    visited.add(node)
                    node = node.next
            return None
    
    
    class Solution(object):
        def getIntersect(self, head):
            tortoise = head
            hare = head
            while hare is not None and hare.next is not None:
                tortoise = tortoise.next
                hare = hare.next.next
                if tortoise == hare:
                    return tortoise
            return None
    
        def detectCycle(self, head):
            if head is None:
                return None
            intersect = self.getIntersect(head)
            if intersect is None:
                return None
            ptr1 = head
            ptr2 = intersect
            while ptr1 != ptr2:
                ptr1 = ptr1.next
                ptr2 = ptr2.next
            return ptr1
    

    62. 不同路径

    # 令 dp[i][j] 是到达 i, j 最多路径
    # 动态方程:dp[i][j] = dp[i-1][j] + dp[i][j-1]
    # 第一行 dp[0][j],第一列 dp[i][0],边界为 1
    # 时间复杂度:O(m*n)O(m∗n)
    # 空间复杂度:O(m * n)O(m∗n)
    
    class Solution:
        def uniquePaths(self, m: int, n: int) -> int:
            dp = [[1]*n] + [[1]+[0] * (n-1) for _ in range(m-1)]
            for i in range(1, m):
                for j in range(1, n):
                    dp[i][j] = dp[i-1][j] + dp[i][j-1]
            return dp[-1][-1]
    
    # 优化1:空间复杂度 O(2n)O(2n)
    # 每次只需要 dp[i-1][j],dp[i][j-1]
    # 只记录这两个数
    class Solution:
        def uniquePaths(self, m: int, n: int) -> int:
            pre = [1] * n
            cur = [1] * n
            for i in range(1, m):
                for j in range(1, n):
                    cur[j] = pre[j] + cur[j-1]
                pre = cur[:]
            return pre[-1]
    
    # 优化2:空间复杂度 O(n)
    class Solution:
        def uniquePaths(self, m: int, n: int) -> int:
            cur = [1] * n
            for i in range(1, m):
                for j in range(1, n):
                    cur[j] += cur[j-1]
            return cur[-1]
    

    146. LRU缓存机制

    # 方法 1:有序字典
    # 实现 LRU 缓存机制,O(1) 时间内完成如下:
    
    # 获取键 / 检查键是否存在
    # 设置键
    # 删除最先插入的键
    # 前两个操作,哈希表在 O(1)时间。
    
    # 有序字典,综合哈希表和链表,在 Python 中为 OrderedDict,在 Java 中为 LinkedHashMap。
    # 首先,对于OrderedDict()中元素的存储,是等同把元素放入了一个可变数组一样
    # popitem(last=False)输出标号为0的元素的popitem(),输出标号为len-1的元素。
    # 但是这里如果修改了值,就好像a[key]=value,字典在数组中的顺序不变!
    # move_to_end,将第一个元素移到最后位置上
    
    from collections import OrderedDict
    class LRUCache(OrderedDict):
    
        def __init__(self, capacity):
            self.capacity = capacity
    
        def get(self, key):
            if key not in self:
                return - 1        
            self.move_to_end(key)
            return self[key]
    
        def put(self, key, value):
            if key in self:
                self.move_to_end(key)
            self[key] = value
            if len(self) > self.capacity:
                self.popitem(last = False)
    
    # LRUCache 对象会以如下语句构造和调用:
    # obj = LRUCache(capacity)
    # param_1 = obj.get(key)
    # obj.put(key,value)
    
    # 时间复杂度:put 和 get 操作复杂度是 O(1)
    # 有序字典中的所有操作:get/in/set/move_to_end/popitem(get/containsKey/put/remove)都可以在常数时间内完成。
    # 空间复杂度:O(capacity),因为空间只用于有序字典存储最多 capacity + 1 个元素。
    
    # 方法 2:哈希表 + 双向链表
    # 哈希表,辅以双向链表记录键值对的信息。
    # O(1) 时间内完成 put 和 get 操作,
    # 支持 O(1)删除第一个添加的节点。
    # 不需要额外信息删除节点,常数时间内从头部或尾部插入删除节点。
    # 双向链表实现中,伪头部和伪尾部标记界限,更新的时不需要检查null 节点。
    
    class DLinkedNode(): 
        def __init__(self):
            self.key = 0
            self.value = 0
            self.prev = None
            self.next = None
                
    class LRUCache():
        def _add_node(self, node):
            node.prev = self.head
            node.next = self.head.next
            self.head.next.prev = node
            self.head.next = node
    
        def _remove_node(self, node):
            prev = node.prev
            new = node.next
    
            prev.next = new
            new.prev = prev
    
        def _move_to_head(self, node):
            """
            Move certain node in between to the head.
            """
            self._remove_node(node)
            self._add_node(node)
    
        def _pop_tail(self):
            res = self.tail.prev
            self._remove_node(res)
            return res
    
        def __init__(self, capacity):
            self.cache = {}
            self.size = 0
            self.capacity = capacity
            self.head, self.tail = DLinkedNode(), DLinkedNode()
    
            self.head.next = self.tail
            self.tail.prev = self.head
            
    
        def get(self, key):
            node = self.cache.get(key, None)
            if not node:
                return -1
            # move the accessed node to the head;
            self._move_to_head(node)
            return node.value
    
        def put(self, key, value):
            node = self.cache.get(key)
            if not node: 
                newNode = DLinkedNode()
                newNode.key = key
                newNode.value = value
    
                self.cache[key] = newNode
                self._add_node(newNode)
    
                self.size += 1
    
                if self.size > self.capacity:
                    # pop the tail
                    tail = self._pop_tail()
                    del self.cache[tail.key]
                    self.size -= 1
            else:
                # update the value.
                node.value = value
                self._move_to_head(node)
    

    4. 寻找两个有序数组的中位数

    class Solution:
        def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float:
            n1 = len(nums1)
            n2 = len(nums2)
            if n1 > n2:
                return self.findMedianSortedArrays(nums2,nums1)
            k = (n1 + n2 + 1)//2
            left = 0
            right = n1
            while left < right :
                m1 = left +(right - left)//2
                m2 = k - m1
                if nums1[m1] < nums2[m2-1]:
                    left = m1 + 1
                else:
                    right = m1
            m1 = left
            m2 = k - m1 
            c1 = max(nums1[m1-1] if m1 > 0 else float("-inf"), nums2[m2-1] if m2 > 0 else float("-inf") )
            if (n1 + n2) % 2 == 1:
                return c1
            c2 = min(nums1[m1] if m1 < n1 else float("inf"), nums2[m2] if m2 <n2 else float("inf"))
            return (c1 + c2) / 2
    

    124. 二叉树中的最大路径和

    # 124. 二叉树中的最大路径和
    class Solution:
        def maxPathSum(self, root: TreeNode) -> int:
            def max_gain(node):
                nonlocal max_sum
                # global全局变量;
                # nonlocal上一级函数中局部变量,只能用于嵌套函数中,且外层定义了相应局部变量;
                if not node:
                    return 0
                left_gain = max(max_gain(node.left), 0)
                right_gain = max(max_gain(node.right), 0)    
                price_newpath = node.val + left_gain + right_gain 
                max_sum = max(max_sum, price_newpath)        
                return node.val + max(left_gain, right_gain)  
            max_sum = float('-inf')
            max_gain(root)
            return max_sum
    

    23. 合并K个排序链表

    class Solution:
        def mergeKLists(self, lists: List[ListNode]) -> ListNode:
            amount = len(lists)
            interval = 1
            while interval < amount:
                for i in range(0, amount - interval, interval * 2):
                    lists[i] = self.merge2Lists(lists[i], lists[i + interval])
                interval *= 2
            return lists[0] if amount > 0 else lists
    
        def merge2Lists(self, l1, l2):
            head = point = ListNode(0)
            while l1 and l2:
                if l1.val <= l2.val:
                    point.next = l1
                    l1 = l1.next
                else:
                    point.next = l2
                    l2 = l1
                    l1 = point.next.next
                point = point.next
            if not l1:
                point.next=l2
            else:
                point.next=l1
            return head.next
    

    23. 合并K个排序链表

    # 将 k 个链表配对并将同一对中的链表合并
    # 第一轮合并以后,k 个链表被合并成了 k/2 个链表
    
    class Solution:
        def mergeKLists(self, lists: List[ListNode]) -> ListNode:
            amount = len(lists)
            interval = 1
            while interval < amount:
                for i in range(0, amount - interval, interval * 2):
                    lists[i] = self.merge2Lists(lists[i], lists[i + interval])
                    # 0 2 4 6 8  &   1  3  5  7  9
                    # 0 2 4 6 8  &   1  3  5  7  9
                interval *= 2
            return lists[0] if amount > 0 else lists
    
        def merge2Lists(self, l1, l2):
            head = point = ListNode(0)
            while l1 and l2:
                if l1.val <= l2.val:
                    point.next = l1
                    l1 = l1.next
                else:
                    point.next = l2
                    l2 = l1
                    l1 = point.next.next
                point = point.next
            if not l1:
                point.next=l2
            else:
                point.next=l1
            return head.next
    

    33. 搜索旋转排序数组

    class Solution:
        def search(self, nums: List[int], target: int) -> int:
            def find_rotate_index(left, right):
                if nums[left] < nums[right]: return 0            
                while left <= right:
                    pivot = (left + right) // 2
                    if nums[pivot] > nums[pivot + 1]: return pivot + 1
                    else:
                        if nums[pivot] < nums[left]: right = pivot - 1
                        else: left = pivot + 1
                    
            def searchf(left, right):
                while left <= right:
                    pivot = (left + right) // 2
                    if nums[pivot] == target: return pivot
                    else:
                        if target < nums[pivot]: right = pivot - 1
                        else:left = pivot + 1
                return -1        
            n = len(nums)        
            if n == 0: return -1
            if n == 1: return 0 if nums[0] == target else -1         
            rotate_index = find_rotate_index(0, n - 1)  
            if nums[rotate_index] == target: return rotate_index
            if rotate_index == 0: return searchf(0, n - 1)
            if target < nums[0]: return searchf(rotate_index, n - 1)
            return searchf(0, rotate_index)
    

    124. 二叉树中的最大路径和

    class Solution:
        def spiralOrder(self, matrix: List[List[int]]) -> List[int]:
            if not matrix: return []
            R, C = len(matrix), len(matrix[0])
            seen = [[False] * C for _ in matrix]
            ans = []
            dr = [0, 1, 0, -1]
            dc = [1, 0, -1, 0]
            r = c = di = 0
            for _ in range(R * C):
                ans.append(matrix[r][c])
                seen[r][c] = True
                cr, cc = r + dr[di], c + dc[di]
                if 0 <= cr < R and 0 <= cc < C and not seen[cr][cc]:
                    r, c = cr, cc
                else:
                    di = (di + 1) % 4
                    r, c = r + dr[di], c + dc[di]
            return ans
    

    15. 三数之和

    class Solution:
        def threeSum(self, nums: List[int]) -> List[List[int]]:
            nums.sort()
            res, k = [], 0
            for k in range(len(nums) - 2):
                if nums[k] > 0: break # 1. because of j > i > k.
                if k > 0 and nums[k] == nums[k - 1]: continue # 2. skip the same `nums[k]`.
                i, j = k + 1, len(nums) - 1
                while i < j: # 3. double pointer
                    s = nums[k] + nums[i] + nums[j]
                    if s < 0:
                        i += 1
                        while i < j and nums[i] == nums[i - 1]: i += 1
                    elif s > 0:
                        j -= 1
                        while i < j and nums[j] == nums[j + 1]: j -= 1
                    else:
                        res.append([nums[k], nums[i], nums[j]])
                        i += 1
                        j -= 1
                        while i < j and nums[i] == nums[i - 1]: i += 1
                        while i < j and nums[j] == nums[j + 1]: j -= 1
            return res
    

    54. 螺旋矩阵

    class Solution:
        def spiralOrder(self, matrix: List[List[int]]) -> List[int]:
            if not matrix: return []
            R, C = len(matrix), len(matrix[0])
            seen = [[False] * C for _ in matrix]
            ans = []
            dr = [0, 1, 0, -1]
            dc = [1, 0, -1, 0]
            r = c = di = 0
            for _ in range(R * C):
                ans.append(matrix[r][c])
                seen[r][c] = True
                cr, cc = r + dr[di], c + dc[di]
                if 0 <= cr < R and 0 <= cc < C and not seen[cr][cc]:
                    r, c = cr, cc
                else:
                    di = (di + 1) % 4
                    r, c = r + dr[di], c + dc[di]
            return ans
    

    61. 旋转链表

    class Solution:
        def rotateRight(self, head: 'ListNode', k: 'int') -> 'ListNode':
            # base cases
            if not head:
                return None
            if not head.next:
                return head
            
            # close the linked list into the ring
            old_tail = head
            n = 1
            while old_tail.next:
                old_tail = old_tail.next
                n += 1
            old_tail.next = head
            
            # find new tail : (n - k % n - 1)th node
            # and new head : (n - k % n)th node
            new_tail = head
            for i in range(n - k % n - 1):
                new_tail = new_tail.next
            new_head = new_tail.next
            
            # break the ring
            new_tail.next = None
            
            return new_head
    

    16. 最接近的三数之和

    from typing import List
    class Solution:
        def threeSumClosest(self, nums: List[int], target: int) -> int:
            size = len(nums)
            # 特判
            if size < 3:
                return []
            # 初始化,因为找最小值,因此把初始值设置成实数的最大值
            diff = float('inf')
    
            # 排序是前提
            nums.sort()
    
            for i in range(size - 2):
                # 常见的剪枝操作
                if i > 0 and nums[i] == nums[i - 1]:
                    continue
                # 双指针:指针对撞
                left = i + 1
                right = size - 1
                while left < right:
                    s = nums[i] + nums[left] + nums[right]
    
                    if abs(s - target) < diff:
                        diff = abs(s - target)
                        res = s
    
                    # 不管是变小还是变大,尝试的作用是让 s 与 target 更接近
                    # 即 s 与 target 的绝对值之差越来越小
                    if s > target:
                        # 如果大了,尝试右边界收缩一格,让 target 变小
                        right -= 1
                    elif s < target:
                        # 如果小了,尝试左边界收缩一格,让 target 变大
                        left += 1
                    else:
                        # 如果已经等于 target 的话, 肯定是最接近的,根据题目要求,返回这三个数的和
                        return target
            return res
    

    148. 排序链表

    class Solution:
        def sortList(self, head: ListNode) -> ListNode:
            if not head or not head.next: return head # termination.
            # cut the LinkedList at the mid index.
            slow, fast = head, head.next
            while fast and fast.next:
                fast, slow = fast.next.next, slow.next
            mid, slow.next = slow.next, None # save and cut.
            # recursive for cutting.
            left, right = self.sortList(head), self.sortList(mid)
            # merge `left` and `right` linked list and return it.
            h = res = ListNode(0)
            while left and right:
                if left.val < right.val: h.next, left = left, left.next
                else: h.next, right = right, right.next
                h = h.next
            h.next = left if left else right
            return res.next
    
    class Solution:
        def sortList(self, head: ListNode) -> ListNode:
            h, length, intv = head, 0, 1
            while h: h, length = h.next, length + 1
            res = ListNode(0)
            res.next = head
            # merge the list in different intv.
            while intv < length:
                pre, h = res, res.next
                while h:
                    # get the two merge head `h1`, `h2`
                    h1, i = h, intv
                    while i and h: h, i = h.next, i - 1
                    if i: break # no need to merge because the `h2` is None.
                    h2, i = h, intv
                    while i and h: h, i = h.next, i - 1
                    c1, c2 = intv, intv - i # the `c2`: length of `h2` can be small than the `intv`.
                    # merge the `h1` and `h2`.
                    while c1 and c2:
                        if h1.val < h2.val: pre.next, h1, c1 = h1, h1.next, c1 - 1
                        else: pre.next, h2, c2 = h2, h2.next, c2 - 1
                        pre = pre.next
                    pre.next = h1 if c1 else h2
                    while c1 > 0 or c2 > 0: pre, c1, c2 = pre.next, c1 - 1, c2 - 1
                    pre.next = h 
                intv *= 2
            return res.next
    

    相关文章

      网友评论

          本文标题:LeetCode-Tencent Top50

          本文链接:https://www.haomeiwen.com/subject/xfqsqhtx.html