代码随想录算法训练营第二十三天 | 额外题目系列

news2024/11/24 6:05:42

额外题目

  • 1365. 有多少小于当前数字的数字
    • 借着本题,学习一下各种排序
    • 未看解答自己编写的青春版
    • 重点
    • 代码随想录的代码
    • 我的代码(当天晚上理解后自己编写)
  • 941.有效的山脉数组
    • 未看解答自己编写的青春版
    • 重点
    • 代码随想录的代码
    • 我的代码(当天晚上理解后自己编写)
  • 1207. 独一无二的出现次数
    • 未看解答自己编写的青春版
    • 重点
    • 代码随想录的代码
    • 我的代码(当天晚上理解后自己编写)
  • 283. 移动零
    • 重点
    • 代码随想录的代码
    • 我的代码(当天晚上理解后自己编写)
  • 189. 轮转数组
    • 未看解答自己编写的青春版
    • 重点
    • 代码随想录的代码
    • 我的代码(当天晚上理解后自己编写)
  • 724. 寻找数组的中心下标
    • 重点
    • 代码随想录的代码
    • 我的代码(当天晚上理解后自己编写)
  • 34. 在排序数组中查找元素的第一个和最后一个位置
    • 未看解答自己编写的青春版
    • 重点
    • 看了思路之后,自己写写试试?
    • 这个题目暴露了我二分法不会写
    • 代码随想录的代码
    • 我的代码(当天晚上理解后自己编写)
  • 目前的欠债:排序学习,二分法学习
  • 922. 按奇偶排序数组 II
    • 未看解答自己编写的青春版
    • 重点
    • 发现了我自己写的代码的问题了,我只要对奇数or偶数,操作就可以了,不需要全部操作。我写的代码太复杂了,没有利用好题目的特性。
    • 代码随想录的代码
    • 我的代码(当天晚上理解后自己编写)
  • 35.搜索插入位置
    • 未看解答自己编写的青春版
    • 重点
    • 代码随想录的代码
    • 我的代码(当天晚上理解后自己编写)
  • 24. 两两交换链表中的节点
    • 未看解答自己编写的青春版
    • 重点
    • 代码随想录的代码
    • 我的代码(当天晚上理解后自己编写)
  • 234. 回文链表
    • 未看解答自己编写的青春版
    • 重点
    • 代码随想录的代码
    • 我的代码(当天晚上理解后自己编写)
  • 143. 重排链表
    • 未看解答自己编写的青春版
    • 重点
    • 代码随想录的代码
    • 方法三,稍微练了一下
    • 我的代码(当天晚上理解后自己编写)
  • 141. 环形链表
    • 未看解答自己编写的青春版
    • 重点
    • 代码随想录的代码
    • 我的代码(当天晚上理解后自己编写)
  • 面试题 02.07. 链表相交
    • 未看解答自己编写的青春版
    • 重点
    • 代码随想录的代码
    • 我的代码(当天晚上理解后自己编写)
  • 205. 同构字符串
    • 未看解答自己编写的青春版
    • 重点
    • 代码随想录的代码
    • 用Python中最基础的{}作为字典的自己写版本
    • 我的代码(当天晚上理解后自己编写)
  • 1002. 查找共用字符
    • 未看解答自己编写的青春版
    • 重点
    • 代码随想录的代码
    • 我的代码(当天晚上理解后自己编写)
  • 925. 长按键入
    • 未看解答自己编写的青春版
    • 重点
    • 代码随想录的代码
    • 我的代码(当天晚上理解后自己编写)
  • 844. 比较含退格的字符串
    • 未看解答自己编写的青春版
    • 重点
    • 代码随想录的代码
    • 我的代码(当天晚上理解后自己编写)
  • 129. 求根节点到叶节点数字之和
    • 未看解答自己编写的青春版
    • 重点
    • 代码随想录的代码
    • 我的代码(当天晚上理解后自己编写)
  • 1382.将二叉搜索树变平衡
    • 未看解答自己编写的青春版
    • 重点
    • 代码随想录的代码
    • 我的代码(当天晚上理解后自己编写)
  • 100. 相同的树
    • 未看解答自己编写的青春版
    • 重点
    • 代码随想录的代码
    • 我的代码(当天晚上理解后自己编写)
  • 116. 填充每个节点的下一个右侧节点指针
    • 未看解答自己编写的青春版
    • 重点
    • 代码随想录的代码
    • 这道题是经典,之前没做过类似的!
    • 我的代码(当天晚上理解后自己编写)
  • 52. N皇后II
    • 未看解答自己编写的青春版
    • 重点
    • 代码随想录的代码
    • 我的代码(当天晚上理解后自己编写)
  • 649. Dota2 参议院
    • 未看解答自己编写的青春版
    • 重点
    • 这道题真的很妙
    • 代码随想录的代码
    • 我的代码(当天晚上理解后自己编写)
  • 1221. 分割平衡字符串
    • 未看解答自己编写的青春版
    • 重点
    • 代码随想录的代码
    • 我的代码(当天晚上理解后自己编写)
  • 5. 最长回文子串
    • 这种题到现在还是不会写,看着答案写一写
    • 重点
    • 本题的双指针法也值得学习
    • 代码随想录的代码
    • 我的代码(当天晚上理解后自己编写)
  • 132. 分割回文串 II
    • 自己看着解答写的不优秀代码
    • 重点
    • 代码随想录的代码
    • 我的代码(当天晚上理解后自己编写)
  • 673. 最长递增子序列的个数
    • 未看解答自己编写的青春版
    • 这题记录一下吧,没心情看了,思路还没看懂,本题也很重要
    • 重点
    • 代码随想录的代码
    • 我的代码(当天晚上理解后自己编写)
  • 657. 机器人能否返回原点
    • 未看解答自己编写的青春版
    • 重点
    • 代码随想录的代码
    • 我的代码(当天晚上理解后自己编写)
  • 剩下的几道题都很难,都是没接触过的题,后面再单开一个文章记录。

1365. 有多少小于当前数字的数字

借着本题,学习一下各种排序

未看解答自己编写的青春版

class Solution:
    def smallerNumbersThanCurrent(self, nums: List[int]) -> List[int]:
        temp = sorted(nums)
        n = len(temp)
        haxi = [0]*101
        for i in range(n-1,-1,-1) :
            haxi[temp[i]] = i
        res = [0]*n
        count = 0
        for i in nums :
            res[count] = haxi[i]
            count += 1
        return res

重点

排序之后加哈希,一开始加哈希是没想到的,光排序是不够的,在哈希赋值中,利用倒序遍历的小技巧,就可以避免值相同的情况。

代码随想录的代码

class Solution:
    def smallerNumbersThanCurrent(self, nums: List[int]) -> List[int]:
        res = nums[:]
        hash = dict()
        res.sort() # 从小到大排序之后,元素下标就是小于当前数字的数字
        for i, num in enumerate(res):
            if num  not in hash.keys(): # 遇到了相同的数字,那么不需要更新该 number 的情况
                hash[num] = i       
        for i, num in enumerate(nums):
            res[i] = hash[num]
        return res

我的代码(当天晚上理解后自己编写)

941.有效的山脉数组

未看解答自己编写的青春版

条件判断大法。

class Solution:
    def validMountainArray(self, arr: List[int]) -> bool:
        flag = 0
        n = len(arr)
        if n < 3 :
            return False
        
        for i in range(1,n):
            next_diff = arr[i]-arr[i-1]
            if flag == 0 :
                if next_diff > 0 :
                    continue
                elif next_diff < 0 and i != 1 :
                    flag = 1
                else :
                    return False
            else :
                if next_diff < 0 :
                    continue
                else :
                    return False
        if flag == 0 :
            return False
        return True

