【挑战30天掌握】算法与数据结构!!!

挑战30天从入门到精通,每日打卡,长期有效,持续关注!!!以下除题目外,答案与解析均原创,转载请标明出处,感兴趣的同学欢迎评论区打卡~,
关注公众号【可与】,关注更多新鲜内容

算法与数据结构打卡【day1】

3.19

二分查找

704. 二分查找

给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/binary-search

class Solution:
    def search(self, nums: List[int], target: int) -> int:
        left = 0
        right = len(nums)-1
        while left<=right:
            mid = (left+right)//2
            if target == nums[mid]:
                return mid
            elif target > nums[mid]:
                left = mid+1
            else: 
                right = mid-1
        return -1
278. 第一个错误的版本

你是产品经理,目前正在带领一个团队开发新的产品。不幸的是,你的产品的最新版本没有通过质量检测。由于每个版本都是基于之前的版本开发的,所以错误的版本之后的所有版本都是错的。

假设你有 n 个版本 [1, 2, …, n],你想找出导致之后所有版本出错的第一个错误的版本。

你可以通过调用 bool isBadVersion(version) 接口来判断版本号 version 是否在单元测试中出错。实现一个函数来查找第一个错误的版本。你应该尽量减少对调用 API 的次数。

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/first-bad-version

# The isBadVersion API is already defined for you.
# def isBadVersion(version: int) -> bool:

class Solution:
    def firstBadVersion(self, n: int) -> int:
        left = 1
        right = n  # right总是指向错误版本
        # 确定相邻两个为False和True即可,由于若有一个true,他后都为true,
        # 让区间[left,right] = [false,true]从而将第一个true包含其中,缩紧边界,
        #让 left 为false或第一个true同时,让right为true,而后left==right时指向第一个true
        while left< right:
            mid = (left+right)//2
            if isBadVersion(mid):  # mid为true
                right = mid    #让right为true  
            else:                # mid 为false, 此时mid+1可能为false或第一个true
                left = mid+1     # left 为false或第一个true
       
       # 此时 left==right 为第一个true
        return left 
35. 搜索插入位置

给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。

请必须使用时间复杂度为 O(log n) 的算法。

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/search-insert-position

class Solution:
    def searchInsert(self, nums: List[int], target: int) -> int:
        left = 0
        right = len(nums)-1
        if target> nums[right]:
            return len(nums)
        if target<nums[0]:
            return 0
        # 二分法总是将target包在[nums[left],nums[right]]之中,当发现nums里没有the exact "target",此时left与right颠倒,取left就可以
        while left<=right:
            mid = (left+right)//2
            if target==nums[mid]:
                return mid
            elif target > nums[mid]:
                left = mid+1
            else :
                right = mid-1
        return left

哈希表

217. 存在重复元素

给你一个整数数组 nums 。如果任一值在数组中出现 至少两次 ,返回 true ;如果数组中每个元素互不相同,返回 false 。
链接:https://leetcode.cn/problems/contains-duplicate/

  • 方法1
    利用python的集合
class Solution:
    def containsDuplicate(self, nums: List[int]) -> bool:
        if len(nums) == len(set(nums)):
             return False
        else:
             return True
  • 方法2
    创建字典
class Solution:
    def containsDuplicate(self, nums: List[int]) -> bool:
        dic = {}
        for num in nums:
            if dic.get(num)==None:
                dic[num]=1
            else:
                dic[num]=dic[num]+1
            if dic[num] > 1:
                return True

        return False

动态规划

53. 最大子数组和

给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
子数组 是数组中的一个连续部分。

示例 1:
输入:nums = [-2,1,-3,4,-1,2,1,-5,4]
输出:6
解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/maximum-subarray

class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        # 以nums[i]结尾的最大和记为dp[i]
        # dp[i] = max(dp[i-1]+nums[i],nums[i])
        # 最大和为所有dp[i]中的最大值
        l = len(nums)
        dp = [min(nums)]*l
        dp[0]=nums[0]  #初始情况(边界)
        #只说输出最大连续子数组之和,并没有让输出数组本身
        for i in range(1,l):
            dp[i] = max(dp[i-1]+nums[i],nums[i])
        return max(dp)

算法与数据结构打卡【day2】

3.20

双指针

知识点讲解:

# 两个指针
# 1. 方向相反
#    求两数之和
#    e.g. 给定升序排列数组 nums=[1,2,3,4,12],求两数之和为7的下标,左指针left=0, 右指针right=4
#            nums[left]+nums[right]>7, right=right-1 ,
#            nums[left]+nums[right]<7, left = left +1 ,一直下去
def oppositeDirection(nums,target):
    left = 0
    right = len(nums)-1
    while left<right:
        if nums[left]+nums[right]==target:
            return left,right
        elif nums[left]+nums[right]> target:
            right = right-1
        else:
            left = left +1
    return "Not found"
