俺的刷题之旅(26题惹)

简洁开发

1 赋值多个变量的值

num_1, num_2 = 666, 999

2 合并join

slogan = ["今", "天", "你", "学", "python", "了", "么", "?"]
real_slogan = "".join(slogan)

3 enumerate() 函数来获取索引-数值对
4 列出当前目录下的所有文件和目录名

import os
files = [file for file in os.listdir(".")]

5 颠倒键值对

dict_1 = {1: "python", 2: "java"}
new_dict = {value:key for key, value in dict_1.items()}
输出:
{'python': 1, 'java': 2}

6 列表推导式
功能:生成符合条件的新列表

arrayList=[[1,2,3],[4,5,6],[7,8,9]]

arrayNew = [yy for yy in arrayList if yy[0]%2==1] 
#新列表为该行第一个数为偶数。

7 map的使用
8 return 1 if true else 0
return count>1 一个判断式可以直接返回bool值,true/false

滑动窗口

从第一个元素开始滑动窗口并逐个元素地向右滑,并根据你所求解的问题调整窗口的长度。

在某些情况下窗口大小会保持恒定,在其它情况下窗口大小会增大或减小。

问题的输入是一种线性数据结构,例如链表,数组或字符串

可以处理大小为k的子数组的最大和 easy

带有k个不同字符的最长子字符串 medium

在这里插入图片描述

leetcode1 两数之和

暴力

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

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

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

例题中的 abcabcbb,进入这个队列(窗口)为 abc 满足题目要求,当再进入 a,队列变成了 abca,这时候不满足要求。所以,我们要移动这个队列!
如何移动?
我们只要把队列的左边的元素移出就行了,直到满足题目要求!一直维持这样的队列,找出队列出现最长的长度时候,求出解!

class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
        if not s:return 0

        lookup = set()
        left, max_len, cur_len = 0,0,0
        for i in range(len(s)):
            cur_len += 1
            while s[i] in lookup:
                lookup.remove(s[left])
                left += 1
                cur_len -= 1
            lookup.add(s[i])
            max_len = max(max_len,cur_len)
        return max_len

链表

leetcode 21. 合并两个有序链表

递归

class Solution:
    def mergeTwoLists(self, l1: Optional[ListNode], l2: Optional[ListNode]) -> Optional[ListNode]:
        #终止条件:当两个链表都为空时,表示我们对链表已合并完成。
        #如何递归:我们判断 l1 和 l2 头结点哪个更小,然后较小结点的 next 指针指向其余结点的合并结果。(调用递归)
        if not l1: return l2  # 终止条件,直到两个链表都空
        if not l2: return l1
        # 递归调用
        if l1.val <= l2.val:  #比较出 较小结点的 next 指针
            l1.next = self.mergeTwoLists(l1.next,l2) #继续调用 指向下一个较小的节点
            return l1
        else:
            l2.next = self.mergeTwoLists(l1,l2.next)
            return l2

迭代

class Solution:
    def mergeTwoLists(self, l1: Optional[ListNode], l2: Optional[ListNode]) -> Optional[ListNode]:
        #首先,我们设定一个哨兵节点 prehead ,这可以在最后让我们比较容易地返回合并后的链表。我们维护一个 pre 指针,我们需要做的是调整它的 next 指针。
        prehead = ListNode(-1)
        pre = prehead
        #如果 l1 当前节点的值小于等于 l2 ,我们就把 l1 当前的节点接在 prev 节点的后面同时将 l1 指针往后移一位。否则,我们对 l2 做同样的操作。不管我们将哪一个元素接在了后面,我们都需要把 prev 向后移一位。
        while l1 and l2:
            if l1.val<=l2.val:
                pre.next = l1
                l1 = l1.next
            else:
                pre.next = l2
                l2 = l2.next
            pre = pre.next
        pre.next = l1 if l1 is not None else l2
        return prehead.next

leetcode 86. 分隔链表

与上一题的迭代法非常相似