重点

代码随想录给出了双指针的思路,确实一开始没想到!这种判断序列的整体趋势的,都可以思考双指针法。

代码随想录的代码

class Solution:
    def validMountainArray(self, arr: List[int]) -> bool:
        left, right = 0, len(arr)-1
        
        while left < len(arr)-1 and arr[left+1] > arr[left]:
            left += 1
        
        while right > 0 and arr[right-1] > arr[right]:
            right -= 1
        
        return left == right and right != 0 and left != len(arr)-1

我的代码(当天晚上理解后自己编写)

1207. 独一无二的出现次数

未看解答自己编写的青春版

class Solution:
    def uniqueOccurrences(self, arr: List[int]) -> bool:
        haxi = [0]*2001
        n = len(arr)
        for i in range(n):
            haxi[arr[i]] += 1
        index = [0]*n
        for i in haxi :
            if i != 0 :
                if index[i-1] != 0 :
                    return False
                index[i-1] = 1
        return True

重点

两次哈希,我的想法和代码随想录的思路一模一样。

代码随想录的代码

# 方法 1: 数组在哈西法的应用
class Solution:
    def uniqueOccurrences(self, arr: List[int]) -> bool:
        count = [0] * 2002
        for i in range(len(arr)):
            count[arr[i] + 1000] += 1 # 防止负数作为下标
        freq = [False] * 1002 # 标记相同频率是否重复出现
        for i in range(2001):
            if count[i] > 0:
                if freq[count[i]] == False:
                    freq[count[i]] = True
                else:
                    return False
        return True
# 方法 2: map 在哈西法的应用
class Solution:
    def uniqueOccurrences(self, arr: List[int]) -> bool:
        ref = dict()

        for i in range(len(arr)):
            ref[arr[i]] = ref.get(arr[i], 0) + 1

        value_list = sorted(ref.values())

        for i in range(len(value_list) - 1):
            if value_list[i + 1] == value_list[i]:
                return False 

        return True 

我的代码(当天晚上理解后自己编写)

283. 移动零

class Solution:
    def moveZeroes(self, nums: List[int]) -> None:
        """
        Do not return anything, modify nums in-place instead.
        """
        n = len(nums)
        slow = 0
        fast = 0
        while fast < n :
            if nums[fast] != 0 :
                nums[slow] = nums[fast]
                fast += 1
                slow += 1

            else :
                fast += 1
        for i in range(slow,n):
            nums[i] = 0

重点

一开始其实没啥思路,没想到用双指针,还是水平太菜。

代码随想录的代码

def moveZeroes(self, nums: List[int]) -> None:
    slow = 0
    for fast in range(len(nums)):
        if nums[fast] != 0:
            nums[slow] = nums[fast]
            slow += 1
    for i in range(slow, len(nums)):
        nums[i] = 0

交换前后变量,避免补零

def moveZeroes(self, nums: List[int]) -> None:
        slow, fast = 0, 0
        while fast < len(nums):
            if nums[fast] != 0:
                nums[slow], nums[fast] = nums[fast], nums[slow]
                slow += 1 # 保持[0, slow)区间是没有0的
            fast += 1

我的代码(当天晚上理解后自己编写)

189. 轮转数组

未看解答自己编写的青春版

我自己想的额外申请空间版:

class Solution:
    def rotate(self, nums: List[int], k: int) -> None:
        """
        Do not return anything, modify nums in-place instead.
        """
        n = len(nums)
        k = k % n
        temp = nums[n-k:n]
        if n > k :
            for i in range(n-k-1,-1,-1):
                nums[i+k] = nums[i]
            nums[:k] = temp

重点

这题还是得看代码随想录的解答。
旋转子串我是没想到的,当时左旋转字符串那道题,就没想到,隔了这么久又该复习了。
在这里插入图片描述

代码随想录的代码

方法一:局部翻转 + 整体翻转

class Solution:
    def rotate(self, A: List[int], k: int) -> None:
        def reverse(i, j):
            while i < j:
                A[i], A[j] = A[j], A[i]
                i += 1
                j -= 1
        n = len(A)
        k %= n
        reverse(0, n - 1)
        reverse(0, k - 1)
        reverse(k, n - 1)

方法二:利用余数
申请copy数组了,而且比我的额外空间还大一些。

class Solution:
    def rotate(self, nums: List[int], k: int) -> None:
        copy = nums[:]

        for i in range(len(nums)):
            nums[(i + k) % len(nums)] = copy[i]
        
        return nums

        # 备注:这个方法会导致空间复杂度变成 O(n) 因为我们要创建一个 copy 数组。但是不失为一种思路。

我的代码(当天晚上理解后自己编写)

724. 寻找数组的中心下标

class Solution:
    def pivotIndex(self, nums: List[int]) -> int:
        total = sum(nums)
        leftsum = 0
        index = 0
        for i in nums :
            rightsum = total - i - leftsum
            if rightsum == leftsum :
                return index
            else :
                leftsum += i
            index += 1
        return -1

重点

先算总和,再遍历数组,和代码随想录的思路一模一样。

代码随想录的代码

class Solution:
    def pivotIndex(self, nums: List[int]) -> int:
        numSum = sum(nums) #数组总和
        leftSum = 0
        for i in range(len(nums)):
            if numSum - leftSum -nums[i] == leftSum: #左右和相等
                return i
            leftSum += nums[i]
        return -1

我的代码(当天晚上理解后自己编写)

34. 在排序数组中查找元素的第一个和最后一个位置

未看解答自己编写的青春版

一开始没思路。想到二分法了,但是觉得二分法,无法处理有相同值的情况。

重点

看了解答,两次二分法啊!先确定左边界,再确定右边界。

看了思路之后,自己写写试试?

没写出来,二分法不会写了,下面是没写完的代码:

class Solution:
    def searchRange(self, nums: List[int], target: int) -> List[int]:
        n = len(nums)
        minima = nums[0]
        maxima = nums[-1]
        if target >= minima and target <= maxima :
            left_edge = -2
            left = 0
            right = n-1
            while left < right :
                middle = left + (right-left)//2
                if target > nums[middle] :
                    left = middle
                else :
                    right = middle
            left_edge = left
            right_edge = -2
            left = 0
            right = n-1
            while left < right :
                middle = left + (right-left)//2
                if target < nums[middle] :
                    left = middle
                else :
                    right = middle
            right_edge = right
            if 

        else :
            return [-1,-1]

这个题目暴露了我二分法不会写

代码随想录的代码

本题的其他方法先暂时不看,先搞懂这一种方法。

class Solution:
    def searchRange(self, nums: List[int], target: int) -> List[int]:
        def getRightBorder(nums:List[int], target:int) -> int:
            left, right = 0, len(nums)-1
            rightBoder = -2 # 记录一下rightBorder没有被赋值的情况
            while left <= right:
                middle = left + (right-left) // 2
                if nums[middle] > target:
                    right = middle - 1
                else: # 寻找右边界,nums[middle] == target的时候更新left
                    left = middle + 1
                    rightBoder = left
    
            return rightBoder
        
        def getLeftBorder(nums:List[int], target:int) -> int:
            left, right = 0, len(nums)-1 
            leftBoder = -2 # 记录一下leftBorder没有被赋值的情况
            while left <= right:
                middle = left + (right-left) // 2
                if nums[middle] >= target: #  寻找左边界,nums[middle] == target的时候更新right
                    right = middle - 1
                    leftBoder = right
                else:
                    left = middle + 1
            return leftBoder
        leftBoder = getLeftBorder(nums, target)
        rightBoder = getRightBorder(nums, target)
        # 情况一
        if leftBoder == -2 or rightBoder == -2: return [-1, -1]
        # 情况三
        if rightBoder -leftBoder >1: return [leftBoder + 1, rightBoder - 1]
        # 情况二
        return [-1, -1]