print(oppositeDirection([1,2,3,4,12],7))


# 2. 方向相同
#    合并两个有序数组
#    [2,5,8,10],[1,2,4,6,11]
#    定义两个指针i,j分别指向两个数组元素,i,j从大到小比较数组,小的放在结果里,移动指针至两个指针指向末尾
#    code:
def sameDirection(lis1,lis2):
    l1 = len(lis1)
    l2 = len(lis2)
    i,j = 0,0
    l = []
    while i<=l1-1 and j<=l2-1:  #不满足两个条件,说明将l1或l2至少一个搬空
        if lis1[i]<=lis2[j]:
            l.append(lis1[i])
            i = i+1
        else:
            l.append(lis2[j])
            j = j+1
    if i<=l1-1:  #说明l1没有搬空
        l.extend(lis1[i:])
    if j<=l2-1:
        l.extend(lis2[j:])
    return l
# print(sameDirection([2,5,8,10],[1,2,4,6,11]))

# 3. 一快一慢(快慢指针)
#    判断单链表是否存在“环”
#    设定指针p1,p2,其中p2的速度是p1的两倍,若存在环,p1会被再次追上



#4. 指针同向,但起点不同
#   返回倒数第n个节点值
#   设定p2初始指向第n个节点,p1初始指向第1个节点,同时跑时,当p2指向最后一个节点时,p1指向倒数第n个节点

977. 有序数组的平方

给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。

示例 1:
输入:nums = [-4,-1,0,3,10]
输出:[0,1,9,16,100]
解释:平方后,数组变为 [16,1,0,9,100]
排序后,数组变为 [0,1,9,16,100]

题目链接:https://leetcode.cn/problems/squares-of-a-sorted-array

  • 方法一,利用python内置方法
class Solution:
    def sortedSquares(self, nums: List[int]) -> List[int]:
        l = sorted([num**2 for num in nums])
        return l
  • 方法二:双指针
class Solution:
    def sortedSquares(self, nums: List[int]) -> List[int]:
        left = 0
        right = len(nums)-1
        l = []
        while left<=right:
            left_n,right_n = nums[left]**2,nums[right]**2
            # 此时左边和右边都为两侧最大值向中间递减,将最大值依次放入后倒转列表
            if left_n >= right_n:
                l.append(left_n)
                left = left+1
            else:
                l.append(right_n)
                right = right-1
        return list(reversed(l))
189. 轮转数组

给定一个整数数组 nums,将数组中的元素向右轮转 k 个位置,其中 k 是非负数。
链接:https://leetcode.cn/problems/rotate-array

示例 1:

输入: nums = [1,2,3,4,5,6,7], k = 3
输出: [5,6,7,1,2,3,4]
解释:
向右轮转 1 步: [7,1,2,3,4,5,6]
向右轮转 2 步: [6,7,1,2,3,4,5]
向右轮转 3 步: [5,6,7,1,2,3,4]
  • 方法一:数组切片
class Solution:
    def rotate(self, nums: List[int], k: int) -> None:
        """
        Do not return anything, modify nums in-place instead.
        """
        nums[:] = [*nums[-(k%len(nums)):],*nums[:-(k%len(nums))]]
        # 数组切片时间复杂度O(n),
        #等于其他语言中的新建一个数组,将原数组中的元素放到新数组中对应的位置上,然后再将新数组的元素复制到原数组中,时间复杂度为 O(n)
        # 注意这里不能直接nums = ,这样为指向了新的不属于in-place操作,
        # 这里 k 可能大于数组长度,当k等于len(nums)的倍数时,和原始数组相同
  • 方法二:反转数组
    反转三次数组得到
class Solution:
    def rotate(self, nums: List[int], k: int) -> None:
        """
        Do not return anything, modify nums in-place instead.
        """
        # ---->-->
        k = k%len(nums)
        nums1 = nums[::-1]  # <--<----
        nums1_left =nums1[:k][::-1]  # -->
        nums1_right = nums1[k:][::-1] # ---->
        nums[:] = nums1_left+nums1_right # -->---->
        # 注意这里不能直接nums = ,这样为指向了新的不属于in-place操作,
        # 这里 k 可能大于数组长度,当k等于len(nums)的倍数时,和原始数组相同
        # 注意这里如果用reversed,需要在用list(reversed),
        #          因为使用后类型为<list_reverseiterator object at 0x0000018F61C13880>