class Solution:
    def partition(self, head: Optional[ListNode], x: int) -> Optional[ListNode]:
        #建立2个链表,分别存放小于x的节点,与大于等于x的节点信息;
        #遍历一遍后,记录2个链表最后链接上的信息,将左链表即小的尾部接上头部
        #大于等于部分的链表尾部可能会成环,故将r.next = None
        #dummy node
        linklist_left, linklist_right =  ListNode(-1), ListNode(-1)
        #将要遍历的指针指向dummyy node
        l, r = linklist_left, linklist_right
        while head is not None:
            if head.val < x:
                l.next = head
                l = l.next
            else:
                r.next = head
                r = r.next
            head = head.next
        l.next = linklist_right.next    # 将小的尾部链接上大于等于的头部
        r.next = None                   # 防止链表成环 
        return linklist_left.next

leetcode 206. 反转链表

迭代

class Solution:
    def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:
        pre,cur = None,head
        while cur:
            nexxt = cur.next
            cur.next = pre #反转cur.next
            pre = cur
            cur = nexxt #使pre cur都能前进
        return pre

leetcode 92. 反转链表 II

迭代

class Solution:
    def reverseBetween(self, head: Optional[ListNode], left: int, right: int) -> Optional[ListNode]:
        dummy = ListNode(0)
        pre,l = dummy,head
        dummy.next = head
#第 1 步:通过第一次遍历找到pre,left,rightright,succ
#第 2 步:待反转的区域反转;
#第 3 步:把 pre 的 next 指针指向反转以后的链表头节点(right),把反转以后的链表的尾节点(left)的 next 指针指向 succ。
        for i in range(left-1):
            pre = pre.next
            l = l.next
        r = l
        for i in range(right - left):
            r = r.next
        succ = r.next

        n1,n2 = l,l.next
        for i in range(right - left):
            nex = n2.next
            n2.next = n1
            n1 = n2
            n2 = nex

        pre.next = r 
        l.next = succ
        return dummy.next

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

  • 为什么需要哑节点

例如:链表 head = [1,2,3,4,5],需要删除倒数第 5 个结点,也就是第一个节点。按照算法逻辑,应该首先找到倒数第 6 个节点。但由于头节点不存在前驱节点,因此我们需要在删除头节点时进行特殊判断。
但是添加一个哑节点(dummy node),让它的next 指针指向链表的头节点。这样一来,头节点的前驱节点就是哑节点本身。

  • 主要思想

我们首先从头节点开始对链表进行一次遍历,得到链表的长度 L。为了方便删除操作,添加一个哑结点dummy,我们可以从哑节点开始遍历,当遍历到第 L−n+1 个节点(L-n次)时,它的下一个节点就是我们需要删除的节点,这样我们只需要修改一次指针,就能完成删除操作。

class Solution:
    def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]:
        def getLen(head: [ListNode]) -> int:
            getLen = 0
            while head:
                getLen += 1
                head = head.next
            return getLen
        L = getLen(head)
        dummy = ListNode(0,head)
        cur = dummy
        for i in range(L-n): 
            cur = cur.next
        cur.next = cur.next.next
        return dummy.next

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

三指针 迭代

class Solution:
    def swapPairs(self, head: Optional[ListNode]) -> Optional[ListNode]:
        dummy = ListNode(0)
        dummy.next = head
        temp = dummy
        while temp.next and temp.next.next:
            #标n1 n2的指针
            node1 = temp.next
            node2 = temp.next.next
            #dummy指向2 2指向1 1指向3
            temp.next = node2
            node1.next = node2.next
            node2.next = node1
            #只用temp右移
            temp = node1
        return dummy.next

二指针或迭代器

二指针通常在排序过的数组或链表中搜索配对时很有用,需要查找满足某些约束的一组元素的问题
数组中的元素集是配对/三元组甚至子数组
在这里插入图片描述

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

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

复杂度为O(nlogn)

def sortedSquares(self, nums: List[int]) -> List[int]:
        return sorted(num * num for num in nums)

动态规划

leetcode 53. 最大子数组和

class Solution(object):
    def maxSubArray(self, nums):
        #思想是动态规划,nums[i-1]并不是数组前一项的意思,而是到前一项为止的最大子序和,和0比较是因为只要大于0,就可以相加构造最大子序和。如果小于0则相加为0,nums[i]=nums[i],相当于最大子序和又重新计算。其实是一边遍历一边计算最大序和
        for i in range(1, len(nums)):
            nums[i]= nums[i] + max(nums[i-1], 0)
        return max(nums)