我的代码(当天晚上理解后自己编写)

目前的欠债:排序学习,二分法学习

922. 按奇偶排序数组 II

未看解答自己编写的青春版

不需要额外存储空间,但是时间只打败5%

class Solution:
    def sortArrayByParityII(self, nums: List[int]) -> List[int]:
        n = len(nums)
        for i in range(n):
            if nums[i]%2 == 0 and i%2 == 0:
                continue
            elif nums[i]%2 == 1 and i%2 == 1:
                continue
            elif nums[i]%2 == 0 and i%2 == 1:
                j = i+1
                while j < n :
                    if nums[j]%2 == 1 and j%2 == 0 :
                        break
                    j += 1
                nums[i],nums[j] = nums[j],nums[i]
            else :
                j = i+1
                while j < n :
                    if nums[j]%2 == 0 and j%2 == 1 :
                        break
                    j += 1
                nums[i],nums[j] = nums[j],nums[i]
        return nums

申请一块额外数组的方法很好写。

重点

三种方法。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

发现了我自己写的代码的问题了,我只要对奇数or偶数,操作就可以了,不需要全部操作。我写的代码太复杂了,没有利用好题目的特性。

修改加速版,方法三真牛逼。

class Solution:
    def sortArrayByParityII(self, nums: List[int]) -> List[int]:
        n = len(nums)
        index = 1
        for i in range(0,n,2):
            if nums[i] % 2 == 1 :
                while nums[index] %2 == 1 :
                    index += 2
                nums[i],nums[index]=nums[index],nums[i]
        return nums

代码随想录的代码

过。

我的代码(当天晚上理解后自己编写)

35.搜索插入位置

未看解答自己编写的青春版

还行,这是最基础的二分法,写出来了

class Solution:
    def searchInsert(self, nums: List[int], target: int) -> int:
        nums.sort()
        n = len(nums)
        mini = nums[0]
        maxi = nums[-1]
        if target < mini :
            return 0
        elif target > maxi :
            return n
        left = 0
        right = n-1
        while left <= right :
            middle = left + (right-left)//2
            if nums[middle] > target :
                right = middle-1
            elif nums[middle] < target :
                left = middle + 1
            else :
                return middle
               # 这里直接return left就行
        return max(left,right)

重点

以后大家只要看到面试题里给出的数组是有序数组,都可以想一想是否可以使用二分法。

同时题目还强调数组中无重复元素,因为一旦有重复元素,使用二分查找法返回的元素下标可能不是唯一的。

代码随想录的代码

class Solution:
    def searchInsert(self, nums: List[int], target: int) -> int:
        left, right = 0, len(nums) - 1

        while left <= right:
            middle = (left + right) // 2

            if nums[middle] < target:
                left = middle + 1
            elif nums[middle] > target:
                right = middle - 1
            else:
                return middle
        return right + 1

我的代码(当天晚上理解后自己编写)

24. 两两交换链表中的节点

未看解答自己编写的青春版

这次写,竟然有些磕磕绊绊,不过稍微调了调,还是AC了。

class Solution:
    def swapPairs(self, head: Optional[ListNode]) -> Optional[ListNode]:
        if head==None :
            return head
        if head.next == None :
            return head
        virtual = ListNode(0,head)
        cur = virtual
        while head and head.next :
            temp = head.next.next
            cur.next = head.next
            head.next.next = head
            head.next = temp
            cur = head
            head = cur.next
        return virtual.next

重点

这道题只要画图,逻辑就能明白。

代码随想录的代码

递归版本还是不太好理解的,我感觉。

# 递归版本
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next

class Solution:
    def swapPairs(self, head: Optional[ListNode]) -> Optional[ListNode]:
        if head is None or head.next is None:
            return head

        # 待翻转的两个node分别是pre和cur
        pre = head
        cur = head.next
        next = head.next.next
        
        cur.next = pre  # 交换
        pre.next = self.swapPairs(next) # 将以next为head的后续链表两两交换
         
        return cur
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next

class Solution:
    def swapPairs(self, head: ListNode) -> ListNode:
        dummy_head = ListNode(next=head)
        current = dummy_head
        
        # 必须有cur的下一个和下下个才能交换,否则说明已经交换结束了
        while current.next and current.next.next:
            temp = current.next # 防止节点修改
            temp1 = current.next.next.next
            
            current.next = current.next.next
            current.next.next = temp
            temp.next = temp1
            current = current.next.next
        return dummy_head.next

我的代码(当天晚上理解后自己编写)

234. 回文链表

未看解答自己编写的青春版

失败失败,用链表不行,想错了,只考虑了“1221”的情况,没考虑“12121”的情况。错误代码如下:

class Solution:
    def isPalindrome(self, head: Optional[ListNode]) -> bool:
        stack = []
        while head :
            if stack == []:
                stack.append(head.val)
            else :
                node = stack[-1]
                if node == head.val :
                    stack.pop()

                else :
                    stack.append(head.val)
            head = head.next

        n = len(stack)
        if n == 0 :
            return True
        else :
            return False

重点

翻转链表的方法细节很多,还是用数组承接是最简单的方法,不做翻转链表可以作为代码练习。

代码随想录的代码

#数组模拟
class Solution:
    def isPalindrome(self, head: Optional[ListNode]) -> bool:
        list=[]
        while head: 
            list.append(head.val)
            head=head.next
        l,r=0, len(list)-1
        while l<=r: 
            if list[l]!=list[r]:
                return False
            l+=1
            r-=1
        return True   
      
#反转后半部分链表
class Solution:
    def isPalindrome(self, head: Optional[ListNode]) -> bool:
        fast = slow = head

        # find mid point which including (first) mid point into the first half linked list
        while fast and fast.next:
            fast = fast.next.next
            slow = slow.next
        node = None

        # reverse second half linked list
        while slow:
            slow.next, slow, node = node, slow.next, slow
        # 这个翻转链表的代码也太复杂了,太难理解了,还是展开写,感觉翻转操作很好写啊
        '''
        while slow:
            temp = slow.next
            slow.next = node
            node = slow
            slow = temp
		'''

        # compare reversed and original half; must maintain reversed linked list is shorter than 1st half
        while node:
            if node.val != head.val:
                return False
            node = node.next
            head = head.next
        return True

我的代码(当天晚上理解后自己编写)

143. 重排链表

未看解答自己编写的青春版

没什么思路

重点

主要有三个思路:数组模拟、双向队列模拟、直接分割链表。

把链表放进数组中,然后通过双指针法,一前一后,来遍历数组,构造链表。

把链表放进双向队列,然后通过双向队列一前一后弹出数据,来构造新的链表。这种方法比操作数组容易一些,不用双指针模拟一前一后了。

在这里插入图片描述

代码随想录的代码

# 方法二 双向队列
class Solution:
    def reorderList(self, head: ListNode) -> None:
        """
        Do not return anything, modify head in-place instead.
        """
        d = collections.deque()
        tmp = head
        while tmp.next: # 链表除了首元素全部加入双向队列
            d.append(tmp.next)
            tmp = tmp.next
        tmp = head
        while len(d): # 一后一前加入链表
            tmp.next = d.pop()
            tmp = tmp.next
            if len(d):
                tmp.next = d.popleft()
                tmp = tmp.next
        tmp.next = None # 尾部置空
 