88. 合并两个有序数组

给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2,另有两个整数 m 和 n ,分别表示 nums1 和 nums2 中的元素数目。
请你 合并 nums2 到 nums1 中,使合并后的数组同样按 非递减顺序 排列。
注意:最终,合并后数组不应由函数返回,而是存储在数组 nums1 中。为了应对这种情况,nums1 的初始长度为 m + n,其中前 m 个元素表示应合并的元素,后 n 个元素为 0 ,应忽略。nums2 的长度为 n 。

示例 1:
输入:nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3
输出:[1,2,2,3,5,6]
解释:需要合并 [1,2,3] 和 [2,5,6] 。
合并结果是 [1,2,2,3,5,6] ,其中斜体加粗标注的为 nums1 中的元素。

链接:https://leetcode.cn/problems/merge-sorted-array

方法1,使用额外数组复制

#使用双指针,注意深复制
#时间复杂度O(m+n)
class Solution:
    def merge(self, nums1: List[int], m: int, nums2: List[int], n: int) -> None:
        """
        Do not return anything, modify nums1 in-place instead.
        """
        num_1 =nums1[:] #复制一份nums1用来排序,原来的nums1用来装结果,这里必须deepcopy,否则nums1变动,会影响num_1
        # 使用同向双指针,从小到大排
        i = 0
        j = 0
        k = 0
        while i<=m-1 and j<=n-1:  #两个数组都没有排完,若有一个数组排列完毕,index经过+1后会大于len-1
            if num_1[i]<=nums2[j]:
                nums1[k]=num_1[i]
                i = i+1
                k = k+1
            else:
                nums1[k]=nums2[j]
                j = j+1
                k = k+1
        if i<=m-1:
            while k<=m+n-1:
                nums1[k]=num_1[i]
                i = i+1
                k = k+1
        if j<=n-1:
            while k<=m+n-1:
                nums1[k]=nums2[j]
                j = j+1
                k = k+1

方法2,从大到小放入nums1末端

class Solution:
    def merge(self, nums1: List[int], m: int, nums2: List[int], n: int) -> None:
        """
        Do not return anything, modify nums1 in-place instead.
        """
       #从后向前遍历数组
        i = m-1
        j = n-1
        k = m+n-1
        if m==0:
            nums1[:]=nums2
        while i>=0 and j>=0:  #任意一个排满了就退出
            if nums1[i]>=nums2[j]:
                nums1[k]=nums1[i]
                k = k-1
                i = i-1
            else:
                nums1[k]=nums2[j]
                k = k-1
                j = j-1

        # if i>=0:  #本身剩下的就不用移动了
        #     while k>=0:
        #         nums1[k]=num1[i]
        #         i = i-1
        #         k = k-1
        if j>=0:
            while k>=0:
                nums1[k]=nums2[j]
                j = j-1
                k = k-1
反转字符串

编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 s 的形式给出。
不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。
链接:https://leetcode.cn/leetbook/read/top-interview-questions-easy/xnhbqj/