贪心算法

leetcode 455. 分发饼干

核心思想:为了尽可能满足最多数量的孩子,从贪心的角度考虑,应该按照孩子的胃口从小到大的顺序依次满足每个孩子,且对于每个孩子,应该选择可以满足这个孩子的胃口且尺寸最小的饼干

class Solution(object):
    def findContentChildren(self, g, s):
        
        g.sort()
        s.sort()
        i = j = count = 0
        while i < len(g) and j < len(s):
            if g[i]>s[j]:
                j += 1
            elif g[i] <= s[j]:
                i += 1
                j += 1
                count += 1
        return count

leetcode 121. 买卖股票的最佳时机
题目描述:一次股票交易包含买入和卖出,只进行一次交易,求最大收益。
核心思想:
只要记录前面的最小价格,将这个最小价格作为买入价格,然后将当前的价格作为售出价格,查看当前收益是不是最大收益。

class Solution(object):
    def maxProfit(self, prices):
        """
        :type prices: List[int]
        :rtype: int
        """
        #min_price = max(prices)
        min_price = float('inf')
        max_profit = 0
        for i in range(len(prices)):
            min_price = min(min_price,prices[i])
            profit = prices[i] - min_price
            max_profit = max(max_profit,profit)
        return max_profit

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

class Solution(object):
    def maxProfit(self, prices):
        """
        :type prices: List[int]
        :rtype: int
        """
        #当访问到一个 prices[i] 且 prices[i] - prices[i-1] > 0,那么就把 prices[i] - prices[i-1] 添加到收益中。
        maxProfit = 0
        for i in range(1,len(prices)):
            if prices[i] - prices[i-1] > 0:
                maxProfit += prices[i] - prices[i-1]
        return maxProfit

leetcode 605. 种花问题

flowerbed 数组中 1 表示已经种下了花朵。花朵之间至少需要一个单位的间隔,求解是否能种下 n 朵花。
贪心思想:
思路:遍历,能种花的情况是该位置为0,前后位置不为1
复杂度:时间复杂度O(n),空间复杂度O(1)

class Solution(object):
    def canPlaceFlowers(self, flowerbed, n):
        count = 0
        #考虑只有1,2格的情况
        if len(flowerbed) == 1:
            if n == 0:
                return True
            elif n == 1 and flowerbed[0] == 0:
                return True
            else:
                return False
        if len(flowerbed) == 2:
            if n == 0:
                return True
            elif n == 1 and flowerbed[0] == 0 and flowerbed[1] == 0:
                return True
            else:
                return False
    
        for i in range(0,len(flowerbed)):
            if i == 0:
                if flowerbed[i] == 0 and flowerbed[i+1] == 0:
                    flowerbed[i] = 1
                    count += 1
                else:
                    continue
            elif i < len(flowerbed)-1:
                if flowerbed[i] == 0 and flowerbed[i-1] == 0 and flowerbed[i+1] == 0:
                    flowerbed[i] = 1
                    count += 1
                else:
                    continue
            elif i == len(flowerbed)-1:
                if flowerbed[i] == 0 and flowerbed[i-1] == 0:
                    count += 1
        #return count
        return count>= n
        

防御式编程思想:在 flowerbed 数组两端各增加一个 0, 这样处理的好处在于不用考虑边界条件
这个方法真的好好啊555,好就好在我tmd想不到啊555

class Solution(object):
    def canPlaceFlowers(self, flowerbed, n):
        count = 0
        tmp = [0] + flowerbed + [0]
        for i in range(1,len(tmp)-1):
            if tmp[i-1] == 0 and tmp[i] == 0 and tmp[i+1] == 0:
                tmp[i] = 1
                count += 1
        return count>= n
        

leetcode 665 修改一个数成为非递减数组