# 方法三 反转链表
class Solution:
    def reorderList(self, head: ListNode) -> None:
        if head == None or head.next == None:
            return True
        slow, fast = head, head
        while fast and fast.next:
            slow = slow.next
            fast = fast.next.next
        right = slow.next # 分割右半边
        slow.next = None # 切断
        right = self.reverseList(right) #反转右半边
        left = head
        # 左半边一定比右半边长, 因此判断右半边即可
        while right:
            curLeft = left.next
            left.next = right
            left = curLeft

            curRight = right.next
            right.next = left
            right = curRight


    def reverseList(self, head: ListNode) -> ListNode:
        cur = head   
        pre = None
        while(cur!=None):
            temp = cur.next # 保存一下cur的下一个节点
            cur.next = pre # 反转
            pre = cur
            cur = temp
        return pre

方法三,稍微练了一下

class Solution:
    def reorderList(self, head: Optional[ListNode]) -> None:
        """
        Do not return anything, modify head in-place instead.
        """
        fast = slow = head

        # find mid point which including (first) mid point into the first half linked list
        while fast and fast.next:
            fast = fast.next.next
            slow = slow.next
        # 下面两句代码别忘了,一个是取右半边,这样取的右半边一定是短的那一方
        # 这样连接也符合题目要求
        right = slow.next # 获取后半天的头
        slow.next = None # 切断!这句话很重要,不然就成环了
        node = None
        while right:
            temp = right.next
            right.next = node
            node = right
            right = temp
        head2 = node
        head1 = head
        while head1 and head2:
            temp1 = head1.next
            temp2 = head2.next
            head1.next = head2
            head2.next = temp1
            head1 = temp1
            head2 = temp2

我的代码(当天晚上理解后自己编写)

141. 环形链表

未看解答自己编写的青春版

只判断是否有环,简单

注意判断条件,当循环里需要用到 fast.next.next 时,while 的判断条件要是:while fast and fast.next 。切记切记。

class Solution:
    def hasCycle(self, head: Optional[ListNode]) -> bool:
        if head == None :
            return False
        if head.next == None:
            return False
        slow = fast = head
        # 注意判断条件,当循环里需要用到 fast.next.next 时,while 的判断条件要是:
        # while fast and fast.next 。切记切记。
        while fast and fast.next :
            slow = slow.next
            fast = fast.next.next
            if slow == fast :
                return True

        return False

重点

如何找入口?自己画一个图,分成三段,x y z ,则有: 2(x+y) = x + y + z + y ,所以 x = z 。那么从头结点出发一个slow,从相遇节点出发一个fast,两个指针每次都移动一格,相遇节点即为环的入口!
在这里插入图片描述
在这里插入图片描述

代码随想录的代码

class Solution:
    def hasCycle(self, head: ListNode) -> bool:
        if not head: return False
        slow, fast = head, head
        while fast and fast.next:
            slow = slow.next
            fast = fast.next.next
            if fast == slow:
                return True
        return False

我的代码(当天晚上理解后自己编写)

面试题 02.07. 链表相交

未看解答自己编写的青春版

我的这个代码,内存占用很高。

class Solution:
    def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
        count1 = count2 = 0
        cur1 = headA
        cur2 = headB
        while cur1 :
            count1 += 1
            cur1 = cur1.next
        while cur2 :
            count2 += 1
            cur2 = cur2.next

        if count1 >= count2 :
            diff = count1 - count2
            while diff > 0 :
                headA = headA.next
                diff -= 1
            while headA :
                if headA == headB :
                    return headA
                headA = headA.next
                headB = headB.next
            return None
        else :
            diff = count2 - count1
            while diff > 0 :
                headB = headB.next
                diff -= 1
            while headA :
                if headA == headB :
                    return headA
                headA = headA.next
                headB = headB.next
            return None

重点

代码随想录的最冗余版本都比我的简洁!这题一定要学习他的写法。

代码随想录的代码

(版本一)求长度,同时出发

class Solution:
    def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
        lenA, lenB = 0, 0
        cur = headA
        while cur:         # 求链表A的长度
            cur = cur.next 
            lenA += 1
        cur = headB 
        while cur:         # 求链表B的长度
            cur = cur.next 
            lenB += 1
        curA, curB = headA, headB
        if lenA > lenB:     # 让curB为最长链表的头,lenB为其长度
            curA, curB = curB, curA
            lenA, lenB = lenB, lenA 
        for _ in range(lenB - lenA):  # 让curA和curB在同一起点上(末尾位置对齐)
            curB = curB.next 
        while curA:         #  遍历curA 和 curB,遇到相同则直接返回
            if curA == curB:
                return curA
            else:
                curA = curA.next 
                curB = curB.next
        return None 
(版本二)求长度,同时出发 (代码复用)
class Solution:
    def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
        lenA = self.getLength(headA)
        lenB = self.getLength(headB)
        
        # 通过移动较长的链表,使两链表长度相等
        if lenA > lenB:
            headA = self.moveForward(headA, lenA - lenB)
        else:
            headB = self.moveForward(headB, lenB - lenA)
        
        # 将两个头向前移动,直到它们相交
        while headA and headB:
            if headA == headB:
                return headA
            headA = headA.next
            headB = headB.next
        
        return None
    
    def getLength(self, head: ListNode) -> int:
        length = 0
        while head:
            length += 1
            head = head.next
        return length
    
    def moveForward(self, head: ListNode, steps: int) -> ListNode:
        while steps > 0:
            head = head.next
            steps -= 1
        return head
(版本三)求长度,同时出发 (代码复用 + 精简)
class Solution:
    def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
        dis = self.getLength(headA) - self.getLength(headB)
        
        # 通过移动较长的链表,使两链表长度相等
        if dis > 0:
            headA = self.moveForward(headA, dis)
        else:
            headB = self.moveForward(headB, abs(dis))
        
        # 将两个头向前移动,直到它们相交
        while headA and headB:
            if headA == headB:
                return headA
            headA = headA.next
            headB = headB.next
        
        return None
    
    def getLength(self, head: ListNode) -> int:
        length = 0
        while head:
            length += 1
            head = head.next
        return length
    
    def moveForward(self, head: ListNode, steps: int) -> ListNode:
        while steps > 0:
            head = head.next
            steps -= 1
        return head

最后这个方法,理解不了,不知道在干什么。

(版本四)等比例法
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None


class Solution:
    def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
        # 处理边缘情况
        if not headA or not headB:
            return None
        
        # 在每个链表的头部初始化两个指针
        pointerA = headA
        pointerB = headB
        
        # 遍历两个链表直到指针相交
        while pointerA != pointerB:
            # 将指针向前移动一个节点
            pointerA = pointerA.next if pointerA else headB
            pointerB = pointerB.next if pointerB else headA
        
        # 如果相交,指针将位于交点节点,如果没有交点,值为None
        return pointerA

我的代码(当天晚上理解后自己编写)

205. 同构字符串

未看解答自己编写的青春版

自己的思路是错的!

因为题目中没有给出:字符串一定由英文字母组成,所以不能使用哈希!

重点

字符串没有说都是小写字母之类的,所以用数组不合适了,用map来做映射。

使用两个map 保存 s[i] 到 t[j] 和 t[j] 到 s[i] 的映射关系,如果发现对应不上,立刻返回 false

代码随想录的代码

