leetcode抢救

新版的编辑器真的好难用好难用

day 1

因为实在是惰性太强,付费参加了做题打卡,希望自己接下来足够自觉吧呜呜呜

704. 二分查找

class Solution:
    def search(self, nums: List[int], target: int) -> int:
        if not len(nums):
            return -1
        left, right = 0, len(nums) - 1
        while left <= right:
            middle = left + (right - left) // 2
            if nums[middle] < target:
                left = middle + 1
            elif nums[middle] > target:
                right = middle - 1
            else:
                return middle
        return -1

 写了八百遍了,很简单,不多说

27. 移除元素

写这道题的时候发现自己在大厂面试的时候的强大劣势就是,完全怎么会用最简单的方式解决问题,反而只会用一些奇奇怪怪的方法做,类似于基础都没有开始,就开始fancy了。。

暴力破解:

它要O(1)的时间复杂度,aka只能在原列表上修改,但是写的又多少有点问题:

        for i in range(len(nums) - 1):
            if nums[i] == val:
                nums[i: len(nums)] = nums[i + 1: len(nums)]
        print(nums)

麻木了,问了python老师之后发现是又越界又出错的。这个方法没办法按照示例来做,还是要用到while。说起来那天美团面试也让用空间复杂度O(1)来做,整个人傻掉了。。

i = 0
while i < len(nums):
    if nums[i] == val:
        nums[i: len(nums)] = nums[i + 1: len(nums)]
        i  -= 1
    i += 1
    
return nums

快慢指针:

class Solution:
    def removeElement(self, nums: List[int], val: int) -> int:
        slow, fast = 0, 0
        while fast < len(nums):
            if nums[slow] == val:
                nums[slow] = nums[fast]
                slow += 1
            fast += 1
        
        return slow

 这个是错的因为压根就没有运动这个slow

while fast < len(nums):
    if nums[fast] != val:
        nums[slow] = nums[fast]
        slow += 1
    fast += 1

return slow

然后return这个slow是因为需要的是短的那个

day 2

目前都还比较简单属于在舒适区中,之后怎么样就不太知道了呜呜

目前的想法是重刷这波之后把代码随想录里相关的题也刷了,生活还是比较艰难呜呜

977. 有序数组的平方

上次写这个题还是在去年6月了,又学习了一段时间的代码视频,可以比较清晰的看出来一些了,这道题用双指针的话属于是变形版的merge sort,比较简单

打脸了,发现这道题要从大到小排,然后再反向输出:

class Solution:
    def sortedSquares(self, nums: List[int]) -> List[int]:
        left, right = 0, len(nums) - 1
        ans = []
        while left <= right:
            if nums[left] ** 2 <= nums[right] ** 2:
                ans.append(nums[right] ** 2)
                right -= 1
            else:
                ans.append(nums[left] ** 2)
                left += 1
        return ans[::-1]

 209. 长度最小的子数组

不知道是从哪次面试开始觉得自己写暴力破解真的写的很差,所以这次还是先试试暴力破解吧

重要!!!

for i in range(len(nums)):
    for j in range(i + 1, len(nums) + 1): # 因为下面打印的东西左闭右开所以才这样打印
        print(nums[i: j])
class Solution:
    def minSubArrayLen(self, target: int, nums: List[int]) -> int:
        count = float("inf") # 定义无限大的or可以len(nums) + 1since里面都是正整数
        for i in range(len(nums)):
            for j in range(i + 1, len(nums) + 1):
                if sum(nums[i: j]) == target and j - i < count:
                    count = j - i
        if count != float("inf"):
            return count
        else:
            return 0

看了一下代码随想录的方法,他说是移动到target之后left_ bound可以选择右移,这个方案是对的,因为里面都是正整数,但是面试里面其实大多数时候都遇到的是里面有正有负的,然后整个人就会傻掉呜呜呜呜,这个待会儿需要去看一下!!!(但是今天就不写了,主要还是因为我的dp真的学的太太太太拉垮了呜呜)

滑动的双指针解法:

悟性太差了,认真看了一下感觉可能还是有一点动规的影子在里面,就是遍历的是右边界(使用右边界作为care about的条件往左看

这道题的重点在于:是先right_bound往右移,如果sum > target,那么left_bound往左移动,然后while的break条件是和与target的关系(这道题基本还是靠了一下答案,还得再做)

class Solution:
    def minSubArrayLen(self, target: int, nums: List[int]) -> int:
        left, right = 0, 0
        count = 0
        res = len(nums) + 1
        for right in range(len(nums)):
            count += nums[right]
            while count >= target:
                res = min(res, right - left + 1)
                count -= nums[left]
                left += 1
                        
        if res < len(nums) + 1:
            return res
        else:
            return 0

59. 螺旋矩阵 II

这又是一道没做过的呜呜呜呜,这么一看,好嘛果然又不太会,但是谁又是天生啥都会呢,看到小红书上面说200-500道就可以量变引起质变了,那就努力!!等待质变呜呜(以后要正能量,少说呜呜,从明天开始做起)

算了很晚了明天继续吧

class Solution:
    def generateMatrix(self, n: int) -> List[List[int]]:
        matrix = []
        for i in range(n):
            matrix.append([0] * n)
        # 用列表生成式感觉简单很多 nums = [[0] * n for i in range(n)]
        if n % 2 != 0:
            matrix[n // 2][n // 2] = n ** 2 # 这个得写在最前面因为之后n的值改变
        m, n = 0, n - 1
        input_num = 0
        extra_count = 0
        
        while n - m >= 1:
            for i in range(m, n):
                input_num += 1
                matrix[m][i] = input_num
            for i in range(m, n):
                input_num += 1
                matrix[i][n] = input_num
            for i in range(m - extra_count , n - extra_count):
                input_num += 1
                matrix[n][n - i] = input_num
            for i in range(m - extra_count, n - extra_count):
                input_num += 1
                matrix[n - i][m] = input_num
            m += 1
            n -= 1
            extra_count += 1

        return matrix

倒是写完了但是还是得,加个思考分析图

day 3

终于day 3了呜呜呜!现在的状态是丝毫没有尽吾志也,所以还是要努力努力再努力,感觉做出来一道题又好有成就感呀!!!

链表,哈哈哈哈不出意外的话就大概又要反转链表了 

但恕我直言我是真的很讨厌链表题....突然发现现在比刚回来好了太多,那个时候做代码随想录,每天在省图里面哭,无语了真的,永远不要放弃勇敢啊啊啊啊

然后关于listnode的定义函数

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

203. 移除链表元素

这道题好像跟剑指18题一毛一样

用一个cur来做指针走完这个链表,同时为了避免第一个结点就是要删除的结点带来的麻烦所以要做了个dummyhead当head

写着写着发现还有一个坑在如果在cur在最后一位(写写改改之后发现忧虑不存在)

class Solution:
    def removeElements(self, head: Optional[ListNode], val: int) -> Optional[ListNode]:
        dummyhead = ListNode(next = head)
        cur = dummyhead
        while cur.next:
            if cur.next.val == val:
                cur.next = cur.next.next # 因为如果cur.next存在,最差最差cur.next.next也是个null,也就是他依然可以写出来不需要判定cur.next.next是否存在
            else:
                cur = cur.next
        return dummyhead.next

707.设计链表

大晚上的好想吃麦当劳啊...做完就去吃吧呜呜呜呜

206. 反转链表

每一次反转链表的时候都会忘记那三个东西谁先谁后的顺序,然后就抓起了ipad(真是代码拯救者,画画图用ipad还挺方便的

朴素好理解的用一个temp来装载cur.next法:

class Solution:
    def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:
        cur, pre = head, None
        while cur:
            temp = cur.next
            cur.next = pre
            pre = cur
            cur = temp
        
        return pre

比较fancy的一行法:(至今记不清楚该什么顺序)

class Solution:
    def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:
        cur, pre = head, None
        while cur:
            cur.next, pre, cur =  pre, cur, cur.next
            # pre, cur, cur.next = cur, cur.next, pre
            # cur, cur.next, pre = cur.next, pre, cur
        
        return pre

算了,放弃挣扎了,这个方法靠背吧,cur.next, pre, cur 

day 4

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

# 错误解法
class Solution:
    def swapPairs(self, head: Optional[ListNode]) -> Optional[ListNode]:
        # 怎么写感觉都还是要做一个dummyhead
        dummyhead = ListNode(val = 0, next = head)
        cur = dummyhead
        while cur.next.next and cur.next:
            temp = cur.next
            cur.next = cur.next.next
            cur.next.next.next = temp
            temp.next = cur.next.next.next
            cur = cur.next.next
        return dummyhead.next

 就,也不太懂错在哪儿了,但是感觉看了一下答案还是挺简单的...他那边是做了三个变量在完成这个东西,思路就会比较清晰

这道题最大难点可能在正常情况应该是处理1. pre.next, 2. cur_next.next, 3. cur.next,但是实际在代码完成中应该是321的方式

class Solution:
    def swapPairs(self, head: Optional[ListNode]) -> Optional[ListNode]:
        # 怎么写感觉都还是要做一个dummyhead
        dummyhead = ListNode(val = 0, next = head)
        pre = dummyhead
        
        while pre.next and pre.next.next:
            cur, cur_next = pre.next, pre.next.next # 这段需要放在while里面,暂时不知原因
            # pre.next, cur_next.next, = cur_next, cur, # 如果这样写最后就找不到pre.next.next那个值了所以得先赋
            cur.next, cur_next.next, pre.next = cur_next.next, cur, cur_next
            pre = pre.next.next

        return dummyhead.next

这部分先等量变引起质变再说吧

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

这道题跟剑指22题基本一致,不过太久了忘记了呜呜呜呜

暂时想到的方法是双指针法,第一个先走n or n - 1 or whatever,就可以找到倒数第n个了再做一个cur.next = cur.next.next大功告成

一直报AttributeError: 'NoneType' object has no attribute 'next',问了群里是空指针异常报错,aka当前指针指向的位置已经是null了,就没有cur.next了

而且万一要删的是第一位,那就没有办法写了,为了简单起见还是要做个dummyhead(目前发现的是如果做删除操作可能都需要一波dummyhead

下面本来写的cur和pre,为了防止我自己老年痴呆以后不记得了,所以还是用代码随想录的fast,slow好了

class Solution:
    def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]:
        dummyhead = ListNode()
        dummyhead.next = head
        fast, slow = dummyhead, dummyhead
        for i in range(n):
            fast = fast.next
        while fast.next:
            fast = fast.next
            slow = slow.next
        slow.next = slow.next.next
        return dummyhead.next

做出来了!!!(感激大佬呜呜呜

160. 相交链表

和面试2.7一毛一样,之前做过一次就用以前的链接了,果然每做出一道题都对自信心极大的提升,我好棒!!!!!有点想笑就是本来想的周末提前把明天的做了,卷死大家,后来发现原来我周末还在赶之前的

记得是分别把两个链表的尾巴叠上两个链表的头,然后看后面是不是有一样的,但是我的问题是,不知道之后还for不for完,看了一下之前写了很多很精妙的写法,感觉那个时候还是菜菜的主要靠答案大佬,自己写终究还是要回归菜鸟本质哈哈哈哈哈,认真的想了想不管是先计数还是怎么样,时间复杂度都是O(m + n) 

        cur_a, cur_b = headA, headB
        count_a, count_b = 0, 0
        while cur_a.next:
            cur_a = cur_a.next
            count_a += 1
        cur_a = headB
        while cur_b.next:
            cur_b = cur_b.next
            count_b += 1
        cur_b = headA
        if count_a > count_b:
            for i in range(count_a - count_b):
                cur_b = cur_b.next
        elif count_a < count_b:
            for i in range(count_b - count_a):
                cur_a = cur_a.next
        while cur_a:
            if cur_a == cur_b:
                return cur_a
            cur_a, cur_b = cur_a.next, cur_b.next

想了一下还是太懒了不想记一遍数再运行一遍了,可以直接拿个count来收数,然后对比一下运行啥的

142. 环形链表 II

这道题类似于之前环形链表的进阶,因为是要找环的入口,不知道为什么写着写着觉得还是要远离很多有毒的关系比如...我是一个很容易接受到情绪然后放大的人,这样不太好而且吧他们也没有带给我任何东西...so就 

我记得当时做这个环形是做了快慢指针来着,但是如果找入口的话还可以用快慢指针吗,看了一下答案,还是快慢指针,但是公式也太太太太太复杂了....我已经不是小学那个奥数双一的我了💔

认真看完了(果然是被段视频和碎片信息荼毒太深了,需要分几次才能有一整段耐心看完一小段需要费点脑子的内容)

开始写的第一个问题,忘记了没有环的话需要怎么表示了

        # 一些错误写法
        fast, slow = head, head
        while fast != slow:
            fast = fast.next.next
            slow = slow.next
        
        cur, pre = fast, slow
        while cur != pre:
            cur, pre = cur.next, pre.next
        
        return pre

上面这个妥妥报错,然后看了一下答案发现应该是先动,保证not null,(如果null就返回null)z在while里面做条件判断,如果遇到了fast == slow然后再处理后面的操作:

class Solution:
    def detectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]:
        fast, slow = head, head
        while fast and fast.next:
            fast = fast.next.next
            slow = slow.next
            if fast == slow:
                cur, pre = fast, head
                while cur != pre:
                    cur, pre = cur.next, pre.next
                return pre
        return None

嘿嘿嘿嘿嘿出门麦去了

day 6

准备开始哈希了(哈希真的是一点没学,有点怕怕)

看完了介绍,但好像又一点都没有看,最重要的一点点可能是最后:要快速判断一个元素在不在集合里用哈希

242. 有效的字母异位词

第一个想法是做一个字典接s的元素,然后减t的元素,但好像没有那种计数方式,check了一下答案,可以直接输出bool判断

class Solution:
    def isAnagram(self, s: str, t: str) -> bool:
        dict_1 = {}
        dict_2 = {}
        for _ in s:
            if _ not in dict_1:
                dict_1[_] = 1
            else:
                dict_1[_] += 1
        for _ in t:
            if _ not in dict_2:
                dict_2[_] = 1
            else:
                dict_2[_] += 1
        return dict_1 == dict_2

但是感觉代码随想录里面第一个方法做数组然后用ascii码加数据也还挺好的,试一试(答案写的有些复杂,可以稍微简化一下

class Solution:
    def isAnagram(self, s: str, t: str) -> bool:
        record = [0] * 26
        for _ in s:
            record[ord(_) - 97] += 1
        for _ in t:
            record[ord(_) - 97] -= 1
        for i in range(26):
            if record[i] != 0:
                return False
        return True

349. 两个数组的交集

草草看了一下介绍,感觉用set很好,结果看到说set的时间空间复杂度都比较高,得不偿失,本来想的两组数都有1-1000,用字典空间复杂度很高,但是想了一下觉得应该还行

class Solution:
    def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
        dict_ = {}
        res = []
        for num in nums1:
            if num not in dict_:
                dict_[num] = 1
        for num in nums2:
            if num in dict_ and dict_[num] == 1: #因为置0并不能删除,所以需要加一个值的判断
                res.append(num)
                dict_[num] = 0
        return res

202. 快乐数

这道题的逻辑感觉已经有点把我绕晕了...应该是会有递归的

class Solution:
    def isHappy(self, n: int) -> bool:
        def calculate_all_item(n):
            sum = 0
            while n != 0:
                sum += (n % 10) ** 2
                n = n // 10
            return sum
        
        record = []
        
        while True: # 感觉只是为了循环用的
            n = calculate_all_item(n)
            if n == 1:
                return True
            if n in record:
                return False
            else:
                record.append(n) # 这部不能放在n = xxx(n)后面不然会直接return False

写完了,发现并没有递归,就只有普通的while循环 

垂死病中惊坐起,今天醒来认真思考了一下,可以用递归,但是得写在递归里面,如果n!=1的话判断出现过没有,return False,没有的话继续调用自己,n = 1的话,return True的情况

1. 两数之和

这不就是那道“有人相爱,有人夜里开车看海,有人leetcode第一题都做不出来。”吗哈哈哈

完了时隔很久我又不会做了。。。我应该只会最笨的办法了

O(n^2):

class Solution:
    def twoSum(self, nums: List[int], target: int) -> List[int]:
        for i in range(len(nums)):
                for j in range(i + 1, len(nums)):
                    if nums[i] + nums[j] == target:
                        return [i, j]

看了一下讲解,不会是要把所有数先存进去吧

        dict_ = {}
        for i in range(len(nums)):
            dict_[nums[i]] = i

试了一下这样写的一个问题是如果遇到一样的数,那么序号会直接覆盖报错 

答案那个我不会,想的是可能得做一个[].append[]的二维数组,但是发现好像还是不可以,因为没有办法定位第一个数。。。只能用内置函数的办法吧

class Solution:
    def twoSum(self, nums: List[int], target: int) -> List[int]:
        record = {}
        for index, num in enumerate(nums):
            if target - num not in record:
                record[num] = index
            else:
                return [record[target - num], index]

抄了一下get了,那其实做一个字典的方式原则上是可行的,但是是从后往前找,也就是遍历到num这个数,看前面存入字典的是否有满足条件的数。按照这个逻辑又写了一个非内置函数,纯字典的办法:

class Solution:
    def twoSum(self, nums: List[int], target: int) -> List[int]:
        record = {}
        for i in range(len(nums)):
            if target - nums[i] in record:
                return [i, record[target - nums[i]]]
            else:
                record[nums[i]] = i

写完!该去背单词了!

day 7

好的点在于,每一次花时间在学习上,就不会想念wxk...这样也挺好的,加油进步!

454. 四数相加 II

思考了一下如果用纯暴力的方式,时间复杂度是n^4,类似于二分的暴力...可能也有个n^2

class Solution:
    def fourSumCount(self, nums1: List[int], nums2: List[int], nums3: List[int], nums4: List[int]) -> int:
        res = 0
        for i in range(len(nums1)):
            for j in range(len(nums2)):
                for k in range(len(nums3)):
                    for l in range(len(nums4)):
                        if nums1[i] + nums2[j] + nums3[k] + nums4[l] == 0:
                            res += 1
        return res

暴力真就是最简单的,除了...超出时间限制

看了下答案,真的是用二分暴力的方法来做的,惊了,那就是还是n^2

class Solution:
    def fourSumCount(self, nums1: List[int], nums2: List[int], nums3: List[int], nums4: List[int]) -> int:
        dict_ = {}
        ls = []
        res = 0
        for i in range(len(nums1)):
            for j in range(len(nums2)):
                if nums1[i] + nums2[j] not in dict_:
                    dict_[nums1[i] + nums2[j]] = 1
                else:
                    dict_[nums1[i] + nums2[j]] += 1

        for i in range(len(nums3)):
            for j in range(len(nums4)):
                ls.append(nums3[i] + nums4[j])
        
        for i in range(len(ls)):
            if - ls[i] in dict_:
                res += dict_[- ls[i]] # 这里正负不要写错了

        return res

这里多写了一个for,算出来时间复杂度是3n^2,如果是直接在第二个for上做对比可以省为2n^2,刚刚上牛客被吓到了之后打开博客还以为之前的博客掉了,哈哈哈哈还好没有 

383. 赎金信

想法是跟之前某道题一样,做一个26位的数组,在第一个里面每遇到一次减一个,最后for一遍看是不是每个item都是<=0,时间复杂度只有O(n)

class Solution:
    def canConstruct(self, ransomNote: str, magazine: str) -> bool:
        ran_list, mag_list = [0] * 26, [0] * 26
        for ran in ransomNote:
            ran_list[ord(ran) - 97] += 1
        for mag in magazine:
            mag_list[ord(mag) - 97] += 1

        for i in range(26):
            if ran_list[i] - mag_list[i] > 0:
                return False
        return True

答案时间复杂度和这个基本一致,少了n(26),空间复杂度少了n[26]:只要一个[0] * 26,所有操作在上面完成,直接在第二个for上对比(要用小的那个作为基底,大的那个作为第二个for里减的元素,一旦item<0, return False:

class Solution:
    def canConstruct(self, ransomNote: str, magazine: str) -> bool:
        res = [0] * 26
        for mag in magazine:
            res[ord(mag) - 97] += 1

        for ran in ransomNote:
            res[ord(ran) - 97] -= 1
            if res[ord(ran) - 97] < 0:
                return False
        return True

15. 三数之和

这道题我的想法是,做两个列表,一个用来装值,一个用来装[i, j],然后做两个指针指向这两个list,判断一下第三个值在不在这俩列表里吧?写了一下这个方法还是O(n^3)

i和j需要不一样,那j是要做一个间隔的for,这就遇到了上次面试的问题....命途多舛啊,这个急待解决

突然发现是任意顺序三个不能重样,那好像就简单很多了

class Solution:
    def threeSum(self, nums: List[int]) -> List[List[int]]:
        ls = []
        for i in range(len(nums)):
            for j in range(i + 1, len(nums)):
                for k in range(j + 1, len(nums)):
                    if nums[i] + nums[j] + nums[k] == 0 and sorted([nums[i], nums[j], nums[k]]) not in ls:
                        ls.append(sorted([nums[i], nums[j], nums[k]]))
        return ls

朴素暴力终究是不可以,展示一个错误写法:

        for i in range(len(nums) - 2):
            left, right = i + 1, len(nums) - 1
            while right > left:
                if nums[i] + nums[left] + nums[right] == 0:
                    count += 1 # 这样写卡死在这个循环里面了
                elif nums[i] + nums[left] + nums[right] > 0:
                    right -= 1
                else:
                    left += 1

确实还是应该想到很多条件呜呜呜

class Solution:
    def threeSum(self, nums: List[int]) -> List[List[int]]:
        nums = sorted(nums)
        ls = []
        for i in range(len(nums) - 2):
            left, right = i + 1, len(nums) - 1
            # if nums[i] > 0:
            #     break # 一些进阶应该想到的
            if i >= 1 and nums[i] == nums[i - 1]: # i去重
                continue
            while right > left:
                if nums[i] + nums[left] + nums[right] < 0:
                    left += 1
                elif nums[i] + nums[left] + nums[right] > 0:
                    right -= 1
                else:
                    ls.append([nums[i], nums[left], nums[right]])
                    while left != right and nums[left] == nums[left + 1]:
                        left += 1 # 这两部都是去重
                    while left != right and nums[right] == nums[right - 1]:
                        right -= 1
                    left += 1
                    right -= 1
                         
        return ls

大晚上的,头发不是很好看,又很想吃麦当劳没吃(太冷了不想下楼),太生气了 

18. 四数之和
傻掉了,四数之和ii比四数之和i简单这么多...这道题难度看起来就是上一道题的变形。。。无语了

class Solution:
    def fourSum(self, nums: List[int], target: int) -> List[List[int]]:
        if len(nums) < 4:
            return []
        ls = []
        nums = sorted(nums)
        for i in range(len(nums) - 3):
            # 这里注释一下,感觉没有必要在这里去重
            if i > 0 and nums[i] == nums[i - 1]:
                continue
            for j in range(i + 1, len(nums) - 2):
                if j > i + 1 and nums[j] == nums[j - 1]: # j > i + 1非常重要,去重关键!!!
                    continue
                left, right = j + 1, len(nums) - 1
                # if nums[i] + nums[j] > target:
                #     break #这句不应该有,因为如果target为负数,那应该前两个数比target小
                while left < right:
                    if nums[i] + nums[j] + nums[left] + nums[right] < target:
                        left += 1
                    elif nums[i] + nums[j] + nums[left] + nums[right] > target:
                        right -= 1
                    else:
                        ls.append([nums[i], nums[j], nums[left], nums[right]])
                        while left != right and nums[left] == nums[left + 1]:
                            left += 1
                        while left != right and nums[right] == nums[right - 1]:
                            right -= 1
                        left += 1
                        right -= 1
        return ls

很像,但是有了更多需要注意的地方,比如第一个相似的不减,从第二开始去重,比如target的正负也和题目相关,棒!今天可以开始准备睡觉了!!!!

day 8

为什么一天比一天题多啊!!!!

344. 反转字符串

啊这,看了一下题,不是列表吗,为啥叫字符串啊,双指针,手快的话甚至不要一分钟

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 += 1
            right -= 1

541. 反转字符串 II

遇到第二道题整个人傻掉了,完全不会啊

答案上是用了一个类似于:

s = 'asdfg'
for i in range(0, 4, 2):
    print(s[i: i + 2])

的方式,但是问题是它打印只打印出了"as"和"df"...哦我是白痴,应该是(0, 5, 2),那就简单太多了!!

class Solution:
    def reverseStr(self, s: str, k: int) -> str:
        def reverse(ls):
            left, right = 0, len(ls) - 1
            while left < right:
                ls[left], ls[right] = ls[right], ls[left]
                left += 1
                right -= 1
            return ls
            
        res = list(s) # 原来可以直接用这个
        for i in range(0, len(res), 2 * k):
            res[i: i + k] = reverse(res[i: i + k])
        return "".join(res)

其实写的时候右边界就算超过了list的长度,输出最多也就到list的右边(其实还是简单的,一早看错了

剑指 Offer 05. 替换空格

class Solution:
    def replaceSpace(self, s: str) -> str:
        s = s.split(" ")
        return "%20".join(s)

简单写法,复杂一点点的得看看,因为毕竟还是用了好几个内置函数,有些题目吧,嘴上说着空间复杂度O(1),实际上答案里还是加了很多东西,准备trytry真实的O(1)

class Solution:
    def replaceSpace(self, s: str) -> str:
        # if len(s) == 0:
        #     return ""
        # if s[0] == " ":
        #     s = s[1: len(s)]
        # if s[len(s) - 1] == " ":
        #     s = s[0: len(s) - 1]
        # 想的太多了,他说的是每个空格替换成%20就行
        count = 0
        for i in range(len(s)):
            if s[i] == " ":
                count += 1

        # 算了我输了,他还是需要一个list
        s = list(s)
        k = len(s)
        for i in range(count * 2):
            s.append(' ')
        left, right = k - 1, len(s) - 1
        # for i in range(len(s)) 好像这样写怪怪的
        while right >= 0:
            if s[left] != " ":
                s[right] = s[left]
                left -= 1
                right -= 1
            else:
                s[right - 2: right + 1] = ['%', '2', '0']
                left -= 1
                right -= 3
        return "".join(s)

151. 反转字符串中的单词

知道为什么上一道题人傻了,因为看解析看错题号了...

看懂了上一道题空间复杂度是1的方式(就是直接在原数组上"asd" + "zxc")这种,应该就行了嘿嘿嘿嘿

s = "We are happy."

print(s[0: 2: -1]) # 这个是没有办法正常输出的
print(s[0: 2][::-1]) # 得写成这个
class Solution:
    def reverseWords(self, s: str) -> str:
        if len(s) == 0:
            return ""
        while s[0] == " ":
            s = s[1: len(s)]
        while s[len(s) - 1] == " ":
            s = s[0: len(s) - 1]
        k = 0
        while k < len(s): # 这一步是数据中去重
            if s[k] == " " and s[k] == s[k + 1]:
                s = s[:k] + s[k + 1:]
            else:
                k += 1
      
        s = s[::-1]
        left = 0
        i = 0
        for i in range(len(s)):
            if s[i] == " ":
                s = s[0: left] + s[left: i][::-1] + s[i: len(s)]
                left += i - left + 1
            if i == len(s) - 1:
                s = s[0: left] + s[left: len(s)][::-1] # 这里是因为之前只能运行到最后一个空格前
        return s

剑指 Offer 58 - II. 左旋转字符串

用列表做太简单了,不想写,这个还是得上手画一下如果是在原字符串上写的话,就是把左边reverse一下,把右边reverse一下,最后把整个函数reverse一下

class Solution:
    def reverseLeftWords(self, s: str, n: int) -> str:
        s = s[: n][::-1] + s[n: ][::-1]
        s = s[::-1]
        return s

day 9

28. 找出字符串中第一个匹配项的下标

aka so called 实现strStr()

想到的方式,滑动窗口,还有就是更简单的匹配,只判断第一位是不是,是的话再看,先自己写一遍试试吧,不行再说

class Solution:
    def strStr(self, haystack: str, needle: str) -> int:
        k = len(needle)
        for i in range(len(haystack)):
            if haystack[i] == needle[0]:
                if haystack[i: i + k] == needle:
                    return i
        return - 1

看答案,原来kmp就是传闻中的前缀是吗哈哈哈

一些关于前缀和后缀的概念

aabaaf的前缀有: a, aa, aab, aaba, aabaa(不包含尾字母)

aabaaf的后缀有: f, ​​​​​​​af, ​​​​​​​aaf, ​​​​​​​baaf, ​​​​​​​abaaf(不包含首字母)

前缀表:最长相等前后缀就已经看不懂了(最长公共前后缀

a a b a a f

0 1 0 1 2 0

459. 重复的子字符串

day 10

232. 用栈实现队列

这一部分跟之前另外一个一样,注意复用吧,init完之后,所有都要self

还得再做,所有底层实现的东西,感觉自己都很懵

class MyQueue:

    def __init__(self):
        # 做两个就行
        self.stack_in = []
        self.stack_out = []

    def push(self, x: int) -> None:
        self.stack_in.append(x)

    def pop(self) -> int:
        if self.empty():
            return None

        if self.stack_out:
            return self.stack_out.pop()
        else:
            for i in range(len(self.stack_in)):
                self.stack_out.append(self.stack_in.pop())
            return self.stack_out.pop()

    def peek(self) -> int:
        # 这个说实话不是很懂诶,年后得问问
        ans = self.pop()
        self.stack_out.append(ans)
        return ans

    def empty(self) -> bool:
        return not self.stack_in and not self.stack_out

225. 用队列实现栈

总觉得这玩意儿只要一个双端队列就好,痛苦,真的不想看

day 11

20. 有效的括号

这个正常写还行,但是逻辑可能就会很复杂,所以还是照着抄了一下

class Solution:
    def isValid(self, s: str) -> bool:
        stack = []
        mapping = {
            '(': ')',
            '[': ']',
            '{': '}'}
        for item in s:
            if item in mapping.keys():
                stack.append(mapping[item])
            elif not stack or stack[- 1] != item:
                return False
            else:
                stack.pop()
        if not stack: # 最后一步避免stack剩下了左括号
            return True
        else:
            return False

看了一下longlongago自己写的,直接return了len(stack) == 0返回bool也对。

1047. 删除字符串中的所有相邻重复项

写了一下下,又out of range了,啊啊啊啊啊

class Solution:
    def removeDuplicates(self, s: str) -> str:
        stack = []
        stack.append(s[0])
        for i in range(1, len(s)):
            if stack and s[i] == stack[- 1]: #需要有一个判断stack存不存在的那什么
                stack.pop()
            else:
                stack.append(s[i])
        return "".join(stack)

150. 逆波兰表达式求值

已经不太记得逆波兰表达式是什么了,看了一下答案,这个map的方法我是一点不会啊

我真的感觉自己好像已经不太会做题了,一定是因为这几天玩的实在是太迷糊了。。。

class Solution:
    def evalRPN(self, tokens: List[str]) -> int:
        stack = []
        # 这个mapping的方法还是要学一下!!
        mapping = {"+": add, "-": sub, "*": mul, "/": lambda x, y: int(x / y)}
        for token in tokens:
            if token not in {"+", "-", "*", "/"}:
                stack.append(int(token))
            else:
                num2 = stack.pop()
                num1 = stack.pop()
                stack.append(mapping[token](num1, num2))
        return stack.pop()

这个题简单却精妙,我真的好喜欢哈哈哈哈

{"+", "-", "*", "/"}用这个是因为集合里面查找时间复杂度是O(1)

day 12

好像已经掉队很多天了。。还是要努力呀!!!

239. 滑动窗口最大值

其实暴力也不算很难,因为怎么都是O(n*k)的时间复杂度,但是超出时间限制了。

发现自己又理解错题意了

class Solution:
    def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
        left, right = 0, k - 1
        res = []
        while left < len(nums) - k + 1:
            res.append(max(nums[left: right + 1]))
            left += 1
            right += 1
        return res

但是我好像依然还是没有很懂这个题目的单调队列的做法,先做我能想到时间复杂度最低的方法O(2n)吧

以下写法是错的!!!因为只维护了最大的,但是遇到最大的不再在窗口里的时候,还是append了之前的最大值

class Solution:
    def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
        left, right = 0, k - 1
        res = []
        max_num = max(nums[0: k])
        res.append(max_num)
        while left < len(nums) - k:
            left += 1
            right += 1
            max_num = max(max_num, nums[right])
            res.append(max_num)
        return res

347. 前 K 个高频元素

这个暂时也先不做吧,我的stack和queue真的学的很糟糕

day 13

二叉树相关了,摆了n天了,发现自己还是....那个人so先学吧,学完才有精力考虑其他事情

满二叉树:有2^n-1个节点的二叉树,深度n

完全二叉树:节点都集中在最左边

二叉搜索树:左<中<右

平衡二叉搜索树:左右子树高度差不超过1

二叉树两种存储方式:1. 链式存储:左指针,右指针balabala的;2. 数组那种层序式,如果父节点的下标是i,那左右子节点的下标是i*2+1,i*2+2

深度优先:前中后序遍历,递归

广度优先:迭代

还是需要再写一遍二叉树定义

class NodeTree:
    def __init__(self, value):
        self.val = value
        self.left = None
        self.right = None

首先是递归的三种方式二叉树:

一些写递归的方式:1. 返回条件;2. base cases

144. 二叉树的前序遍历

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def preorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        res = []

        def recursion(root):
            if not root:
                return
            res.append(root.val)
            recursion(root.left)
            recursion(root.right)

        recursion(root)
        return res

94. 二叉树的中序遍历

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val):
#         self.val = val
#         self.left = None
#         self.right = None

class Solution:
    def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        res = []

        def recursion(root):
            if not root:
                return
            recursion(root.left)
            res.append(root.val)
            recursion(root.right)

        recursion(root)
        return res

145. 二叉树的后序遍历

class Solution:
    def postorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        res = []
        
        def recursion(root):
            if not root:
                return
            recursion(root.left)
            recursion(root.right)
            res.append(root.val)

        recursion(root)
        return res

so easy没啥写头的一堆。

迭代遍历,真想当个基础不好的录友,但是又怕找不到工作,理论上所有递归的问题都可以用栈来实现。还是这三道题,carl讲的是用栈来先入后出,看弹幕说可以用fifo,觉得有道理

144. 二叉树的前序遍历

前序可能是最简单的了,因为就是先处理中间的,然后用一个值来记录弹出,再append进右、左结点 

class Solution:
    def preorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        if not root:
            return []
        stack = [root]
        res = []
        while stack:
            node = stack.pop()
            res.append(node.val)
            if node.right:
                stack.append(node.right)
            if node.left:
                stack.append(node.left)
        return res

94. 二叉树的中序遍历

这个中序遍历的方法其实还是比较好理解,先遍历到最低的那个节点,然后如果是左子树不存在,那就弹出栈里的元素,然后判断右子树在不在,如果在的话append到栈里,不在的话继续判断处理栈里的元素

class Solution:
    def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        if not root:
            return []
        stack = []
        res = []
        cur = root
        while cur or stack:
        # while cur and stack: 应该是两个都不存在的时候才会结束迭代
            if cur:
                stack.append(cur)
                # stack.append(cur.val) 这个地方不可以是cur.val因为这样就cur.right不了
                cur = cur.left
            else:
                cur = stack.pop()
                res.append(cur.val)
                cur = cur.right
        return res

145. 二叉树的后序遍历

完了距离上次写前序已经是两三天以前的事情了,已经逐渐忘记了哈哈哈哈哈得看看前序是咋写的,复习了之后,第一想法是翻转,但是觉得翻转好像违背了一开始的想法了,结果认真的review了一下下,结果发现真的就是翻转呜呜呜大意了

class Solution:
    def postorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        if not root:
            return []
        stack = [root]
        res = []
        while stack:
            node = stack.pop()
            res.append(node.val)
            # 这个地方因为要反过来所以是先入左子树
            if node.left:
                stack.append(node.left)
            if node.right:
                stack.append(node.right)
        return res[::-1]

好家伙写完迭代,发现还有个统一迭代哈哈哈哈,多少有点无奈,这里暂时不想写了,下一个吧,其他的也够用了

二叉树的统一迭代法

day 14

这三道之前都做过,可以直接用之前的链接

剑指 Offer 32 - II. 从上到下打印二叉树 II

哈哈哈哈层序,做不来了,我好菜菜哈哈哈哈

看了一眼,carl的层序是用queue来做的,层序感觉确实是很久没写过了呜呜呜呜呜

class Solution:
    def levelOrder(self, root: TreeNode) -> List[List[int]]:
        res = []
        if not root:
            return res
        from collections import deque
        queue = deque([root])
        while queue:
            res_ = []
            size = len(queue)
            for item in range(size):
                cur = queue.popleft()
                res_.append(cur.val)
                if cur.left:
                    queue.append(cur.left)
                if cur.right:
                    queue.append(cur.right)
            res.append(res_)
        return res

剑指 Offer 27. 二叉树的镜像

层层反转就行了...我的令人无语的朋友又开始了,搞得人压力好大啊。。。这个题搞得让人觉得,多少有点迷惑

开写while之后才意识到应该用递归就行了哈哈哈哈哈

class Solution:
    def mirrorTree(self, root: TreeNode) -> TreeNode:
        if not root:
            return
        root.left, root.right = root.right, root.left
        self.mirrorTree(root.left)
        self.mirrorTree(root.right)

        return root

剑指 Offer 28. 对称的二叉树

class Solution:
    def isSymmetric(self, root: TreeNode) -> bool:
        def isbalance(left, right):
            if not left and right:
                return False
            elif not right and left:
                return False
            elif not right and not left:
                return True
            elif left.val != right.val: # 这个必须放在判定为空处理完之后
                return False
            else:
                return isbalance(left.left, right.right) and isbalance(left.right, right.left)
        
        if not root:
            return True
        else:
            return isbalance(root.left, root.right)
        # return True if not root else isbalance(root.left, root.right) 也可以这么写

day 16

剑指 Offer 55 - I. 二叉树的深度

深度和高度是相反的,一些直接截图carl的内容

用前序求深度,用后序求高度

class Solution:
    def maxDepth(self, root: TreeNode) -> int:
        def finddepth(root):
            if not root:
                return 0
            leftdepth = finddepth(root.left)
            rightdepth = finddepth(root.right)
            depth = 1 + max(leftdepth, rightdepth) # 这里一定要+1不然一直是0
            return depth

        return finddepth(root)

但这个方法还是用的后序 

111. 二叉树的最小深度

这道题的难点在于考虑是根结点到最近的叶子结点,aka左边没有左子树的话,不能输出深度是1

然后跟上道题有相似关系,这道题可以用后序遍历求每个叶子结点的高度然后求到最小的高度

class Solution:
    def minDepth(self, root: Optional[TreeNode]) -> int:
        def findmindepth(root):
            if not root:
                return 0
            leftdepth = findmindepth(root.left)
            rightdepth = findmindepth(root.right)
            if not leftdepth and rightdepth:
                return rightdepth + 1
            elif leftdepth and not rightdepth:
                return leftdepth + 1
            else:
                return 1 + min(leftdepth, rightdepth)
            
        return findmindepth(root)

222. 完全二叉树的节点个数

完了,就,记不清楚概念哈哈哈哈,又要来学一遍完全二叉树和平衡二叉树的区别...

完全二叉树:在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层,则该层包含 1~ 2h 个节点。

平衡二叉树:如果某二叉树中任意节点的左右子树的深度相差不超过1,那么它就是一棵平衡二叉树。

正常的情况:

class Solution:
    def countNodes(self, root: Optional[TreeNode]) -> int:
        def findnodenum(root):
            if not root:
                return 0
            leftnodenum = findnodenum(root.left)
            rightnodenum = findnodenum(root.right)
            return leftnodenum + rightnodenum + 1

        return findnodenum(root)

其实按照道理来说简单的层序一遍就可以了,但是如果他这个想要极致的减少时间复杂度,按照carl的一些花里胡哨的写法:

class Solution:
    def countNodes(self, root: Optional[TreeNode]) -> int:
        def countnode(root):
            if not root:
                return 0
            left, right = root.left, root.right
            leftdepth, rightdepth = 1, 1
            while left:
                left = left.left
                rightdepth += 1
            while right:
                right = right.right
                rightdepth += 1
                
            if leftdepth == rightdepth:
                return 2 ** leftdepth - 1
            else:
                return countnode(root.left) + countnode(root.right) + 1
            
        return countnode(root)

day 17

剑指 Offer 55 - II. 平衡二叉树

很久没写过了哈哈哈哈,已经忘记了,然后浅看了一下之前的内容,回忆起来了

用abs(leftdepth - rightdepth)<= 1来判断是否是平衡二叉树,又一次,求高度用后序(从下往上返回),求深度用前序(从上往下所以是中左右)

一个错误写法展示(一不小心直接输出了数字了):

class Solution:
    def isBalanced(self, root: TreeNode) -> bool:
        def getheight(root):
            if not root:
                return 0
            left = getheight(root.left)
            right = getheight(root.right)
            if abs(left - right) > 1:
                return False
            else:
                return 1 + max(left, right)
        
        return getheight(root)

这道题可知的是:

1. 一旦出现abs>1的情况会返回false,其他情况返回数字,但是!!!这他妈要求的是输出一个bool;

2. 还有就是忘记check左右两边是不是也不是-1了

class Solution:
    def isBalanced(self, root: TreeNode) -> bool:
        def getheight(root):
            if not root:
                return 0
            left = getheight(root.left)
            right = getheight(root.right)
            if abs(left - right) > 1:
                return -1
                # return False
            # left, right的check是为了检查是不是左边右边也符合高度差小于1
            elif left == -1:
                return -1
            elif right == -1:
                return -1
            else:
                return 1 + max(left, right)
        
        return getheight(root) != -1

257. 二叉树的所有路径

这道题就是第一次正式讨论回溯了,没看懂carl写的东西,然后重新找了一个视频写了个语法糖来完成这个,哦check了一下好像没有,看了好一会儿,感觉还是没有变化,就还是base case+左右两边的recursion func

这道题得再做一遍其实还是有点点不太熟

class Solution:
    def binaryTreePaths(self, root: Optional[TreeNode]) -> List[str]:
        path = ''
        res = []

        def traversal(root, path):
            path += str(root.val) # 这个+=很关键,因为才能做字符串的加法
            left, right = root.left, root.right
            if not left and not right:
                res.append(path)
            if left:
                traversal(left, path + '->')
            if right:
                traversal(right, path + '->')
        
        if not root:
            return res
        else:
            traversal(root, path)
            return res

404. 左叶子之和 - 力扣(Leetcode)

本来觉得我这道题想法有点瓜,结果看了一下视频我的想法和carl是一致的,哈哈哈哈是不是说明我现在已经具备有效的思维了呢

这道题不知道为什么直接用res传回就不对,然后用

class Solution:
    def sumOfLeftLeaves(self, root: Optional[TreeNode]) -> int:
        def find_left_leaf(root, ls):
            if not root:
                return
            left, right = root.left, root.right
            if left and not left.left and not left.right:
                ls.append(left.val)
                # res += left.val
            if left:
                find_left_leaf(left, ls)
            if right:
                find_left_leaf(right, ls)

        # res = 0
        ls = []
        if not root:
            return 0
        find_left_leaf(root, ls)
        return sum(ls)

关于那个不能直接传出res的方式:

1. 老师的讲解:(不太懂,可能要以后慢慢悟)

你想用 res 作为全局变量去记录 但是每次又传入一个形参res 进递归函数  然后递归函数内改变的只是这个形参res  无法影响到真正的全局变量res

如果还是这种定义全局变量在递归函数中操作的话  可以去掉递归函数的res参数 然后在定义res时候写成 self.res  后面对res的操作也一并修改

class Solution:
    def sumOfLeftLeaves(self, root: Optional[TreeNode]) -> int:
        def find_left_leaf(root, res):
            if not root:
                return
            left, right = root.left, root.right
            if left and not left.left and not left.right:
                self.res += left.val
                # res += left.val
            if left:
                find_left_leaf(left, res)
            if right:
                find_left_leaf(right, res)

        self.res = 0
        # ls = []
        if not root:
            return 0
        find_left_leaf(root, res = self.res)
        return self.res

2. 第二个方式是gyls说的怎么改改,就:

class Solution:
    def sumOfLeftLeaves(self, root: Optional[TreeNode]) -> int:
        def find_left_leaf(root):
            if not root:
                return 0
            left, right = root.left, root.right
            if left and not left.left and not left.right:
                return left.val + find_left_leaf(right)   
            return find_left_leaf(left) + find_left_leaf(right)
            
        if not root:
            return 0
        return find_left_leaf(root)

day 18

513. 找树左下角的值

还是天真了,看错题了呜呜呜呜,想复杂了,看来应该特别简单,因为层序就可以做出来:

class Solution:
    def findBottomLeftValue(self, root: Optional[TreeNode]) -> int:
        from collections import deque
        queue = deque([root])
        ls = []
        while queue:
            add = []
            s = len(queue)
            for i in range(s): # 不能用for _ in queue: 因为里面在变化
                cur = queue.popleft()
                add.append(cur.val)
                if cur.left:
                    queue.append(cur.left)
                if cur.right:
                    queue.append(cur.right)
            ls.append(add)
        
        return ls[-1][0]

这里示例是用一个res来直接接收每个for中i == 0的情况,然后直接return

然后carl写的是还是需要做一下递归,感觉练了这么久的递归还是可以trytry:

然后看了一下讲的,如果是递归的话,基本跟我一开始的想法是一致的,就是需要用一个东西记录一下最大深度,然后返回深度最大的那个叶子结点:

这部分暂时不写吧,再说哈哈哈哈,先过一遍​​​​​​​

112. 路径总和

这道题就还是我们最常见的递归方式,先def一个新函数,然后base case,然后处理左边,然后处理右边(中间的左右无所谓)

class Solution:
    def hasPathSum(self, root: Optional[TreeNode], targetSum: int) -> bool:
        def istarget(root, targetSum):
            # targetSum -= root.val # 这个地方不能这么写
            if not root.left and not root.right:
                if targetSum == 0:
                    return True
                else:
                    return False
            if root.left:
                targetSum -= root.left.val
                if istarget(root.left, targetSum): # 其实这部分得再看看
                    return True
                targetSum += root.left.val
            if root.right:
                targetSum -= root.right.val
                if istarget(root.right, targetSum):
                    return True
                targetSum += root.right.val
            return False #这个地方需要判断一下其他情况是false
            
        if not root:
            return False
        else:
            return istarget(root, targetSum - root.val)

这波暂时只掌握这些吧

113. 路径总和 II

这道题其实和上一道题差不多,感觉很有多相似的情况,注释前后一致

class Solution:
    def pathSum(self, root: Optional[TreeNode], targetSum: int) -> List[List[int]]:

        def istarget(root, targetSum):
            if not root.left and not root.right:
                if targetSum == 0:
                    res.append(path[:])
                    # res.append(path) # 这个就不行,很奇怪
                return
            if root.left:
                path.append(root.left.val) # 应该是一定要这个val的
                targetSum -= root.left.val
                istarget(root.left, targetSum)
                targetSum += root.left.val
                path.pop()
                # path.append(root.left.val)
                # istarget(root.left, targetSum - root.left.val)
                # path.pop()
            if root.right:
                path.append(root.right.val)
                istarget(root.right, targetSum - root.right.val)
                path.pop()
        
        res, path = [], []
        if not root:
            return res
            # return []
        # else:
        path.append(root.val)
        istarget(root, targetSum - root.val)
        return res

关于为什么用append(path[:])而不用append(path),代码随想录抄的:

  

106. 从中序与后序遍历序列构造二叉树

啥也不会,学了一下觉得递归真的是精妙文学啊啊啊啊!!!

recursion还是先base case,然后以中为目标切割左右,然后左边和右边分别recursion,顺便写了一下TreeNode的构造

# class TreeNode:
#     def __init__(self, val):
#         self.val = val
#         self.left = None
#         self.right = None

class Solution:
    def buildTree(self, inorder: List[int], postorder: List[int]) -> Optional[TreeNode]:
        if not postorder: # 本来写的是inorder或postorder为空,突然意识到不会产生这种
            return

        root = TreeNode(postorder[-1]) # 这步在构造里一定一定要写
        root_index = inorder.index(postorder[-1]) # 这个地方是从中序里面找后序最后一个
        
        in_left = inorder[:root_index]
        in_right = inorder[root_index + 1:]
        # postorder里面left和right的长度是跟inorder里一致的
        post_left = postorder[:len(in_left)]
        post_right = postorder[len(in_left): len(postorder) - 1]

        root.left = self.buildTree(in_left, post_left)
        root.right = self.buildTree(in_right, post_right)
        return root

105. 从前序与中序遍历序列构造二叉树

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def buildTree(self, preorder: List[int], inorder: List[int]) -> Optional[TreeNode]:
        if not preorder:
            return

        root = TreeNode(preorder[0])
        root_index = inorder.index(preorder[0])

        in_left = inorder[: root_index]
        in_right = inorder[root_index + 1:]
        pre_left = preorder[1: len(in_left) + 1]
        pre_right = preorder[len(in_left) + 1:] # 这个地方不一样

        root.left = self.buildTree(pre_left, in_left)
        root.right = self.buildTree(pre_right, in_right) # 这个地方注意和参数位置一致
        return root

day 19

654. 最大二叉树

看了一下网上说怎么提高程序员的代码能力的一个帖子,说是还是要先思考步骤,所以以后我每道题还是先想一想吧

感觉像是做了一个中序遍历的样子,那就还是按照之前的例子和自己的想法写一下哈哈哈哈哈(不要怕!!!刚开始做都会觉得难,但是真的不要害怕!!!

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val):
#         self.val = val
#         self.left = None
#         self.right = None
class Solution:
    def constructMaximumBinaryTree(self, nums: List[int]) -> Optional[TreeNode]:
        if not nums:
            return
        # 找到最大值,root定义为这个
        # root = max(nums) # 。。。真的是,第二次犯这个错了哈哈哈
        max_num = max(nums)
        root = TreeNode(max_num)
        root_index = nums.index(max_num)

        # 拆分左子树和右子树
        left_node = nums[: root_index]
        right_node = nums[root_index + 1:]

        # 做左右子树的recursion
        root.left = self.constructMaximumBinaryTree(left_node)
        root.right = self.constructMaximumBinaryTree(right_node)

        # return root
        return root

617. 合并二叉树

感觉这道题像是用两个指针来做,这道题有个点是不能用None + nums,从根节点做的话,那就是用中左右了,想想怎么写吧。。

空间复杂度O(n)的方法:

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def mergeTrees(self, root1: Optional[TreeNode], root2: Optional[TreeNode]) -> Optional[TreeNode]:
        cur1, cur2 = root1, root2
        if not cur1 and not cur2:
            return None
        elif not cur1 and cur2:
            # return cur2.val # 不能返回数值
            return cur2
        elif cur1 and not cur2:
            # return cur1.val # 真的不能返回数值
            return cur1
        # else:
        #     return cur1.val + cur2.val # 这句不需要
        
        root = TreeNode(cur1.val + cur2.val)
        root.left = self.mergeTrees(cur1.left, cur2.left)
        root.right = self.mergeTrees(cur1.right, cur2.right)
        return root

关于这个返回值,因为我最终是要返回一个treenode,所以前面if判断的时候返回的东西不能是int

空间复杂度O(1)的方法:

class Solution:
    def mergeTrees(self, root1: Optional[TreeNode], root2: Optional[TreeNode]) -> Optional[TreeNode]:
        if not root1:
            return root2
        if not root2:
            return root1
        root1.val += root2.val
        root1.left = self.mergeTrees(root1.left, root2.left)
        root1.right = self.mergeTrees(root1.right, root2.right)
        return root1

700. 二叉搜索树中的搜索

好开心啊要学新的内容了呢,找到节点返回他的子树那就是还是中左右了,然后二叉搜索树的概念,左边所有子树的结点值小于根节点,右边所有子树的结点值大于根节点,那也就是说找就需要用到二叉搜索树的概念了,突然意识到直接返回这个结点就可以了诶!!!

class Solution:
    def searchBST(self, root: Optional[TreeNode], val: int) -> Optional[TreeNode]:
        cur = root
        while cur:
            if cur.val < val:
                cur = cur.right
            elif cur.val > val:
                cur = cur.left
            else:
                return cur
        return None

 太太太太简单了。。下一个递归,有一点点不太会这个,看了一下答案是,如果root.val小于val那就递归左边那个,如果root.val大于val那就递归右边那个

class Solution:
    def searchBST(self, root: Optional[TreeNode], val: int) -> Optional[TreeNode]:
        if not root or root.val == val:
            return root
        if root.val > val:
            return self.searchBST(root.left, val) # 这个地方要return啊啊啊!!
        if root.val < val:
            return self.searchBST(root.right, val)

98. 验证二叉搜索树

遇到一个longlongago的题目了,感觉可以凭记忆做一下,btw发现taresa王真的很了解我就是,我真的很喜欢用补进度来证明自己比别人聪明哈哈哈哈哈,最近疯狂赶之前丢下的东西呜呜呜呜!加油加油(我就是比很多人都聪明呀哈哈哈哈

就还是左边所有都需要比根节点小,右边所有都需要比根节点大,但是这样的话逻辑稍微有点绕,得想想,呕心沥血限制了一堆条件还是写错了,错误代码:

class Solution:
    def isValidBST(self, root: Optional[TreeNode]) -> bool:
        if not root: # 错误代码
            return True
        if root.left and root.left.val >= root.val:
            return False
        if root.right and root.right.val <= root.val:
            return False
        if root.left and root.left.right and root.left.right.val >= root.val:
            return False
        if root.right and root.right.left and root.right.left.val <= root.val:
            return False
        return self.isValidBST(root.left) and self.isValidBST(root.right)

看了一下测试用例,管了第二层没有管到第三层,那个下下下层右边的左边还是可能比根节点小,fine看了一下carl的视频,发现一开始就想错了...

1. 原来应该是左中右看是不是输出一个完整的从小到大排序的二叉树就可以了:

class Solution:
    def isValidBST(self, root: Optional[TreeNode]) -> bool:
        ls = []
        def find_node(root):
            if not root:
                return
            find_node(root.left)
            ls.append(root.val)
            find_node(root.right)

        find_node(root)
        for i in range(1, len(ls)):
            if ls[i] <= ls[i - 1]:
                return False
        return True

2. aka二叉树的根节点的值大于左子树的最大值,小于右子树的最小值

糟糕看了好几遍没有看懂这个leftbound和rightbound是怎么赋值的

实在是不知道该怎么做了,就先背一下吧,可能也许之后能会呜呜呜呜

class Solution:
    def isValidBST(self, root: Optional[TreeNode]) -> bool:
        def isBST(root, leftbound, rightbound):
            if not root:
                return True
            elif root.val <= leftbound or root.val >= rightbound:
                return False
            return isBST(root.left, leftbound, root.val) and isBST(root.right, root.val, rightbound)
        
        leftbound = float('-inf')
        rightbound = float('inf')
        return isBST(root, leftbound, rightbound)

day 20

530. 二叉搜索树的最小绝对差

看了两遍题,终于理解对了呜呜呜! 

class Solution:
    def getMinimumDifference(self, root: Optional[TreeNode]) -> int:
        def inorder(root):
            if not root:
                return
            inorder(root.left)
            res.append(root.val)
            inorder(root.right)

        res = []
        inorder(root)
        min_val = float('inf')
        for i in range(1, len(res)):
            min_val = min(min_val, abs(res[i] - res[i - 1]))
        return min_val

501. 二叉搜索树中的众数

唉就...真的爱他但也真的不能因为他自毁前程...所以还是要好好弄,而且如果没有办法从爱情里得到快乐,工作里得到也是可以的!权利是最好的美容药

拿不起又放不下的样子最丑啦~所以还是要好好过

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

day 21

235. 二叉搜索树的最近公共祖先

701. 二叉搜索树中的插入操作

450. 删除二叉搜索树中的节点

day 23

669. 修剪二叉搜索树

108. 将有序数组转换为二叉搜索树

538. 把二叉搜索树转换为累加树

day 24 开始做回溯了

77. 组合

day 25

216. 组合总和 III

17. 电话号码的字母组合

day 26

39. 组合总和

40. 组合总和 II

131. 分割回文串

day 27

93. 复原 IP 地址

78. 子集 - 力扣

90. 子集 II - 力扣

day 28

day 30

day 31

day 32

day 33

day 34

day 35

day 37

day 38

先停一小段时间的二叉树吧,感觉面了腾讯音乐又有点丧,先看看动规,针对华为的笔试,过了的话肯定就又有信心了

然后发现好家伙这个背包问题好像并没有结束啊。。。nb,那还是得多多准备

动规是由前一个状态推导出来的,而贪心是局部直接选最优的

然后一些抄carl的内容:

  1. 确定dp数组(dp table)以及下标的含义
  2. 确定递推公式
  3. dp数组如何初始化
  4. 确定遍历顺序
  5. 举例推导dp数组

dp的debug方式:打印数组

btw,其实大家年后都开始忙起来了,我也没有那么多社交,那时间基本都可以留给自己so是时候为成为天龙人进步了!!!

剑指 Offer 10- I. 斐波那契数列

今晚差不多入个门,按照carl的五步把斐波拉契数写上

1. 确定dp[i]是第i个dp数;

2. 递推公式:dp[i] = dp[i - 1] + dp[i - 2];

3. 如何初始化:dp[0] = 0, dp[1] = 1;

4. 遍历顺序:从前向后;

5. 打印dp数组,主要用来debug

这道题遇到很大的数就出幺蛾子了,看了一下是要取mod

class Solution:
    def fib(self, n: int) -> int:
        if n == 0:
            return 0
        mod = 10 ** 9 + 7
        # mod = float('inf') # 不能用这个,因为会出现小数点

        dp = [0] * (n + 1)
        dp[0], dp[1] = 0, 1
        for i in range(2, n + 1):
            dp[i] = (dp[i - 1] + dp[i - 2]) % mod

        return dp[n]

70. 爬楼梯

这道题再一次迷惑了,为什么dp3 = dp2+dp1是因为,dp3前一步可以是2级台阶,对应dp1,可以是一级台阶对应dp2

1. dp[i] 是达到第i阶的方法有多少种

2. 递推公式:dp[i] = dp[i - 1] + dp[i - 2]

3. dp[0] = 1, dp[1] = 1 # carl这里是初始化的dp1和dp2

4. 从小到大

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

746. 使用最小花费爬楼梯

这道题开始费脑子了,但是既然carl说用他的五步曲可以做出来那就肯定可以,但是感觉前两个最小也不代表之后就一定最小诶

1. dp[i]代表走到该步的最小花费

2. 递推公式:不行,try了一下发现用min的话dp1就还是dp0了就不太对可能也许是

从dp3开始:dp[3] = min(dp[2] + cost[3], dp[1] + cost[2])

3. dp[0] = cost[0], dp[1] = min(cost[0], cost[1]), dp[2] = min(dp[1] + cost[2], cost[1])

4. 从下往上推

不出意外还是out of range了

看了carl的方法,呜呜呜遇到华为的题还是好虚啊我好害怕啊

所以真实的情况是:

1. 递推公式写错了,应该是dp[i] = min(dp[i - 1] + cost[i - 1], dp[i - 2] + cost[i - 2])

2. 初始化做错了,然后发现这个题可以用直接把数值带入的方法做,aka n = 2aka dp[2] = min(cost[0], cost[1]),所以dp[0], dp[1] = 0, 0

class Solution:
    def minCostClimbingStairs(self, cost: List[int]) -> int:
        n = len(cost)
        dp = [0] * (n + 1)
        dp[0], dp[1] = 0, 0
        for i in range(2, n + 1):
            dp[i] = min(dp[i - 1] + cost[i - 1], dp[i - 2] + cost[i - 2])
        return dp[n]

结论就是做这道题之前真的想太多了

day 39

62. 不同路径

二维动态规划,傻眼了,浅看了一下视频,发现还是之前那五步,get到了一点点思路可以开始了哈哈哈哈

1. dp[i][j]是运动到第i行第j列的时候需要的可以有的路径数

2. 递推公式:dp[i][j] = dp[i][j- 1] + dp[i - 1][j]

3. 初始化:????不会了,dp[0][0] = 1

4. 从后往前推吧?但是我不知道怎么限定

好的发现其实自己还是想错了初始化应该是dp[i][0], dp[0][j] = 1, 1

以下这个方法输出对了但是错了!!!!!!

class Solution:
    def uniquePaths(self, m: int, n: int) -> int:
        dp = [[0] * n] * m 
        # dp = [([0] * (n + 1)) * (m + 1)]
        dp[0][0] = 1
        for i in range(m):
            dp[i][0] = 1
        for j in range(n):
            dp[0][j] = 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[m - 1][n - 1] 

因为前拷贝的原因,只是生成的东西是一样的,但是无情复制了所以本质上是错的

还是得用carl的方法来做才对:

class Solution:
    def uniquePaths(self, m: int, n: int) -> int:
        # dp = [[0] * n] * m # 这个方法也不对,因为是浅拷贝了!!!
        dp = [[0 for j in range(n)] for i in range(m)]
        dp[0][0] = 1
        for i in range(m):
            dp[i][0] = 1
        for j in range(n):
            dp[0][j] = 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[m - 1][n - 1] 

二维数组生成的方式还是要记录一下:

dp = [[0 for j in range(n)] for i in range(m)]

63. 不同路径 II

看了一下,学会了

class Solution:
    def uniquePathsWithObstacles(self, obstacleGrid: List[List[int]]) -> int:
        m, n = len(obstacleGrid), len(obstacleGrid[0])
        dp = [[0 for j in range(n)] for i in range(m)]
        if obstacleGrid[0][0] == 1: # 要记得判别[0][0]位置
            return 0
        else:
            dp[0][0] = 1
        for i in range(1, m):
            # if dp[i][0] == 1: # 遍历错矩阵了
            if obstacleGrid[i][0] == 1:
                break
            dp[i][0] = 1
        # for j in range(n): # 这个地方注意第一个位置已经赋值过了所以要跳过
        for j in range(1, n):
            # if dp[0][j] == 1: # 遍历错矩阵了
            if obstacleGrid[0][j] == 1:
                break
            dp[0][j] = 1
        for i in range(1, m):
            for j in range(1, n):
                # if dp[i][j] == 0: # 遍历错矩阵了
                if obstacleGrid[i][j] == 0:
                    dp[i][j] = dp[i - 1][j] + dp[i][j - 1]
        return dp[m - 1][n - 1]

注意点1. 不要遍历错矩阵;2. 千万注意[0][0]位置是否有障碍物

day 40

343. 整数拆分

还是五步曲,但是其实今天的题没有怎么看懂:

1. dp[i]代表加起来是i的数的最大乘积;

2. 递推公式:dp[i] = max(dp[i - j] * j, (i - j) * j, dp[i])

3. dp[0] = 0, dp[1] = 0, dp[2] = 1

4. 大往小推

其实这道题倒懂不懂的,可能得再看看

class Solution:
    def integerBreak(self, n: int) -> int:
        dp = [0] * (n + 1)
        dp[2] = 1
        for i in range(3, n + 1):
            for j in range(1, i // 2 + 1): # 这个地方必须用//因为range里面不可以有哦float
                dp[i] = max(dp[i - j] * j, (i - j) * j, dp[i])
        return dp[n]

96. 不同的二叉搜索树

这道题思路有点点复杂,但是还是比较清晰,btw不管发生什么就是还是得好好过,因为赚钱不少的人,其他也不会很差,呜呜呜呜!想想yrh,再对比一下xxx,我觉得还是高下立见的吧,没关系就是还是像重新养个孩子一样对自己呀,所有需要帮助和照顾的时候都用安全感来填满,就很好了,然后慢慢的一步一步建立安全感,我已经很勇敢了呀!完全没有因为渣男的事情一蹶不振,since我从小其实没有受过什么挫折,我已经足够足够勇敢啦,npd的伤害平均都是2年,我现在已经可以熟练解决伤害了,我已经很棒了呀

而且我很幸运啊,妈妈也愿意慢慢陪我弥补缺失的童年,我也全世界最爱妈妈了呀!那就重新开始吧~

1. dp[i]有i个节点的时候有多少棵二叉搜索树;

2. 递推公式:dp[i] = dp[0] * dp[i - 1] + dp[1] * dp[i - 2] + ... + dp[i - 1] * dp[0]

比较奇特的是carl这个地方写作dp[i] += dp[j - 1] * dp[i - j]

3. dp[0] = 1, dp[1] = 1, dp[2] = 2

4. 从大往小推

class Solution:
    def numTrees(self, n: int) -> int:
        dp = [0] * (n + 1)
        dp[0], dp[1] = 1, 1
        for i in range(2, n + 1):
            for j in range(1, i + 1):
            # for j in range(0, i): # range写错了,因为j-1>=0,i-j>=0
                dp[i] += dp[j - 1] * dp[i - j]
        return dp[n]

 终于!!!要开始!!背包了!!!!

day 41

学到了,真的很精妙!

二维数组的0-1背包

1. dp[i][j]表示的是从0-i中选几个数,然后其重量为j的所有数的最大价值;

2. 递推公式:dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i])

就是说,没有i的时候,总量和为j的数的总和最大价值和有i的情况两情况选个最大的

3. 初始化比较复杂,比如dp[i][0] = 0, dp[0][j](j >= 1)原则上来说=value[0]但如果有weight的限制比如weight限制1,而weight[0] == 2,那还是dp[0][1]只能初始化为0了

4. 遍历方式,对于二维数组的01背包其实先遍历物品还是先遍历背包都可以呜呜

压缩成一维数组的0-1背包

carl说的把上面直接拷贝到当前层,那就相当于,浅拷贝的方法了?

这个好像实际上不需要用那么复杂的东西来看呀,直接看作二维映射到一维,前两个东西就都可以解决了:

1. dp[j] 表示从0-i中选几个数,然后其重量为j的所有数的最大价值;

2. 递推公式:dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);

3. 初始化是dp[0] = 0

4. 这个用的是倒序遍历(至于为什么我现在暂时还是不知道的):

先for 物品,再for背包。

然后开始了解各种背包,只能放进去一次的是0-1背包,可以多次放入的是完全背包

416. 分割等和子集

直接去抄答案去了,都忘记了应该按照动归五步曲来做的事情了呜呜呜!

集合里面每个元素只能使用一次,看装不装得满,可以抽象为一个0-1背包问题

ok其实我现在依然是,不太懂这个背包问题,但是如果他是0-1背包,那看起来就是可以套用之前的模板:

1. dp[j]表示重量为j的所有数的最大值

2. 递推公式:dp[j] = max(dp[j], dp[j - weight[i]] + value[i])

3. 初始化:因为如果后面的数太大,可能会max掉,所以选择最小的数:dp[0] = 0,其他也得为0

4. 先for物品i++,再for背包j--(倒序的背包)

好家伙按照carl的方式写了一遍居然报错了,然后复制carl的代码,发现也有错呜呜呜呜呜呜!世间又多了一个伤心人

class Solution:
    def canPartition(self, nums: List[int]) -> bool:
        if sum(nums) % 2 != 0: # 这个地方可以用作减少一半算力的方式
            return False
        target = sum(nums) // 2
        # dp = [0] * (len(nums) + 1) # 这个是二维里面用的行,而不是列
        dp = [0] * (target + 1)
        for i in range(len(nums)):
            # 倒着来也要从大到小,同时右边界要是后面一位(还是开区间的意思),这个range的意思就是<=target,>=nums[i]
            for j in range(target, nums[i] - 1, -1): 
                dp[j] = max(dp[j], dp[j - nums[i]] + nums[i])
        return dp[target] == target

破案了,还是同学说的😓,这道题的dp的一维长度是target + 1,二维是(len(nums) + 1) * (target + 1) 

终于走出了0-1背包第一步!!!!嘿嘿嘿嘿嘿!!!

day 42

1049. 最后一块石头的重量 II

看了这道题,觉得自己可能有点笨不太懂呜呜呜呜!

哇,carl实在是太太太太太聪明了!!!!直接把target还是切成一半,然后最接近那一半就可以了嗷!!

1. dp[j]是总和为j的最大的石头重量

2. 递推公式:就还是那个老生常谈的:dp[j] = max(dp[j], dp[j - stones[i]] + stones[i])

3. 初始化,还是有target + 1的长度的dp,dp[0] = 0

4. 先遍历背包再遍历倒序的物品

class Solution:
    def lastStoneWeightII(self, stones: List[int]) -> int:
        weight = sum(stones)
        target = weight // 2
        dp = [0] * (target + 1)
        for i in range(len(stones)):
            for j in range(target, stones[i] - 1, -1):
                dp[j] = max(dp[j], dp[j - stones[i]] + stones[i])
        return weight - 2 * dp[target]

太简单了,属于是没有做头的一道题

494. 目标和

目标和这道题,似曾相识哈哈哈

感慨一下,carl真的是很聪明,加集合 + 减集合 = sum,加集合 - 减集合 = target,所以可以直接搞出来加集合,那么这道题对我来说难点就在怎么计数上面了

1. dp[j]代表凑成j的价值有最多dp[j]种方式

2. 递推公式:

因为dp[5] = 已知有一个1,需要dp[4]种方法凑成dp[5] = 已知有一个2,需要dp[3]种方法凑成dp[5] = 已知有一个3,需要dp[2]种方法凑成dp[5] = 已知有一个4,需要dp[1]种方法凑成dp[5] = 已知有一个5,需要dp[0]种方法凑成dp[5](这个地方不是排列组合,是固定的位置,所以是+而不是*)

装满这个背包有多少种方法都是用的这个递推公式

dp[j] += dp[j - nums[i]]

3. 初始化:dp[0] = 1(如果dp[0] = 0,那之后不管怎么加都是0)

4. 遍历顺序:因为是0-1背包,所以先遍历物品,再遍历倒序背包

class Solution:
    def findTargetSumWays(self, nums: List[int], target: int) -> int:
        nums_sum = sum(nums)
        # if (nums_sum - target) % 2 != 0: # 这里的特殊情况写少了,还有sum < target的绝对值
        if (nums_sum - target) % 2 != 0 or abs(nums_sum) < abs(target):
            return 0
        target_ = (nums_sum - target) // 2
        # dp = [1] * (target_ + 1)
        dp = [0] * (target_ + 1)
        dp[0] = 1
        for i in range(len(nums)):
            for j in range(target_, nums[i] - 1, -1):
                dp[j] += dp[j - nums[i]]
        return dp[target_]

​​​​​​​474. 一和零

好家伙这完全无从下笔。。。

这道题怎么着都得定义一个二维的数组了,感觉看了,但是这道题好像不是很懂,大概懂了就是针对数组中每一个字符串再for一遍?

这道题递推公式反而是最简单的了哈哈哈哈,过于狗血:

1. dp[i][j]是完成i个0和j个1的最大数量的数组

2. 递推公式:dp[i][j] = max(dp[i][j], dp[i - x][j - y] + 1)

3. 初始化,全部都是0,因为后面反正要+1,如果改成其他数了反而max有可能被干扰

4. 遍历顺序:两个for都是从后往前推,可能是因为遍历str才算遍历物品,0和1组成的str本身都算背包?

class Solution:
    def findMaxForm(self, strs: List[str], m: int, n: int) -> int:
        dp = [[0 for j in range(n + 1)] for i in range(m + 1)]
        for str in strs:
            num_0 = str.count('0')
            num_1 = str.count('1')
            for i in range(m, num_0 - 1, - 1):
                for j in range(n, num_1 - 1, - 1):
                    dp[i][j] = max(dp[i][j], dp[i - num_0][j - num_1] + 1)
        return dp[m][n]

day 44

学习完全背包的各种:同一件物品使用无数次,可以先遍历背包,再遍历物品,但是呢为了写起来方便还是不要给自己弄这么幺蛾子了吧

518. 零钱兑换 II

1. dp[j]是凑满面值为j的背包需要的面值的凑齐方式有多少种

2. dp[j] += dp[j - coin[i]]

3. 初始化:dp[0] = 1

4. 遍历顺序:其实这个有点点懵,但是可以先背着:

先遍历物品再遍历背包:只有1,2这种组合数的情况

先遍历背包再遍历物品:会出现(1,2)和(2,1)两种情况,排列数

所以这道题(仅限这道题)只需要组合数,那就需要先遍历物品再遍历背包!

class Solution:
    def change(self, amount: int, coins: List[int]) -> int:
        # dp = [1] * (amount + 1)
        # 以后初始化还是老老实实的吧!!
        dp = [0] * (amount + 1)
        dp[0] = 1
        for i in range(len(coins)):
        # for i in range(len(dp)): # 这里是先遍历物品!!
            for j in range(coins[i], amount + 1):
                dp[j] += dp[j - coins[i]]
        return dp[amount]

377. 组合总和 Ⅳ

因为这道题顺序不同就会被视为不同组合,所以可以更刚刚的反着写就行!(得再做一下,感觉就是背了个答案,没有完全懂

1. dp[j] = 总和为目标j的所有数字的组合的数量

2. dp[j] += dp[j - nums[i]]

3. dp[0] = 1

4. 先背包后物品

写了一遍可以说全然有问题,以下是错的方法!!!

class Solution:
    def combinationSum4(self, nums: List[int], target: int) -> int:
        dp = [0] * (target + 1)
        dp[0] = 1
        for j in range(nums[i], target + 1):
            for i in range(len(nums)):
                dp[j] += dp[j - nums[i]]
        return dp[target]

正确的写法:

class Solution:
    def combinationSum4(self, nums: List[int], target: int) -> int:
        dp = [0] * (target + 1)
        dp[0] = 1
        # for j in range(target): # 不可以是这个
        for j in range(1, target + 1):
            for i in range(len(nums)):
                if j >= nums[i]: # 这个是需要target>那个数才行
                    dp[j] += dp[j - nums[i]]
        return dp[target]

day 45

70. 爬楼梯

哇,现在再看这道题,真的觉得很神奇诶,真的可以用完全背包来做!!!算法的世界真的好精彩:

1. dp[j]是组合为n层楼的组合有多少个

2. dp[j] += dp[j - step[i]]

3. dp[0] = 1

4.  还是先遍历背包,再遍历物品

class Solution:
    def climbStairs(self, n: int) -> int:
        dp = [0] * (n + 1)
        dp[0] = 1
        step = [1, 2]
        for j in range(1, n + 1):
            for i in range(2):
                if j >= step[i]:
                    dp[j] += dp[j - step[i]]
        return dp[n]

322. 零钱兑换

这道题跟前面的题区别在于,这道题是求的最小的组成硬币数的情况

1. dp[j]是在组成和为j的硬币的时候的最小硬币数

2. dp[j] = min(dp[j], dp[j - coin[i]] + 1) 这个地方+ 1不能忘记了

3. 这个初始化真的不太会啊,看了一下,初始化应该是dp所有为int('inf'),dp[0] = 0(其实这道题感觉也可以用target + 1)

4. 遍历方式都一样,所以那还是先物品后背包吧,比较习惯这个

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        dp = [amount + 1] * (amount + 1)
        dp[0] = 0
        for i in range(len(coins)):
            for j in range(coins[i], amount + 1):
                dp[j] = min(dp[j], dp[j - coins[i]] + 1)
        # 这里是需要注意的,因为有可能出现报错的情况
        return dp[amount] if dp[amount] != amount + 1 else - 1
        # if dp[amount] != amount + 1:
        #     return dp[amount]
        # else:
        #     return - 1
        # return dp[amount]

279. 完全平方数

感觉一鼓作气完成这些题,真的好快乐呀!!

这道题和上一道题差不太多的感觉,没有做背包五步曲果然就是写代码的时候哦一堆错误

1. dp[j]是凑成和为j的数的最小数量排列

2. dp[j] = min(dp[j], dp[j - nums[i]] + 1)

3. dp[0] = 0,其他都是最大值

4. 随便怎么遍历啦,但是习惯先物品后背包了呜呜

class Solution:
    def numSquares(self, n: int) -> int:
        square = int(n ** 0.5)
        nums = [0] * square
        for i in range(square):
            nums[i] = (i + 1) ** 2
        dp = [n + 1] * (n + 1)
        dp[0] = 0 # 这步初始化千万不能错不然就前功尽弃了!!
        for i in range(len(nums)):
            for j in range(nums[i], n + 1):
                dp[j] = min(dp[j], dp[j - nums[i]] + 1)
        return dp[n]

day 46

​​​​​​​139. 单词拆分

惊呆了这道题居然可以用完全背包,我完全想不到哈哈哈哈!

但是这道题做的时候已经有点懵了,还是得再看看 

1. dp[j]是长度为j的一串字符串是否可能由worddict里面的单词组成

2. dp[j] = dp[j] or (dp[j - len(nums[i])] and nums[i] == s(j - len(nums[i]): j) 

3. dp[0] = ture

4. 无所谓吧还是,那还是先物品后背包,做完了发现有错,然后看了carl的才知道,需要求的是排列数

先遍历物品再遍历背包:只有1,2这种组合数的情况

先遍历背包再遍历物品:会出现(1,2)和(2,1)两种情况,排列数

因为我们需要的是121的输出方式,如果是组合数,那么有可能出现112的情况,所以要先遍历背包再遍历物品呜呜呜!

class Solution:
    def wordBreak(self, s: str, wordDict: List[str]) -> bool:
        dp = [False] * (len(s) + 1)
        dp[0] = True
        # for i in range(len(wordDict)):
        #     for j in range(len(wordDict[i]), len(s) + 1):
        # 求的是排列数所以不可以这样写
        for j in range(1, len(s) + 1):
            for i in range(len(wordDict)):
                dp[j] = dp[j] or (dp[j - len(wordDict[i])] and wordDict[i] == s[j - len(wordDict[i]): j])
        return dp[len(s)]

一些多重背包的学习

动态规划:关于多重背包,你该了解这些!

暂时放弃了,不要为难自己呜呜! 

day 47

198. 打家劫舍

看了一下题目,有这么不符合社会主义核心价值观的题目吗哈哈哈,就是说取相邻的两个数的情况不允许出现,枚举的话这道题还是简单的

大概看了一下,感觉可以做了,做完背包再做这个感觉真的是好简单好简单好简单

1. dp[i]是在有i下标的时候的最大值

2. dp[i] = max(dp[i - 2] + nums[i], dp[i - 1])

3. 取决于dp[0] = nums[0], dp[1] = max(nums[0], nums[1])

4. 从前往后遍历

一些out of range的写法:

        dp = [0] * (len(nums)+1) # 不需要n + 1
        if len(nums) == 1:
            return nums[0]
        # elif len(nums) == 2:
        #     return max(nums[0], nums[1])
        dp[1], dp[2] = nums[0], max(nums[0], nums[1])
        for i in range(2, len(nums) +1):
            a = dp[i - 2] + nums[i]
            b = dp[i - 1]
            dp[i] = max(a,b)
            # dp[i] = max(dp[i - 2] + nums[i], dp[i - 1])
        return dp[len(nums)]

len(nums) + 1的情况dp数组是可以遍历到len(nums),但nums遍历不到,需要nums[i - 1] 

class Solution:
    def rob(self, nums: List[int]) -> int:
        dp = [0] * (len(nums)) # 不需要n + 1
        if len(nums) == 1:
            return nums[0]
        # 这段必须要不然有用例会out of range
        dp[0], dp[1] = nums[0], max(nums[0], nums[1])
        for i in range(2, len(nums)):
            dp[i] = max(dp[i - 2] + nums[i], dp[i - 1])
        return dp[len(nums) - 1]

​​​​​​​213. 打家劫舍 II

感觉又有好几天没写了,今天一整个人被华为的滑动窗口弄傻了呜呜呜呜呜!前面那道题复习了一下下,手感还在嘿嘿嘿嘿!完了我的小脑子,环形的又不是很会了呜呜呜!

好的还是没有做,但是今天感觉还是被美团的笔试打击到了...还是很难很难呜呜呜!

这道题也是一个学习新的思路的方式:

337. 打家劫舍 III

day 48

股票问题!

121. 买卖股票的最佳时机

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

day 49

123. 买卖股票的最佳时机 III

188. 买卖股票的最佳时机 IV

day 51

309. 最佳买卖股票时机含冷冻期

714. 买卖股票的最佳时机含手续费

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值