这道题用的思想是贪心算法,你需要保证每次改动之后都对原序列的影响是最小的,换个说法就是每当出现逆序的时候,你应该思考到底是修改nums[i]好还是nums[i - 1]好
找数组中第一个 nums[i]>nums[i+1] 的 i 点,没找到直接true
找到了后,解决办法有两种:
1) nums[i] = nums[i+1]
2) nums[i+1] = nums[I]
更改完数组后若是非递减数列则true,否则false

class Solution(object):
    def checkPossibility(self, nums):
        count = 0
        tmp = [float("-inf")] + nums + [float("inf")]
        #找数组中第一个 nums[i]>nums[i+1] 的 i 点,没找到直接true
        for i in range(1,len(tmp)-1):
            if tmp[i] > tmp[i+1]:
                #找到了后,nums[i]赋值为nums[i+1]
                if tmp[i] > tmp[i+2]:
                    tmp[i] = tmp[i+1]
                else:
                    tmp[i+1] = tmp[i]
                break
            else:
                count += 1
        if count == len(nums):
            return True

        count2 = 0

        for i in range(1,len(tmp)-1):
            if tmp[i] > tmp[i+1]:
                return False
            else:
                count2 += 1
        if count2 == len(nums):
            return True

二分查找

剑指 Offer 53 - I. 在排序数组中查找数字 I
统计一个数字在排序数组中出现的次数。

#字典法
class Solution(object):
    def search(self, nums, target):
        dic = {}
        for i in nums:
            dic[i] = dic.get(i,0) + 1
        if target not in dic.keys():
            return 0
        return dic[target]
#列表count方法
class Solution(object):
    def search(self, nums, target):
        return nums.count(target)

二叉树

二叉树介绍 https://blog.csdn.net/ycfxxr/article/details/122921489
先在开头总结一下,二叉树解题的思维模式分两类:

1、是否可以通过遍历一遍二叉树得到答案?如果可以,用一个 traverse 函数配合外部变量来实现,这叫「遍历」的思维模式。

2、是否可以定义一个递归函数,通过子问题(子树)的答案推导出原问题的答案?如果可以,写出这个递归函数的定义,并充分利用这个函数的返回值,这叫「分解问题」的思维模式。

无论使用哪种思维模式,你都需要思考:

如果单独抽出一个二叉树节点,它需要做什么事情?需要在什么时候(前/中/后序位置)做?其他的节点不用你操心,递归函数会帮你在所有节点上执行相同的操作。

递归

什么是递归呢?函数在运行时调用自己。
我们从中总结两个规律:
递归函数必须要有终止条件,否则会出错;
递归函数先不断调用自身,直到遇到终止条件后进行回溯,最终返回答案。

快排和归并

两个经典排序算法 快速排序 和 归并排序,对于它俩,你有什么理解?

如果你告诉我,快速排序就是个二叉树的前序遍历,归并排序就是个二叉树的后序遍历,那么我就知道你是个算法高手了。
快排

快速排序的原理如下:

  1. 从待排序列表中选取一个基准数据(通常选取第一个数据)。

  2. 将待排序列表中所有比基准数据小的元素都放到基准数据左边,所有比基准数据大的元素都放到基准数据右边(升序排列,降序反之)。用基准数据进行分割操作后,基准数据的位置就是它最终排序完成的位置,第一轮排序完成。

  3. 递归地对左右两个部分的数据进行快速排序。即在每个子列表中,选取基准,分割数据。直到被分割的数据只有一个或零个时,列表排序完成。

def quick_sort(array, start, end):
    if start >= end:
        return
    mid_data, left, right = array[start], start, end
    while left < right:
        while array[right] >= mid_data and left < right:
            right -= 1
        array[left] = array[right]
        while array[left] < mid_data and left < right:
            left += 1
        array[right] = array[left]
    array[left] = mid_data
    quick_sort(array, start, left-1)
    quick_sort(array, left+1, end)
 
 
if __name__ == '__main__':
    array = [10, 17, 50, 7, 30, 24, 27, 45, 15, 5, 36, 21]
    quick_sort(array, 0, len(array)-1)
    print(array)

前序中序后序

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

leetcode 144 二叉树的前序遍历

前序遍历首先访问根节点,然后遍历左子树,最后遍历右子树。