class Solution:
    def isIsomorphic(self, s: str, t: str) -> bool:
        default_dict1 = defaultdict(str)
        default_dict2 = defaultdict(str)
    
        if len(s) != len(t): return false

        for i in range(len(s)):
            if not default_dict1[s[i]]:
                default_dict1[s[i]] = t[i]
            
            if not default_dict2[t[i]]:
                default_dict2[t[i]] = s[i]

            if default_dict1[s[i]] != t[i] or default_dict2[t[i]] != s[i]:
                return False
            
        return True

用Python中最基础的{}作为字典的自己写版本

class Solution:
    def isIsomorphic(self, s: str, t: str) -> bool:
        dic1 = {}
        dic2 = {}
        n = len(s)
        m = len(t)
        if m!=n :
            return False
        for i in range(n):
            if not dic1.get(s[i],0):
                dic1[s[i]] = t[i]
            if not dic2.get(t[i],0):
                dic2[t[i]] = s[i]

            if dic1[s[i]]!=t[i] or dic2[t[i]]!=s[i]:
                return False

        return True

我的代码(当天晚上理解后自己编写)

1002. 查找共用字符

未看解答自己编写的青春版

这道题在编写能力上要求很高,因为如果是N个字符串,要有N个哈希表。

重点

哈希哈希,看到字符串全部由小学字母组成没有?这么明显的提示,不用哈希用什么?

主要注意的点是,如果两个字符串为 [ ‘ll’ , ‘ll’ ] , 结果应该是: [ ‘l’ , ‘l’ ] , 而不是 [ ‘l’ ] 。

所以要取每个哈希中的最小值。

注意:思路是取三个哈希,不能只取一个计数!(我一开始是这样想的),因为这样会有问题,假如第一个字符串出现两个
‘a’ , 如果只有一个哈希表,会计数为2 , 和两个字符串中都出现一个 ‘a’ 的结果一样。

代码随想录的代码

看懂了,但应该还没掌握。

class Solution:
    def commonChars(self, words: List[str]) -> List[str]:
        if not words: return []
        result = []
        hash = [0] * 26 # 用来统计所有字符串里字符出现的最小频率
        for i, c in enumerate(words[0]):  # 用第一个字符串给hash初始化
            hash[ord(c) - ord('a')] += 1
        # 统计除第一个字符串外字符的出现频率
        for i in range(1, len(words)):
            hashOtherStr = [0] * 26
            for j in range(len(words[i])):
                hashOtherStr[ord(words[i][j]) - ord('a')] += 1
            # 更新hash,保证hash里统计26个字符在所有字符串里出现的最小次数
            for k in range(26):
                hash[k] = min(hash[k], hashOtherStr[k])
        # 将hash统计的字符次数,转成输出形式
        for i in range(26):
            while hash[i] != 0: # 注意这里是while,多个重复的字符
                result.extend(chr(i + ord('a')))
                hash[i] -= 1
        return result
class Solution:
    def commonChars(self, words: List[str]) -> List[str]:
        tmp = collections.Counter(words[0])
        l = []
        for i in range(1,len(words)):
            # 使用 & 取交集
            tmp = tmp & collections.Counter(words[i])

        # 剩下的就是每个单词都出现的字符(键),个数(值)
        for j in tmp:
            v = tmp[j]
            while(v):
                l.append(j)
                v -= 1
        return l

我的代码(当天晚上理解后自己编写)

925. 长按键入

未看解答自己编写的青春版

class Solution:
    def isLongPressedName(self, name: str, typed: str) -> bool:
        n = len(name)
        m = len(typed)
        i = j =0
        while i < n and j < m :
            if name[i] == typed[j] :
                i += 1
                j += 1
            else :
                if j == 0 :
                    return False
                while j < m and typed[j]==typed[j-1] :
                    j += 1
                # 这个判断是要加的,不加会报 index 的错误
                # 笑死了,C++不加就不会报错,python不加就会报错
                # 不同语言的测试用例还不一样
                # 不过应该是需要加的我认为
                if j == m :
                    return False
                # 移动后要再次匹配,关键
                if typed[j] != name[i] :
                    return False
                else :
                    i += 1
                    j += 1
        # 排除最后一个字符重复的情况,移动j
        if j < m :
            while j < m and typed[j]==typed[j-1]:
                j+=1
        if i < n or j < m:
            return False
        
        else :
            return True

重点

在这里插入图片描述

代码随想录的代码

代码随想录这题给的代码不好,看我上面自己写的就行,思路和代码随想录的完全一致。

我的代码(当天晚上理解后自己编写)

844. 比较含退格的字符串

未看解答自己编写的青春版

用栈模拟很简单,只要注意,只有当栈非空时,再对栈进行操作就可以避免bug。

重点

这道题要学习使用双指针法的思路。
在这里插入图片描述

代码随想录的代码

class Solution:

    def get_string(self, s: str) -> str :
        bz = []
        for i in range(len(s)) :
            c = s[i]
            if c != '#' :
                bz.append(c) # 模拟入栈
            elif len(bz) > 0: # 栈非空才能弹栈
                bz.pop() # 模拟弹栈
        return str(bz)

    def backspaceCompare(self, s: str, t: str) -> bool:
        return self.get_string(s) == self.get_string(t)
        pass
class Solution:
    def backspaceCompare(self, s: str, t: str) -> bool:
        s_index, t_index = len(s) - 1, len(t) - 1
        s_backspace, t_backspace = 0, 0 # 记录s,t的#数量
        while s_index >= 0 or t_index >= 0: # 使用or,以防长度不一致
            while s_index >= 0: # 从后向前,消除s的#
                if s[s_index] == '#':
                    s_index -= 1
                    s_backspace += 1
                else:
                    if s_backspace > 0:
                        s_index -= 1
                        s_backspace -= 1
                    else:
                        break
            while t_index >= 0: # 从后向前,消除t的#
                if t[t_index] == '#':
                    t_index -= 1
                    t_backspace += 1
                else:
                    if t_backspace > 0:
                        t_index -= 1
                        t_backspace -= 1
                    else:
                        break
            if s_index >= 0 and t_index >= 0: # 后半部分#消除完了,接下来比较当前位的值
                if s[s_index] != t[t_index]:
                    return False
            elif s_index >= 0 or t_index >= 0: # 一个字符串找到了待比较的字符,另一个没有,返回False
                return False
            s_index -= 1
            t_index -= 1
        return True

我的代码(当天晚上理解后自己编写)

129. 求根节点到叶节点数字之和

未看解答自己编写的青春版

挺简单的,但是递归结束条件,一开始写错了。

class Solution:
    def sumNumbers(self, root: Optional[TreeNode]) -> int:
        self.res = 0
        if root == None :
            return 0
        if root.left == None and root.right == None :
            return root.val
        level = [str(root.val)]
        self.digui(root,level)

        return self.res

    def digui(self,root,level):
       # 注意这里的递归结束条件,是叶子节点,不是None
        if root.left == None and root.right == None:
            self.res += int(''.join(level))
           
            return
        
        if root.left :
            level.append(str(root.left.val))
            self.digui(root.left,level)
            level.pop()

        if root.right :
            level.append(str(root.right.val))
            self.digui(root.right,level)
            level.pop()

重点

本题主要注意的就是:递归结束条件,遇到叶子节点则返回。

代码随想录的代码

class Solution:
    def sumNumbers(self, root: TreeNode) -> int:
        res = 0
        path = []
        def backtrace(root):
            nonlocal res
            if not root: return # 节点空则返回
            path.append(root.val)
            if not root.left and not root.right: # 遇到了叶子节点
                res += get_sum(path)
            if root.left: # 左子树不空
                backtrace(root.left)
            if root.right: # 右子树不空
                backtrace(root.right)
            path.pop()

        def get_sum(arr):
            s = 0
            for i in range(len(arr)):
                s = s * 10 + arr[i]
            return s

        backtrace(root)
        return res

