LeetCode-Tencent Top50

2020-02-24  本文已影响0人  inspiredhss

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
上一篇下一篇

猜你喜欢

热点阅读