class Solution(object):
    def preorderTraversal(self, root):
        """
        :type root: TreeNode
        :rtype: List[int]
        """
        #根->左->右 递归
        if not root:
            return []
        return [root.val] + self.preorderTraversal(root.left) + self.preorderTraversal(root.right)

leetcode 94 二叉树的中序遍历

class Solution(object):
    def inorderTraversal(self, root):
        """
        :type root: TreeNode
        :rtype: List[int]
        """
        #左->根->右 递归
        if not root:
            return []
        return self.inorderTraversal(root.left) + [root.val]  + self.inorderTraversal(root.right)

leetcode 145 二叉树的后序遍历

class Solution(object):
    def postorderTraversal(self, root):
        """
        :type root: TreeNode
        :rtype: List[int]
        """
        #左->右->根 递归
        if not root:
            return []
        return self.postorderTraversal(root.left) + self.postorderTraversal(root.right) + [root.val]

leetcode 102. 二叉树的层序遍历

给你二叉树的根节点 root ,返回其节点值的 层序遍历 。 (即逐层地,从左到右访问所有节点)。
广度优先搜索(BFS去父留子):
当队列里还有东西时
取出队头元素,查看该头元素的left和right
如果该left/right还没出现过,存入queue

class Solution(object):
    def levelOrder(self, root):
        """
        :type root: TreeNode
        :rtype: List[List[int]]
        """
        if not root:
            return []
        res = []
        queue = [root]
        while queue:
            length = len(queue)
            level = []
            for i in range(length):
                node = queue.pop(0)
                #queue都是去父留子
                level.append(node.val)
                if node.left:
                    queue.append(node.left)
                if node.right:
                    queue.append(node.right)
                
            res.append(level)
        return res

leetcode104. 二叉树的最大深度

深度优先 一棵二叉树的最大深度可以通过子树的最大深度推导出来,这就是分解问题计算答案的思路。

class Solution(object):
    def maxDepth(self, root):
        """
        :type root: TreeNode
        :rtype: int
        """#递归
        if not root:
            return 0
        else:
        #整棵树的最大深度等于左右子树的最大深度取最大值,然后再加上根节点自己
            left_max = self.maxDepth(root.left)
            right_max = self.maxDepth(root.right)
            return max(left_max,right_max) + 1

leetcode 226 翻转二叉树

深度优先 前序遍历

class Solution:
    def invertTree(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
        if not root:
            return None
        print(root.val)
		# 将当前节点的左右子树交换
        root.left,root.right = root.right,root.left
		# 递归交换当前节点的 左子树和右子树
        self.invertTree(root.left)
        self.invertTree(root.right)
		# 函数返回时就表示当前这个节点,以及它的左右子树
		# 都已经交换完了		
        return root

leetcode 101 对称二叉树

我们将根节点的左子树记做 left,右子树记做 right。比较 left 是否等于 right,不等的话直接返回就可以了。
如果相当,比较 left 的左节点和 right 的右节点,再比较 left 的右节点和 right 的左节点

递归函数的两个条件:

  1. 终止条件:left 和 right 不等,或者 left 和 right 都为空
  2. 递归的比较 left.left 和 right.right,递归比较 left.right 和 right.left
class Solution:
    def isSymmetric(self, root: Optional[TreeNode]) -> bool:
        #递归的比较 left.left 和 right.right,递归比较 left.right 和 right.left
        if not root:
            return True
        def dfs(left,right):
            #逻辑表达式取非后,or and交换
            #递归的终止条件是两个节点都为空
            if not (left or right):
                return True
            #两个节点中有一个为空:不对称
            if not (left and right):
                return False
             #两个节点的值不相等:就不是对称二叉树
            if left.val!=right.val:
                return False
            return dfs(left.left,right.right) and dfs(left.right,right.left)
		# 用递归函数,比较左节点,右节点
        return dfs(root.left,root.right)

回溯算法

leetcode46 全排列

在这里插入图片描述
我们定义的 backtrack 函数其实就像一个指针,在这棵树上游走,同时要正确维护每个节点的属性,每当走到树的底层叶子节点,其「路径」就是一个全排列。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值