我的代码(当天晚上理解后自己编写)

1382.将二叉搜索树变平衡

未看解答自己编写的青春版

先将二叉搜索树,中序遍历为有序数组,再利用有序数组,构造二叉平衡搜索树

class Solution:
    def balanceBST(self, root: TreeNode) -> TreeNode:
        if root == None :
            return None
        if root.left == None and root.right == None :
            return root
        self.nums = []
        self.get_num(root)
        head = self.compute_tree(self.nums)
        return head
        

    def get_num(self,root):
        if root == None:
            return
        self.get_num(root.left)
        self.nums.append(root.val)
        self.get_num(root.right)

    def compute_tree(self,nums):
        if len(nums) == 0:
            return None
        n = len(nums)
        middle = n//2
        value = nums[middle]
        node = TreeNode(value)
        left = self.compute_tree(nums[:middle])
        right = self.compute_tree(nums[middle+1:])
        node.left = left
        node.right = right
        return node

重点

代码随想录的代码

class Solution:
    def balanceBST(self, root: TreeNode) -> TreeNode:
        res = []
        # 有序树转成有序数组
        def traversal(cur: TreeNode):
            if not cur: return
            traversal(cur.left)
            res.append(cur.val)
            traversal(cur.right)
        # 有序数组转成平衡二叉树
        def getTree(nums: List, left, right):
            if left > right: return 
            mid = left + (right -left) // 2
            root = TreeNode(nums[mid])
            root.left = getTree(nums, left, mid - 1)
            root.right = getTree(nums, mid + 1, right)
            return root
        traversal(root)
        return getTree(res, 0, len(res) - 1)

我的代码(当天晚上理解后自己编写)

100. 相同的树

未看解答自己编写的青春版

写是写出来了,但是内存占用过高?

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

重点

本题的迭代法,有坑,要多练

代码随想录的代码

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

本题的迭代法注意:不能像遍历那里一样,空节点不入栈,这里空节点必须入栈,然后加判断加continue

# 迭代法
class Solution:
    def isSameTree(self, p: TreeNode, q: TreeNode) -> bool:
        if not p and not q: return True
        if not p or not q: return False
        que = collections.deque()
        que.append(p)
        que.append(q)
        while que:
            leftNode = que.popleft()
            rightNode = que.popleft()
            # 这句话太关键了,必须加判断空节点和continue
            if not leftNode and not rightNode: continue 
            if not leftNode or not rightNode or leftNode.val != rightNode.val: return False 
            # 不能像之前的写法一样,空节点不入栈
            que.append(leftNode.left)
            que.append(rightNode.left)
            que.append(leftNode.right)
            que.append(rightNode.right)
        return True

我的代码(当天晚上理解后自己编写)

116. 填充每个节点的下一个右侧节点指针

未看解答自己编写的青春版

这道题和之前做过的那道题略有不同,这道题要求只能使用常量级的存储空间,说白了就是明着告诉你:给老子用递归写!不能像之前的题目一样使用层序遍历了。

但是用递归,就没什么思路了,明显就是层序遍历的模板题,怎么用递归?

重点

在这里插入图片描述

if (cur->left) cur->left->next = cur->right; // 操作1
if (cur->right) {
    if (cur->next) cur->right->next = cur->next->left; // 操作2
    else cur->right->next = NULL;
}

代码随想录的代码

# 递归法
class Solution:
    def connect(self, root: 'Node') -> 'Node':
        def traversal(cur: 'Node') -> 'Node':
            if not cur: return []
            if cur.left: cur.left.next = cur.right # 操作1
            if cur.right:
                if cur.next:
                    cur.right.next = cur.next.left # 操作2
                else:
                    cur.right.next = None
            traversal(cur.left) # 左
            traversal(cur.right) # 右
        traversal(root)
        return root

这道题是经典,之前没做过类似的!

我的代码(当天晚上理解后自己编写)

52. N皇后II

未看解答自己编写的青春版

class Solution:
    def totalNQueens(self, n: int) -> int:
        if n == 1 :
            return 1
        self.res = 0
        level = []
        self.backtracking(level,n)
        return self.res


    def backtracking(self,level,n):
        
        m = len(level)
        if m >= n :
            print(level)
            self.res += 1
            return
        row = m
        for i in range(n):
            col = i
            level.append([row,col])
            # 下面那里,参数要传入 m+1 , 因为level已经加入一个数值了,Debug了好久
            if self.is_right(level,n,m+1):        
                self.backtracking(level,n)
                level.pop()
            else :
                level.pop()


    def is_right(self,level,n,m):
        for i in range(m):
            for j in range(i+1,m):
                if level[i][1]==level[j][1] :
                    return False
                if abs((level[i][1]-level[j][1])/(level[i][0]-level[j][0])) == 1 :
                    return False
        return True

重点

同N皇后。

代码随想录的代码

没有Python的代码。

我的代码(当天晚上理解后自己编写)

649. Dota2 参议院

未看解答自己编写的青春版

没写出来,这道题还是很有难度的。

重点

没想到用一个循环来控制轮数。

这道题真的很妙

代码随想录的代码

class Solution:
    def predictPartyVictory(self, senate: str) -> str:
        # R = true表示本轮循环结束后,字符串里依然有R。D同理
        R , D = True, True

        # 当flag大于0时,R在D前出现,R可以消灭D。当flag小于0时,D在R前出现,D可以消灭R
        flag = 0

        senate = list(senate)
        while R and D: # 一旦R或者D为false,就结束循环,说明本轮结束后只剩下R或者D了
            R = False
            D = False
            for i in range(len(senate)) :
                if senate[i] == 'R' :
                    if flag < 0: senate[i] = '0' # 消灭R,R此时为false
                    else: R = True # 如果没被消灭,本轮循环结束有R
                    flag += 1
                if senate[i] == 'D':
                    if flag > 0: senate[i] = '0'
                    else: D = True
                    flag -= 1
        # 循环结束之后,R和D只能有一个为true
        return "Radiant" if R else "Dire"

我的代码(当天晚上理解后自己编写)

1221. 分割平衡字符串

未看解答自己编写的青春版

class Solution:
    def balancedStringSplit(self, s: str) -> int:
        res = 0
        s = list(s)
        n = len(s)
        i = 0
        count = 0
        
        
        while i < n :
            if s[i] == 'R':
                count += 1
            else :
                count -= 1
            if count == 0 :
                res += 1
            i += 1
            
        return res

重点

贪心就好,这道题一开始被示例迷惑了,都是对称的字符串,但是一般情况是,不对称的字符串,比如 “RRLRLL”

代码随想录的代码

class Solution:
    def balancedStringSplit(self, s: str) -> int:
        diff = 0 #右左差值
        ans = 0
        for c in s:
            if c == "L":
                diff -= 1
            else:
                diff += 1
            if diff == 0:
                ans += 1
        return ans

我的代码(当天晚上理解后自己编写)

5. 最长回文子串

这种题到现在还是不会写,看着答案写一写