class Solution:
    def reverseString(self, s: List[str]) -> None:
        """
        Do not return anything, modify s in-place instead.
        """
        # 双指针
        # 对称交换
        for i in range(len(s)//2):
            s[i],s[-i-1]=s[-i-1],s[i]  # 这里其实相当于用temp额外空间O(1)进行交换

哈希表

1. 两数之和

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。
你可以按任意顺序返回答案。
链接:https://leetcode.cn/problems/two-sum

示例 1:

输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。
class Solution:
    def twoSum(self, nums: List[int], target: int) -> List[int]:
        # 哈希映射
        mapDic = {}  
        #按顺序将mapDic 中存入nums的值和其index,{[num]:index}
        #从左往右计算每个num的互补值,如果mapDic中有其互补值,则已经找到,没有则将num及其index存入mapDic
        # 时间复杂度为O(n)
        for i in range(len(nums)):
            cur = target-nums[i]
            if mapDic.get(cur) !=None:  #mapDic中存在当前nums[i]的互补值
                return [mapDic[cur],i]
            else:
                mapDic[nums[i]]=i   #将nums[i]本身存入mapDic
字符串中的第一个唯一字符

给定一个字符串 s ,找到 它的第一个不重复的字符,并返回它的索引 。如果不存在,则返回 -1 。
示例 1:
输入: s = “leetcode”
输出: 0
链接:https://leetcode.cn/leetbook/read/top-interview-questions-easy/xn5z8r/

class Solution:
    def firstUniqChar(self, s: str) -> int:
        Dic = {}
        for i in range(len(s)):
            if Dic.get(s[i])==None:
                Dic[s[i]]=1
            else:
                Dic[s[i]]=Dic[s[i]]+1
        for i in range(len(s)):
            if Dic.get(s[i])==1:
                return i
        return -1

算法与数据结构打卡【day3】

3.21

双指针

283. 移动零

给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。

请注意 ,必须在不复制数组的情况下原地对数组进行操作。

示例 1:
输入: nums = [0,1,0,3,12]
输出: [1,3,12,0,0]

题目链接:https://leetcode.cn/problems/move-zeroes

方法1
遍历两遍
第一遍记录非零个数n
第二遍,i记录经过的非零个数-1,j向后移动,当nums[j]!=0时,放入nums[i],
最后将nums后的n个位置赋值0

class Solution:
    def moveZeroes(self, nums: List[int]) -> None:
        """
        Do not return anything, modify nums in-place instead.
        """

        n = 0
        for i in range(len(nums)):
            if nums[i]==0:
                n = n+1
        i,j=0,0

        while j<len(nums)-1:
            if nums[i]!=0:
                i = i+1
                j = j+1
            else: #此时i指向第一个0
                while j<=len(nums)-2: #当j不是最后一个元素,继续向后移动
                    j=j+1
                    if nums[j]!=0:   #当当前位置不为零,将值放前面
                        nums[i] = nums[j]
                        i=i+1

        if n!=0:  #没有0,不需要剪切
            nums[-n:]=[0]*n

方法2
遍历一遍
指针i,记录第一个0的位置,j向后移动,当nums[j]!=0时,与nums[i]交换,
i++后j继续向后移动,直至j==len(nums)

class Solution:
    def moveZeroes(self, nums: List[int]) -> None:
        """
        Do not return anything, modify nums in-place instead.
        """
        i,j=0,0
        while j<len(nums)-1: #j到达最后一个,停止操作
            if nums[i]!=0:
                i = i+1
                j = j+1
            else: #此时i指向第一个0
                while j<=len(nums)-2: #当j不是最后一个元素,继续向后移动
                    j=j+1  
                    if nums[j]!=0:   #当当前位置不为零,加入交换操作
                        nums[i],nums[j]=nums[j],nums[i]
                        i=i+1

方法3,python内置函数

class Solution:
    def moveZeroes(self, nums: List[int]) -> None:
        """
        Do not return anything, modify nums in-place instead.
        """
        return nums.sort(key=lambda x:x==0)  #优先级高的放在最后
350. 两个数组的交集 II

给你两个整数数组 nums1 和 nums2 ,请你以数组形式返回两数组的交集。返回结果中每个元素出现的次数,应与元素在两个数组中都出现的次数一致(如果出现次数不一致,则考虑取较小值)。可以不考虑输出结果的顺序。

示例 1:
输入:nums1 = [1,2,2,1], nums2 = [2,2]
输出:[2,2]

链接:https://leetcode.cn/problems/intersection-of-two-arrays-ii

方法1,排序+双指针

class Solution:
    def intersect(self, nums1: List[int], nums2: List[int]) -> List[int]:
        nums1[:]=sorted(nums1)
        nums2[:]=sorted(nums2)
        ls = []
        i,j=0,0
        while i<=len(nums1)-1 and j<=len(nums2)-1: #若有一个测试完毕就退出
            if nums1[i]==nums2[j]: #当前元素相同
                ls.append(nums1[i])
                i=i+1
                j=j+1
            elif nums1[i]<nums2[j]:
                i=i+1
            else:
                j=j+1
        return ls

方法2,哈希表
创建一个哈希表H存储nums1元素出现次数,
在nums2中若H[num]存在,H[num]-1,且在结果中append这个num
默认len(nums1)<=len(nums2),否则在首行调用自身"func(nums2,nums1)"

# 官方版本
class Solution:
    def intersect(self, nums1: List[int], nums2: List[int]) -> List[int]:
        if len(nums1) > len(nums2):
            return self.intersect(nums2, nums1)
        
        m = collections.Counter()
        for num in nums1:
            m[num] += 1
        
        intersection = []
        for num in nums2:
            if m.get(num, 0) > 0:
                intersection.append(num)
                m[num] -= 1
                if m[num] == 0:
                    m.pop(num)
        
        return intersection

动态规划

121. 买卖股票的最佳时机

给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。
你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。
返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0 。

示例 1:
输入:[7,1,5,3,6,4]
输出:5
解释:在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
     注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。

原题链接:https://leetcode.cn/problems/best-time-to-buy-and-sell-stock

class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        if len(prices)==1:
            return 0
        earn = 0
        min_price = prices[0]
        for i in range(1,len(prices)):
            min_price = min(min_price,prices[i]) #寻找之前的最小的price
            earn=max(prices[i]-min_price,earn)
            # dp[i]=max(dp[i−1],prices[i]−minprice)
        return earn

链表

删除链表中的节点
输入:head = [4,5,1,9], node = 1
输出:[4,5,9]
解释:指定链表中值为 1 的第三个节点,那么在调用了你的函数之后,该链表应变为 4 -> 5 -> 9

提示:
链表中节点的数目范围是 [2, 1000]
-1000 <= Node.val <= 1000
链表中每个节点的值都是 唯一 的
需要删除的节点 node 是 链表中的节点 ,且 不是末尾节点
链接:https://leetcode.cn/leetbook/read/top-interview-questions-easy/xnarn7/

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def deleteNode(self, node):
        """
        :type node: ListNode
        :rtype: void Do not return anything, modify node in-place instead.
        """
        node.val = node.next.val  #将下一个节点的值赋给当前节点(当前结点的值已经被跳过)
        node.next = node.next.next #将下一节点的指针赋给当前节点的指针(当前节点的指针被跳过)

算法与数据结构打卡【day4】

3.22

双指针

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

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

示例 1:

输入:s = "Let's take LeetCode contest"
输出:"s'teL ekat edoCteeL tsetnoc"

链接:https://leetcode.cn/problems/reverse-words-in-a-string-iii

方法1

class Solution:
    def reverseWords(self, s: str) -> str:
        i,j=0,0
        result = ""
        while j<len(s): #j出界时退出
            if s[i]==" ":
                i=i+1 #i,j跳过空格
                j=j+1
                result=result+" "
            else:  #i指向第一个非空元素
                while j<=len(s)-1 and s[j]!=" ": #跳出时j指向下一个空格或者j指向末尾
                    j=j+1
                result=result+s[i:j][::-1]
                i=j   #此时一起指向空格
        return result

方法2 内置方法

class Solution:
    def reverseWords(self, s: str) -> str:
        ls = s.split(" ")
        ls[:] = list(map(lambda x:x[::-1],ls))
        return " ".join(ls)

矩阵

566. 重塑矩阵

在 MATLAB 中,有一个非常有用的函数 reshape ,它可以将一个 m x n 矩阵重塑为另一个大小不同(r x c)的新矩阵,但保留其原始数据。

给你一个由二维数组 mat 表示的 m x n 矩阵,以及两个正整数 r 和 c ,分别表示想要的重构的矩阵的行数和列数。

重构后的矩阵需要将原始矩阵的所有元素以相同的 行遍历顺序 填充。

如果具有给定参数的 reshape 操作是可行且合理的,则输出新的重塑矩阵;否则,输出原始矩阵。
链接:https://leetcode.cn/problems/reshape-the-matrix

方法1,元素赋值

class Solution:
    def matrixReshape(self, mat: List[List[int]], r: int, c: int) -> List[List[int]]:
        R = len(mat)
        C = len(mat[0])
        ls = []
        if r*c!=R*C:
            return mat
        inner_ls =[0]*c
        for i in range(r):
            for j in range(c):
                inner_ls[j] = mat[(j + i * c) // C][(j + i * c) % C]
            ls.append(inner_ls[:])  #这里需要用[:],否则当变量inner_ls改变时,原来赋值过的位置会一起改变
        return ls

方法二:切片赋值

class Solution:
    def matrixReshape(self, nums: List[List[int]], r: int, c: int) -> List[List[int]]:
        if len(nums)*len(nums[0])!=r*c:
            return nums
        l=[]
        new=[]
        for i in range(len(nums)):
            l+=nums[i]
        for i in range(0,len(l),c):
            new.append(l[i:i+c])
        return new

数学

118. 杨辉三角

给定一个非负整数 numRows,生成「杨辉三角」的前 numRows 行。

示例 1:

输入: numRows = 5
输出: [[1],[1,1],[1,2,1],[1,3,3,1],[1,4,6,4,1]]

方法1,传统公式

class Solution:
    def generate(self, numRows: int) -> List[List[int]]:
        ls=[[1],[1,1]]
        inner_ls=[1,1]
        if numRows==1:
            return [[1]]
        if numRows==2:
            return ls
        for i in range(2,numRows):
            cur=[1]
            for j in range(1,i):
                cur.append(inner_ls[j-1]+inner_ls[j])
            cur.append(1)
            inner_ls[:]=cur[:]  #储存生成的值,供下次使用
            ls.append(inner_ls[:])
        return ls

方法二:发现 错位之和为下一行答案:
如:
1 2 1 0
0 1 2 1

1 3 3 1
从而有:

class Solution:
    def generate(self, numRows: int) -> List[List[int]]:
        ls = [[1]]
        res = [1]
        while len(ls)<=numRows-1: ## 前面已经有一个初始情况,当numsRows==1时,直接返回ls
            res = [a+b for a,b in zip(res+[0],[0]+res)]
            ls.append(res)
        return ls

去掉一行

class Solution:
    def generate(self, numRows: int) -> List[List[int]]:
        ls = [[1]]
        while len(ls)<=numRows-1: ## 前面已经有一个初始情况,当numsRows==1时,直接返回ls
            res = [a+b for a,b in zip(ls[-1]+[0],[0]+ls[-1])]
            ls.append(res)
        return ls

链表 & 双指针

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

给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
https://leetcode.cn/leetbook/read/top-interview-questions-easy/xn2925/

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]:
        dummy = ListNode(next = head)  #实际上在head前面加了一个节点,从而防止head本身只有一个节点的删头问题,因为head的头部相当于dummy的中间(正数第一个节点)
        fast = dummy
        slow = dummy
        for i in range(n): #与最后一个的间隔为n,此时slow指向的是倒数第n+1个
            fast = fast.next
        while fast.next!=None:  #当fast没到尾部,退出时,fast为尾
            fast = fast.next
            slow = slow.next
        slow.next = slow.next.next #将倒数第n+1个节点的指针赋成倒数第n个节点的指针就可以了
        return dummy.next  #去掉在head处加的头部

算法与数据结构打卡【day5】

3.23

链表

876. 链表的中间结点

给你单链表的头结点 head ,请你找出并返回链表的中间结点。
如果有两个中间结点,则返回第二个中间结点。

示例
输入:head = [1,2,3,4,5]
输出:[3,4,5]
解释:链表只有一个中间结点,值为 3 。

方法1,遍历两遍

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def middleNode(self, head: Optional[ListNode]) -> Optional[ListNode]:
        n = 0
        p = head
        while p: #计算节点个数 , 注意head本身也算
            n=n+1
            p = p.next

        mid_n = n//2
        Q = head  #已经算作第一个节点了
        for i in range(mid_n):  #加上前面的走的那一步,就是n//2+1
            Q = Q.next
        return Q

方法2,使用数组

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def middleNode(self, head: Optional[ListNode]) -> Optional[ListNode]:
        ls = [head]
        while ls[-1].next: #下一个节点不是None就放入,放入后下次检验的是刚刚放入的节点的下一节点
            ls.append(ls[-1].next)
        return(ls[len(ls)//2]) #注意这里实际上是len//2+1个,index为len//2

方法3,快慢指针,快指针是慢指针的2倍速

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def middleNode(self, head: Optional[ListNode]) -> Optional[ListNode]:
        fast = head
        slow = head
        while fast and fast.next: #保证
            slow = slow.next
            fast = fast.next.next
        return slow

滑动窗口

3. 无重复字符的最长子串

给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。

示例 1:
输入: s = "abcabcbb"
输出: 3 
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。

示例 2:
输入: s = "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
     请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。

方法1,双指针,滑动窗口

class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
        left,ans = 0,0
        while left<len(s):
            cur = 1 #初始化当前长度
            if left+1<len(s):               
                right = left+1
                s_set = {s[left]} #初始化当前集合(无需标号时,用集合比列表更快)
                while right<len(s) and s[right] not in s_set:
                    s_set.add(s[right])
                    cur = cur+1
                    right = right+1
            
            ans = max(ans,cur)
            left = left +1
        return ans

方法2,使用哈希表、滑动变长区间
key为字符,value存储字符出现的最后一次位置,
保证[i,j]中没有重复字符串

class Solution:
    def lengthOfLongestSubstring(self, s):
        """
        :type s: str
        :rtype: int
        """
        st = {}
        i, ans = 0, 0
        for j in range(len(s)):
            if s[j] in st:   
                i = max(st[s[j]]+1, i) #设为新的区间左端(舍弃经过的重复字符,保证区间左端到右端位置里没有重复字符窜)
            st[s[j]] = j    #记录当前字符串出现的位置index
            ans = max(ans, j - i+1)  #区间长度
        return ans

矩阵

36. 有效的数独

请你判断一个 9 x 9 的数独是否有效。只需要 根据以下规则 ,验证已经填入的数字是否有效即可。

数字 1-9 在每一行只能出现一次。
数字 1-9 在每一列只能出现一次。
数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。(请参考示例图)

注意:

一个有效的数独(部分已被填充)不一定是可解的。
只需要根据以上规则,验证已经填入的数字是否有效即可。
空白格用 ‘.’ 表示。

输入:board = 
[["5","3",".",".","7",".",".",".","."]
,["6",".",".","1","9","5",".",".","."]
,[".","9","8",".",".",".",".","6","."]
,["8",".",".",".","6",".",".",".","3"]
,["4",".",".","8",".","3",".",".","1"]
,["7",".",".",".","2",".",".",".","6"]
,[".","6",".",".",".",".","2","8","."]
,[".",".",".","4","1","9",".",".","5"]
,[".",".",".",".","8",".",".","7","9"]]
输出:true

class Solution:
    def isValidSudoku(self, board: List[List[str]]) -> bool:
            
        """
        :type board: List[List[str]]
        :rtype: bool
        """
        for i in range(3):
            s_set = {}
            for j in range(3):
                nums = [board[p][q] for p in range(i*3,i*3+3) for q in range(j*3,j*3+3) if board[p][q]!="."] 
                if len(nums)!=len(set(nums)):
                    return False
        for i in range(9):
            nums_row = board[i]
            nums_row = list(filter(lambda x:x!=".",nums_row))
            if len(nums_row)!=len(set(nums_row)):
                return False
            nums_col = [board[j][i] for j in range(9) if board[j][i]!="."]
            if len(nums_col)!=len(set(nums_col)):
                return False
        return True
73. 矩阵置零

方法1,时间复杂度O(mn),空间复杂度O(mn)

class Solution:
    def setZeroes(self, matrix: List[List[int]]) -> None:
        """
        Do not return anything, modify matrix in-place instead.
        """
        m = len(matrix)
        n = len(matrix[0])
        set_0 = []
        for i in range(m):
            for j in range(n):
                if matrix[i][j]==0:
                    set_0.append((i,j))

        for i,j in set_0:
            matrix[i] = [0]*n
            for k in range(m):
                matrix[k][j]=0

方法2,时间复杂度O(mn),空间复杂度O(m+n)
因为行列都要清零,所以直接储存要清零的行和列就行

class Solution:
    def setZeroes(self, matrix: List[List[int]]) -> None:
        """
        Do not return anything, modify matrix in-place instead.
        """
        m = len(matrix)
        n = len(matrix[0])
        rows=set()
        cols=set()
        for i in range(m):
            for j in range(n):
                if matrix[i][j]==0:
                    rows.add(i)
                    cols.add(j)

        for row in rows:
            matrix[row] = [0]*n
        for col in cols:
            for k in range(m):
                matrix[k][col]=0

方法3,时间复杂度O(mn),空间复杂度O(1)
利用矩阵第一行和第一列存储行列是否为零的信息
利用额外的两个标量存储第一行和第一列的信息

class Solution:
    def setZeroes(self, matrix: List[List[int]]) -> None:
        """
        Do not return anything, modify matrix in-place instead.
        """
        m = len(matrix)
        n = len(matrix[0])
        find_row = any( matrix[0][j]==0 for j in range(n))
        find_col = any( matrix[i][0]==0 for i in range(m))
        
        for i in range(1,m):   #注意要从index==1开始
            for j in range(1,n):
                if matrix[i][j]==0:
                    matrix[i][0] = matrix[0][j] = 0 #如果当前元素为零,则这一行和列都为零,在第一行和第一列做标记
        for i in range(1,m):
            for j in range(1,n):
                if matrix[i][0]==0 or matrix[0][j]==0: #根据标记置零
                    matrix[i][j]=0

        if find_row :
            matrix[0] = [0]*n
        if find_col:
            for k in range(m):
                matrix[k][0]=0

算法与数据结构打卡【day6】

3.24

字符串/ 哈希映射 /哈希表

387. 字符串中的第一个唯一字符

给定一个字符串 s ,找到 它的第一个不重复的字符,并返回它的索引 。如果不存在,则返回 -1 。

方法1,哈希映射,二次遍历

class Solution:
    def firstUniqChar(self, s: str) -> int:
        dic = {}
        for i in range(len(s)):
            if dic.get(s[i] )==None:
                dic[s[i]]=i   #存储index
            else:
                dic[s[i]]=-1
        ans = len(s)
        flag = False
        for k,v in dic.items():
            if v!=-1:
                flag = True
                ans = min(ans,v)
        if flag:
            return ans
        else:
            return -1

方法2,字母表
唯一出现的元素左起index和右起index相同

class Solution:
    def firstUniqChar(self, s: str) -> int:
        min_index = len(s)
        for i in 'abcdefghijklmnopqrxtuvwxyz':
            left = s.find(i)
            if(i in s and s.rfind(i)==left):
                min_index = min(min_index, left)
        return min_index if(min_index<len(s)) else -1
383. 赎金信

给你两个字符串:ransomNote 和 magazine ,判断 ransomNote 能不能由 magazine 里面的字符构成。
如果可以,返回 true ;否则返回 false 。
magazine 中的每个字符只能在 ransomNote 中使用一次。
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/ransom-note

方法1,字符统计
哈希表,二次遍历
第一遍记录表值,第二遍减去表中值

class Solution:
    def canConstruct(self, ransomNote: str, magazine: str) -> bool:
        m_has={}
        for m in magazine:
            if m_has.get(m)==None:
                m_has[m]=1
            else:
                m_has[m]=m_has[m]+1
        for r in ransomNote:
            if m_has.get(r)==None or m_has[r]==0:
                return False
            else:
                m_has[r]=m_has[r]-1
        return True

简化以上代码

class Solution:
    def canConstruct(self, ransomNote: str, magazine: str) -> bool:
        if len(ransomNote)>len(magazine):
            return False
        else:
            return not collections.Counter(ransomNote)-collections.Counter(magazine) #返回字典差集,如果有元素,说明ransomNote里面有magazine中没有的元素,所以not true == false,  反之没有元素说明magazine包含ransomNote, not 空 == true
            #注意 not {}或not () 或 not 1 都是true

这里Counter是collections里的一个计数器,是字典的子类
用法:

import  collections
print(collections.Counter("abcdsbb"))
print(collections.Counter("abcdsbb")-collections.Counter("abcds"))
print(collections.Counter("abcds")-collections.Counter("abcds"))

输出:

Counter({'b': 3, 'a': 1, 'c': 1, 'd': 1, 's': 1})
Counter({'b': 2})
Counter()
242. 有效的字母异位词

给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。

注意:若 s 和 t 中每个字符出现的次数都相同,则称 s 和 t 互为字母异位词。

class Solution:
    def isAnagram(self, s: str, t: str) -> bool:
        return collections.Counter(s)==collections.Counter(t)
567. 字符串的排列

给你两个字符串 s1 和 s2 ,写一个函数来判断 s2 是否包含 s1 的排列。如果是,返回 true ;否则,返回 false 。
换句话说,s1 的排列之一是 s2 的 子串 。

示例 1:
输入:s1 = "ab" s2 = "eidbaooo"
输出:true
解释:s2 包含 s1 的排列之一 ("ba").

方法1,哈希映射(字典)

class Solution:
    def checkInclusion(self, s1: str, s2: str) -> bool:
        if len(s1)>len(s2):
            return False
        
        count_s1 = collections.Counter(s1)
        n = len(s1)
        for i in range(len(s2)):
            if count_s1.get(s2[i])!=None:
                if count_s1 == collections.Counter(s2[i:(i+n)]):
                    return True
        return False

优化以上算法

class Solution:
    def checkInclusion(self, s1: str, s2: str) -> bool:
        if len(s1)>len(s2):
            return False
        count_s1 = collections.Counter(s1)
        n = len(s1)
        count_s2 = collections.Counter(s2[:n])
        if count_s1 == count_s2:
            return True

        for i in range(n,len(s2)): #以长度为n的窗口滑动, i代表区间右端点
            count_s2[s2[i-n]] = count_s2[s2[i-n]]-1
            if count_s2.get(s2[i])==None:
                count_s2[s2[i]] = 1
            else:
                count_s2[s2[i]] = count_s2[s2[i]]+1
            if count_s1 == count_s2: 
                return True
        return False

方法2,哈希表
26个字母,index表示第几个字母,value表示个数
原理同上

class Solution:
    def checkInclusion(self, s1: str, s2: str) -> bool:
        n, m = len(s1), len(s2)
        a = ord("a") # 97
        if n > m:
            return False
        charCount1 = [0]*26
        charCount2 = [0]*26
        for i in range(n):
            charCount1[ord(s1[i])-a] += 1
            charCount2[ord(s2[i])-a] += 1
        if charCount1 == charCount2:
            return True
        for j in range(n,m):
            charCount2[ord(s2[j])-a] += 1
            charCount2[ord(s2[j-n])-a] -= 1
            if charCount1 == charCount2:
                return True
        return False

算法与数据结构打卡【day7】

3.25

链表

深度优先/广度优先

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

可与很ok

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值