class Solution:
    def longestPalindrome(self, s: str) -> str:
        n = len(s)
        if n <= 1 :
            return s

        dp = [[False]*n for _ in range(n)]
        maxd = 0
        left = 0
        right = 0
        for i in range(n-1,-1,-1):
        # 注意这里 j 的取值范围,是从i开始的,包括i,因为dp[i][j]定义的是[i,j]闭区间
        # 所以 i=j 是可行的,也是必须要考虑的
            for j in range(i,n):
                if s[j]==s[i]:
                    if j-i <= 1 :
                        dp[i][j]=True
                    # 一定注意这里是 elif 而不是 if , if 会出现数组越界
                    elif dp[i+1][j-1] == True :
                        dp[i][j]=True
                if dp[i][j] and j-i+1 > maxd :
                    maxd = j-i+1
                    left = i
                    right = j
        return s[left:right+1]
class Solution:
    def longestPalindrome(self, s: str) -> str:
        n = len(s)
        if n <= 1 :
            return s

        start = 0
        end = 0
        for i in range(n):
            left,right = self.find_point(i,i,s)
            start,end = self.compare(end,start,right,left)

            left,right = self.find_point(i,i+1,s)
            # 注意这里,第二次比较,其实这次比较的,已经是前面,以单字符为中心的最大结果了
            # 即是上一次的 left 和 right 结果,和这次的 left right 进行比较
            start,end = self.compare(end,start,right,left)

        return s[start:end]

        

    def find_point(self,i,j,s):
        while i >= 0 and j < len(s) and s[i]==s[j] :
            i -= 1
            j += 1
        return i+1,j


    def compare(self,a,b,c,d):
        if a-b > c-d :
            return b,a
        else :
            return d,c

重点

对于回文子串的题,要学会这种动态规划的写法,dp[i][j],代表 [ i , j ] 的子串,是否为回文串。这里一定要注意,这里有一个循环不变量,关系着代码的编写,注意这里是,左闭右闭区间!

注意处理三种情况,数值相等,那么下标相等或者相邻,都是回文;数值相等,如果里面也是回文,则是回文。

一定要注意,第三种情况,和前两种情况的关系,一定是 elif ,而不是 if , if 会出现数组越界。

在这里插入图片描述
在这里插入图片描述

本题的双指针法也值得学习

代码随想录的代码

class Solution:
    def longestPalindrome(self, s: str) -> str:
        dp = [[False] * len(s) for _ in range(len(s))]
        maxlenth = 0
        left = 0
        right = 0
        for i in range(len(s) - 1, -1, -1):
            for j in range(i, len(s)):
                if s[j] == s[i]:
                    if j - i <= 1 or dp[i + 1][j - 1]:
                        dp[i][j] = True
                if dp[i][j] and j - i + 1 > maxlenth:
                    maxlenth = j - i + 1
                    left = i
                    right = j
        return s[left:right + 1]
class Solution:
    def longestPalindrome(self, s: str) -> str:

        def find_point(i, j, s):
            while i >= 0 and j < len(s) and s[i] == s[j]:
                i -= 1
                j += 1
            return i + 1, j

        def compare(start, end, left, right):
            if right - left > end - start:
                return left, right
            else:
                return start, end

        start = 0
        end = 0
        for i in range(len(s)):
            left, right = find_point(i, i, s)
            start, end = compare(start, end, left, right)

            left, right = find_point(i, i + 1, s)
            start, end = compare(start, end, left, right)
        return s[start:end]

我的代码(当天晚上理解后自己编写)

132. 分割回文串 II

自己看着解答写的不优秀代码

class Solution:
    def minCut(self, s: str) -> int:

        n = len(s)
        if n <= 1 :
            return 0

        dp = [[False]*n for _ in range(n)]
        for i in range(n-1,-1,-1):
            for j in range(i,n):
                if s[i]==s[j]:
                    if j-i <= 1:
                        dp[i][j]=True
                    elif dp[i+1][j-1]==True:
                        dp[i][j]=True

        DP = [inf]*n
        DP[0]=0
        for i in range(1,n):
            # 这里的if在我这里,应该是不能去掉的
            # 这里我是根据debug才发现的,解答里也是这么写的,要先进行一个判断,不然DP[i]最小就是 1 了。
            if dp[0][i] == True :
                DP[i] = 0
            else :       
                # 这里的下标也有一些混乱,我一直想着,dp的含义是[i,j]
                # DP的含义是[0,j]是回文串的最小分割
                # 始终明确数组的区间是否是闭的
                for j in range(i):
                    if dp[j+1][i] == True :
                        DP[i] = min(DP[i],DP[j]+1)

        return DP[n-1]


重点

本题的重点主要有两个:DP数组的含义,dp[i] : 范围是 [ 0 , i ] 的回文子串,最小分割次数是 dp[i] 。递推公式的话,就是去遍历每一个当前index前面的值,如果 [ i , j ] 是回文串,就是 dp[j] = dp[i] + 1。

那么怎么知道 [ i , j ] 是不是回文串呢 ? 其实就是前一题的代码,一模一样,先用前一题的DP方法,把记录所有子串的回文情况的DP数组,先计算出来。

代码随想录的代码

要学习代码的细节!

class Solution:
    def minCut(self, s: str) -> int:

        isPalindromic=[[False]*len(s) for _ in range(len(s))]

        for i in range(len(s)-1,-1,-1):
            for j in range(i,len(s)):
                if s[i]!=s[j]:
                    isPalindromic[i][j] = False
                elif  j-i<=1 or isPalindromic[i+1][j-1]:
                    isPalindromic[i][j] = True

        # print(isPalindromic)
        dp=[sys.maxsize]*len(s)
        dp[0]=0

        for i in range(1,len(s)):
            if isPalindromic[0][i]:
                dp[i]=0
                continue
            for j in range(0,i):
                if isPalindromic[j+1][i]==True:
                    dp[i]=min(dp[i], dp[j]+1)
        return dp[-1]

我的代码(当天晚上理解后自己编写)

673. 最长递增子序列的个数

未看解答自己编写的青春版

不会。

这题记录一下吧,没心情看了,思路还没看懂,本题也很重要

重点

本题是第一次接触,同时维护两个数组的动态规划的题目。
在这里插入图片描述
在这里插入图片描述

代码随想录的代码

class Solution:
    def findNumberOfLIS(self, nums: List[int]) -> int:
        size = len(nums)
        if size<= 1: return size

        dp = [1 for i in range(size)]
        count = [1 for i in range(size)]

        maxCount = 0
        for i in range(1, size):
            for j in range(i):
                if nums[i] > nums[j]:
                    if dp[j] + 1 > dp[i] :
                        dp[i] = dp[j] + 1
                        count[i] = count[j]
                    elif dp[j] + 1 == dp[i] :
                        count[i] += count[j]
                if dp[i] > maxCount:
                    maxCount = dp[i];
        result = 0
        for i in range(size):
            if maxCount == dp[i]:
                result += count[i]
        return result;

我的代码(当天晚上理解后自己编写)

657. 机器人能否返回原点

未看解答自己编写的青春版

重点

用坐标模拟就好。

代码随想录的代码

# 时间复杂度:O(n)
# 空间复杂度:O(1)
class Solution:
    def judgeCircle(self, moves: str) -> bool:
        x = 0 # 记录当前位置
        y = 0
        for i in range(len(moves)):
            if (moves[i] == 'U'):
                y += 1
            if (moves[i] == 'D'):
                y -= 1
            if (moves[i] == 'L'):
                x += 1
            if (moves[i] == 'R'):
                x -= 1
        return x == 0 and y == 0

我的代码(当天晚上理解后自己编写)

剩下的几道题都很难,都是没接触过的题,后面再单开一个文章记录。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/786638.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

react native远程调试js(无法打开)

解决方案一&#xff1a; 因为chrome inspect需要加载 https://chrome-devtools-frontend.appspot.com 上的资源&#xff0c;所以需要FQ。 GoogleChrome/ADBPlugin#14 解决方案二&#xff1a; 编辑hosts文件&#xff0c;添加&#xff1a; 61.91.161.217 chrome-devtools-f…

【Seata】微服务集成seata

文章目录 1、Seata介绍2、Seata架构3、部署TC服务4、微服务集成seata 1、Seata介绍 Seata是 2019 年 1 月份蚂蚁金服和阿里巴巴共同开源的分布式事务解决方案。 官网http://seata.io/ 2、Seata架构 Seata事务管理有三个角色&#xff1a; TC (Transaction Coordinator) - 事务…

使用rt-thread Studio下载固件时出现Unable to enter Isp mode

根据 我发现我缺了图中的文件夹 解决方法: 在rt-thread studio的sdk管理包中下载

Java阶段五Day12

Java阶段五Day12 文章目录 Java阶段五Day12问题解析顺序消息事务消息 Rocket核心概念KeysTags Springboot整合RocketMQ案例使用准备案例环境生产端发送消息消费端&#xff08;push&#xff09;异步下单操作Business生产端Order消费端Order-adapter整合 rocketmq消费逻辑步骤获取…

【Spring Boot丨(11 )】json的集成

集成JSON 概述JacksonGsonJSON-B 主页传送门&#xff1a;&#x1f4c0; 传送 概述 Spring boot 提供了三种json库的集成&#xff1a; GsonJacksonJSON-B 上述三种库提供了将Java对象转换为JSON字符串以及将JSON字符串转换为Java对象的功能。 其中Jackson 是 Spring Boot 官方…

IDEA常用高效开发工具—screw一键生成数据库文档(仅需三步)

1.配置 引入screw核心... <!-- screw核心 --> <dependency><groupId>cn.smallbun.screw</groupId><artifactId>screw-core</artifactId><version>1.0.3</version> </dependency><!-- HikariCP --> <dependency…

Spring Boot 缓存 Cache 入门

Spring Boot 缓存 Cache 入门 1.概述 在系统访问量越来越大之后&#xff0c;往往最先出现瓶颈的往往是数据库。而为了减少数据库的压力&#xff0c;我们可以选择让产品砍掉消耗数据库性能的需求。 当然也可以引入缓存,在引入缓存之后&#xff0c;我们的读操作的代码&#xff…

考了个试,我扯下了理论式数据治理的遮羞布

事情要从2023年618CDGP考试说起 ...... 在全国人民都在欢天喜地剁手的时候&#xff0c;没错&#xff0c;我正在紧张的进行2023年第3期的CDGP考试。 而7月7日&#xff0c;就是放榜的日子。以dama中国的尿性&#xff0c;都是卡在第三周周五的最后一刻才会放榜。于是&#xff0…

SQL SUM() 函数

SUM() 函数返回数值列的总数。 SQL SUM() 语法&#xff1a; SELECT SUM(column_name) FROM table_name WHERE condition; column_name 是要计算总和的列名。 table_name 是包含要计算总和的列的表的名称。 WHERE 子句可选&#xff0c;用于指定要计算总和的行的条件。 演示…

动量定理不愧是大师都在推荐使用的交易策略

动量定理对交易策略的重要性不言而喻&#xff0c;许多交易大师都在推荐使用。Forexclub认为它可以通过观察趋势的持续时间来预测价格走势&#xff0c;使用振荡器来确定趋势支点&#xff0c;这个振荡器比标准振荡器更快&#xff0c;能够提前给出买卖信号。该振荡器由两条线组成&…

【Vue】vue3 v-draggable 拖拽指令封装

说明 需求&#xff1a;实现一个拖拽指令&#xff0c;可在父元素区域任意拖拽元素&#xff0c;同时如果传入的值为 father&#xff0c;则拖拽的时候以父元素为拖拽对象 思路&#xff1a; 1、设置需要拖拽的元素为absolute&#xff0c;其父元素为relative。 2、鼠标按下(onmous…

最新MPAS跨尺度、可变分辨率模式

跨尺度预测模式&#xff08;The Model for Prediction Across Scales - MPAS&#xff09;是由洛斯阿拉莫斯实验室和美国国家大气研究中心(NCAR)共同开发&#xff0c;其由3个部分组成&#xff0c;分别称为 MPAS-A&#xff08;大气模型&#xff09;、MPAS-O&#xff08;海洋模型&…

观察者模式(java)

目录 结构 案例 代码实现 抽象观察者 抽象主题类 具体观察者 具体主题类 测试类 优缺点 优点 缺点 结构 在观察者模式中有如下角色&#xff1a; Subject&#xff1a;抽象主题&#xff08;抽象被观察者&#xff09;&#xff0c;抽象主题角色把所有观察者对象保存在一个…

C语言每天一练----输出水仙花数

题目&#xff1a;请输出所有的"水仙花数" 题解&#xff1a;所谓"水仙花数"是指一个3位数,其各位数字立方和等于该数本身。 例如, 153是水仙花数, 因为153 1 * 1 * 1 5 * 5 * 5 3 * 3 * 3" #define _CRT_SECURE_NO_WARNINGS 1#include <stdio.h&g…

【Spring】ApplicationEventPublisher 发布订阅模式

概念 关于发布订阅这个词&#xff0c;其实不仅仅出现在Spring框架当中&#xff0c;其实在Redis中也有存在&#xff08;其对应的是convertAndSend()方法&#xff09;&#xff0c;还有在MQ消息队列里也是有的&#xff0c;但这里就主要介绍的是关于Spring框架的ApplicationEventPu…

数据库管理-第九十四期 19c OCM之路-第四堂(02)(20230725)

第九十四期 19c OCM之路-第四堂&#xff08;02&#xff09;&#xff08;20230725&#xff09; 第四堂继续&#xff01; 考点3&#xff1a;SQL statement tuning SQL语句调优 收集Schema统计信息 exec dbms_stats.gather_schems_stats(HR);开启制定表索引监控 create index…

IDEA+SpringBoot + Mybatis + Shiro+Bootstrap+Mysql资产设备管理系统

IDEASpringBoot Mybatis ShiroBootstrapMysql资产设备管理系统 一、系统介绍1.环境配置 二、系统展示1. 管理员登录2.用户新增3.用户设置4.岗位管理5. 审批节点6. 人员查询7. 组织设置8. 人员调整9.角色设置10.角色模块映射11.模块设置12.应用模块13.光纤交换机14.服务器15.网…

使用的华为云RDS数据库不小心把数据删了

目录 前言恢复qp文件帮助文档表级时间点恢复删除数据的时候要注意 前言 华为云查数据的时候前面是有个序号的&#xff0c;删除数据的时候不小心把序号看成id了&#xff0c;导致误删数据。 注&#xff1a;图片如果看不清楚可以点击放大观看&#xff01; 恢复qp文件 华为云每天…

centos中修改防火墙端口开放配置

1、直接进入文件修改 vim /etc/sysconfig/iptables 2、添加需要开放的端口 &#xff08;1&#xff09;添加需要开放的单个端口 4001 -A INPUT -m state --state NEW -m tcp -p tcp --dport 4001 -j ACCEPT &#xff08;2&#xff09;添加需要开放的某个网段端口 4001:4020 …

Windows Server 2019 中文版、英文版下载 (updated Jul 2023)

Windows Server 2019 中文版、英文版下载 (updated Jul 2023) Windows Server 2019 Version 1809&#xff0c;2023 年 7 月更新 请访问原文链接&#xff1a;https://sysin.org/blog/windows-server-2019/&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出处。 作者…