近日刷算法分享

最近刷算法的一些想法,借此分享,如果有大佬看到,希望多多指教!

1.时间分配

1.1 碎片时间

因为上班时间比较忙,且通勤时间比较长(一天约3小时)所以大部分题都是在通勤路上刷的。

1.2 不死磕

感觉刚拿到一道题,看个5-10分钟之后,确保看懂了,要是还没有思路就直接看题解,题解要是还看不懂的就上b站看视频,要是还看不懂就先放一下,等下一次通勤的时候再看,尽量不死磕,感觉效率太低了。

2.实践

2.1 打断点方便理解

主要是一些题解其实就给了def的过程,其实并没有给出实际的例子,整体的跑下来,我觉得这里需要自己理解了之后,自己把这个过程写下来,包括实现的调用具体的参数,到出参,然后打断点,每一步去看是怎么走的,每一步走出来的样子是不是自己理解的样子,尽量确保自己真理解了。

2.2 整段的时间留给实践

刚刚上面说了,尽量把思考都放在碎片时间,有空有整段时间能坐下来忙自己的事的时候,再来实践已经有一定思路和理解的题。

-- 以上是自己的一些总结,感觉还是比较适合自己(上班族)的,供大家参考哈

-- 以下是一些自己刷过的题以及部分自己的注释和理解,不是很全,因为有的草稿在另外的电脑,没来得及放一起,以后想统一放到git上来弄,但是这篇文章拖了好久,先不追求完美,走到哪是哪吧,希望大家多多指教,谢谢!

0704 堆

# 堆: 了解下就好
# 一、堆是完全二叉树的结构
# 什么是完全二叉树:1.只允许最后一行不满 2.最后一行必须从左往右排,中间不能有间隔
# 二、堆序性 1.小根堆,父节点都要更小 2.大根堆,父节点都要更大
# 三、堆的存储,因为是完全二叉树,所以可以根据层序遍历,来得到一个数组,此时,父节点为i时,左右子节点一定为2i+1/2
# 四、堆有两个基本操作:1.上滤,通常用于插入新元素到根中时,向上调整位置时
# 2.下滤(因为必须要满足堆序性的话,所以对不满足的要操作),把根节点向下调整的操作叫下滤
# 五、自顶向下建堆法:1.插入堆 2.上滤
# 六、自下而上建堆法:对每个父节点进行下滤(从最下面的父节点开始)-- 复杂度O(N)
# 七、应用 1.优先队列:弹出最小元素 -- 可以用来实现堆排序,用大根堆排序完,弹出的是正序,小根堆反 2.插入:就是上滤

0703栈 最小栈

# 一个栈同时保留当前值和栈内最小值
class MinStack(object):

    def __init__(self):
        """
        initialize your data structure here.
        """
        self.stack = []
        

    def push(self, x):
        """
        :type x: int
        :rtype: void
        """
        if not self.stack:
            self.stack.append((x, x))
        else:
            self.stack.append((x, min(x, self.stack[-1][1])))
        

    def pop(self):
        """
        :rtype: void
        """
        self.stack.pop()
        

    def top(self):
        """
        :rtype: int
        """
        return self.stack[-1][0]
        

    def getMin(self):
        """
        :rtype: int
        """
        return self.stack[-1][1]


# Your MinStack object will be instantiated and called as such:
# obj = MinStack()
# obj.push(x)
# obj.pop()
# param_3 = obj.top()
# param_4 = obj.getMin()

0702 栈 判断括号

# !栈和列表(数组的概念)其实是一样的,排列的时候也是一样的
# !只是平时print的时候也只是查看而已
# !pop的时候自动内置了从-1位开始顶出,而且每次都只pop出一位
# append和clear都是都是一样的用

# 创建一个空栈
stack = []

# 将元素推入栈顶
stack.append(1)
stack.append(2)
stack.append(3)

# 弹出栈顶元素
top_element = stack.pop()
print(top_element)  # 输出:3

# 查看栈顶元素,但不弹出
top_element = stack[-1]
print(top_element)  # 输出:2

# 检查栈是否为空
is_empty = len(stack) == 0
print(is_empty)  # 输出:False

# 清空栈
stack.clear()
####################################################从这里才开始
class Solution:
    def isValid(self, s: str) -> bool:
        dic = {'{': '}',  '[': ']', '(': ')', '?': '?'}
        stack = ['?']
        for c in s:
            if c in dic: stack.append(c)
            elif dic[stack.pop()] != c: return False 
        return len(stack) == 1

0701二叉树的:

# 理解:主要都是运用的递归的思路,例前序,方法就是先把根节点调用自己 1  2  3
# 1已经调用完了,2再调用自己 就是2 4 5 那么把 2 替换成这个 就是  1 2 4 5
# 再到3,在调用自身的时候,已经没有左子树和右子树了,同时相当于碰到出发截止的条件了
# 最后就是 1 2 4 5 3 
#     1
#    / \
#   2   3
#  / \
# 4   5

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

class Solution:
    def preorderTraversal(self, root: TreeNode) -> list[int]:
        if not root:
            return []
        # 前序递归 根-左右
        return [root.val] + self.preorderTraversal(root.left) + self.preorderTraversal(root.right)
        # # 中序递归 左根右
        # return self.inorderTraversal(root.left) + [root.val] + self.inorderTraversal(root.right)
        # # 后序递归 左右根
        # return self.postorderTraversal(root.left) + self.postorderTraversal(root.right) + [root.val]

# 构建二叉树节点
root = TreeNode(1)
node2 = TreeNode(2)
node3 = TreeNode(3)
node4 = TreeNode(4)
node5 = TreeNode(5)

# 设置节点之间的连接关系
root.left = node2
root.right = node3
node2.left = node4
node2.right = node5

a = Solution()
b = a.preorderTraversal(root)
print(b)

#  还有一个看着可能更好理解的写法,但是也还是递归
# def preorderTraversal(self, root:TreeNode): -> list[int]
#     if root is None:
#         return []
#     res = []  # 结果列表
#     res.append(root.val)  # 将根节点的值添加到结果列表中
#     res += self.preorderTraversal(root.left)  # 递归地遍历左子树,并将结果连接到结果列表中
#     res += self.preorderTraversal(root.right)  # 递归地遍历右子树,并将结果连接到结果列表中
#     return res  # 返回结果列表

0629反转链表:

## !这一段要好好看下
## !同时以后不知道 怎么执行的时候也可以这么问:(这段代码能否引入实际的参数,实际的跑一遍看一下)
class ListNode:
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next

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

# 创建一个链表
head = ListNode(1)
node2 = ListNode(2)
node3 = ListNode(3)
node4 = ListNode(4)
head.next = node2
node2.next = node3
node3.next = node4

# 输出反转前的链表
current_node = head
while current_node:
    print(current_node.val)
    current_node = current_node.next

# 创建Solution对象,调用reverseList方法来反转链表
solution = Solution()
reversed_head = solution.reverseList(head)

# 输出反转后的链表
while reversed_head:
    print(reversed_head.val)
    reversed_head = reversed_head.next

0628 矩阵置零:

# ?写了两遍了,但是还有一个常数复杂度的还没看明白 再看
# 思路一: 用
# O(m+n)额外空间
# 两遍扫matrix,第一遍用集合记录哪些行,哪些列有0;第二遍置0
# !!在原地修改意味着在给定的矩阵上直接修改数据,而不创建新的数据结构来存储修改结果。因此,返回结果通常是不必要的,
# !! 因为修改后的矩阵已经在计算过程中被直接改变了。
# !! 一些算法设计的目标是尽量减少额外空间的使用,以提高算法的效率。在这种情况下,直接在原地进行修改是更好的选择。

from typing import List
class Solution:
    def setZeroes(self, matrix: List[List[int]]) -> List[List[int]]:
        """
        Do not return anything, modify matrix in-place instead.
        """
        row = len(matrix)
        col = len(matrix[0])
        row_zero = set()
        col_zero = set()
        for i in range(row):
            for j in range(col):
                if matrix[i][j] == 0:
                    row_zero.add(i)
                    col_zero.add(j)
        for i in range(row):
            for j in range(col):
                if i in row_zero or j in col_zero:
                    # 已经是 收集好位置的额外空间,重新再过一遍双遍历,看这些数有没有在这个额外空间出现
                    matrix[i][j] = 0
        return matrix

matrix = [[1,1,1],[1,0,1],[1,1,1]]
s = Solution()
ans = s.setZeroes(matrix)
print(ans)  # 之所以打印出来是None,如果需要的话,在返回结果那里可以修改输出的数据类型  #现在是已经修改过后的

0627 合并区间(数组)

# 当出现 NameError: name 'List' is not defined 的错误提示时,
# 通常是因为你在代码中错误地使用了 List,而 Python 解释器并不认识它,因为它只认识 list 作为关键字。
# 要解决这个错误,你应该将错误的 List 改为正确的 list 关键字使用。
# 或是 需要导入 List 类型,并结合类型提示来使用。例如:from typing import List
# 数组里的题,但是不确定是不是动态规划的感觉?

class Solution:
    def merge(self, intervals: list[list[int]]) -> list[list[int]]:
        intervals.sort(key=lambda x: x[0])

        merged = []
        for interval in intervals:
            # 如果列表为空,或者当前区间与上一区间不重合,直接添加
            if not merged or merged[-1][1] < interval[0]:
                merged.append(interval)
            else:
                # 否则的话,我们就可以与上一区间进行合并
                merged[-1][1] = max(merged[-1][1], interval[1])

        return merged

intervals = [[1,3],[2,6],[8,10],[15,18]]
a = Solution()
b = a.merge(intervals)
print(b)

0626 最大数组和(动态规划)

# 给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
# 子数组 是数组中的一个连续部分。
# 输入:nums = [-2,1,-3,4,-1,2,1,-5,4]
# 输出:6
# 解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。
# !!其实,就是要返回 
from typing import List
class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        size = len(nums)
        if size == 0:
            return 0
        dp = [0 for _ in range(size)]

        dp[0] = nums[0]
        for i in range(1, size):
            if dp[i - 1] >= 0:
                dp[i] = dp[i - 1] + nums[i]
            else:
                dp[i] = nums[i]
        return max(dp)

nums = [1, -2, 3, 4, -1, 2, 1, -5, 4]
solution = Solution()
result = solution.maxSubArray(nums)
print(result)

0622 相交链表

给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null 。

例:

输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,6,1,8,4,5], skipA = 2, skipB = 3

输出:Intersected at '8'

思路就是只要相交了,两边同时走的长度都是一样的,a+c+b = b + c + a

class Solution:
    def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
        A, B = headA, headB
        while A != B:
            A = A.next if A else headB
            B = B.next if B else headA
        return A

-- 但是这样出不来值,因为ListNode没有自带这样的数据结构,所以需要自定义 ListNode

-- 下面这个不知道要怎么结合实际的跑? -- 而且 出来的值也很奇怪

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

# 创建链表1
list1 = [4,1,8,4,5]
headA = ListNode(list1[0])
currA = headA
for i in range(1, len(list1)):
    currA.next = ListNode(list1[i])
    currA = currA.next

# 创建链表2
list2 = [5,6,1,8,4,5]
headB = ListNode(list2[0])
currB = headB
for i in range(1, len(list2)):
    currB.next = ListNode(list2[i])
    currB = currB.next

pA, pB = headA, headB
while pA != pB:
    pA = pA.next if pA else headB
    pB = pB.next if pB else headA

intersection_node = pA

if intersection_node:
    print("相交节点值为:", intersection_node.val)
else:
    print("链表不相交")

# 不知道为什么就是跑不出来

-- gpt题解:

要解决相交链表问题,你可以按照以下步骤进行:

  1. 定义一个链表节点类ListNode:
python


复制
class ListNode:
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next
  1. 根据给定的链表值,创建两个链表listA和listB:
python


复制
# 创建链表1
list1 = [4,1,8,4,5]
headA = ListNode(list1[0])
currA = headA
for i in range(1, len(list1)):
    currA.next = ListNode(list1[i])
    currA = currA.next

# 创建链表2
list2 = [5,6,1,8,4,5]
headB = ListNode(list2[0])
currB = headB
for i in range(1, len(list2)):
    currB.next = ListNode(list2[i])
    currB = currB.next
  1. 找到两个链表的相交节点,可以使用双指针法。定义指针pA和pB分别指向链表headA和headB的头节点,同时遍历两个链表,当其中一个指针到达链表尾部时,将其指向另一个链表的头节点,直到两个指针相等或者都为None为止。
python


复制
pA, pB = headA, headB

while pA != pB:
    pA = pA.next if pA else headB
    pB = pB.next if pB else headA

intersection_node = pA
  1. 最后,打印相交节点的值。
python


复制
if intersection_node:
    print("相交节点值为:", intersection_node.val)
else:
    print("链表不相交")

完整的代码如上:

0621 无重复字符最长子串(滑动窗口)

#写的时候if先,但其实这一层判断的时候是左指针了,但是需要布局好 下面的 while时右指针先玩明白了才会出来给左指针

#哈希集合,空集合的时候只能用set()而不能用{},因为后者默认是字段

class Solution:
    def zxzfc(self, s: str) -> int:
        occ = set()
        n = len(s)
        # rk就是右边界,之所以要从-1开始,是因为下面的while条件有要+1才好循环
        rk, ans = -1, 0 # 0 也是初始值
        for i in range(n):
            if i != 0: #需要先构思好,while的时候就会一直移动右指针
                occ.remove(s[i - 1])
            # 这里的rk初始值是0
            while rk + 1 < n and s[rk + 1] not in occ:
                occ.add(s[rk + 1])
                rk += 1
            ans = max(ans, rk - i + 1) #要么就是这一次循环的的长度的最大,要么就是上一次
        return ans
    
s1 = Solution()
r = s1.zxzfc("aaaaabcccccdeeeeefhjkl")
print(r)

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

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

class Solution:
    #前提是不管哪一段最长重复,总要从一个位置开始,一个位置结束
    #那么每个位置的开始都需要去遍历
    #然后从该位置后的下一个位置也都需要去遍历
    def cfzfc(self, s: str) -> int:
        occ = set()
        n = len(s)
        rk, ans = -1, 0
        #!!这里的rk是右指针,因为下面的条件就是移动+1,所以起始位置才能是0
        # ans是answer呀,输出的东西
        for i in range(n):
        # !!这里的i才是左指针
            if i != 0:
                occ.remove(s[i-1]) 
                #从occ集合里移除s字符串的第i-1位
                #所以上面要加上i != 0,因为=的话就移除第一位了
                #所以range的第一步出来的0直接往后的while走
            while rk + 1 < n and s[rk + 1] not in occ:
                #这里的条件的意思是边界值不能超过s的长度,以及s[rk + 1]没有在集合里,所以才有下面的加
                occ.add(s[rk + 1])
                rk += 1
            ans = max(ans, rk - i + 1)
            # !!这里也很关键,就是这里才开始对比,前一次的不重复长度,和下一次的不重复长度的
        return ans
sl = Solution()
r = sl.cfzfc("abc")
print(r)           

--

0620 字母异位词分组

class Solution:
    def groupAnagrams(self, strs: List[str]) -> List[List[str]]:
        mp = collections.defaultdict(list)


        for st in strs:
            key = "".join(sorted(st))
            mp[key].append(st)
        
        return list(mp.values())


a = Solution()
a1 = a.groupAnagrams(["eat", "tea", "tan", "ate", "nat", "bat"])
print (a1)

没跑出来还,报 Type "help", "copyright", "credits" or "license" for more information.

-- 好像是因为 没有开头那行

name 'collections' is not defined

-- 后面开头的时候又引入了这一行 import collections

-- 然后跑文件的时候跑不动,不知道为什么选段又可以了

-- 然后后面就都可以跑了

0619 -- 两数之和

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target  的那 两个 整数,并返回它们的数组下标。

你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。

你可以按任意顺序返回答案。

著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

1.暴力解 两次 循环

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 []


a = Solution()
a1 = a.twoSum([1,2,3],5)
print (a1)

2.哈希表 -- 看的不是很懂还 -- 看懂了 主要是相当于有一个容器来装,反正两个数都必须从这里面出来

class Solution:
    def twoSum(self, nums: List[int], target: int) -> List[int]:
        hashtable = dict()
        for i, num in enumerate(nums):
            if target - num in hashtable:
                return [hashtable[target - num], i]
            hashtable[nums[i]] = i
        return []


a = Solution()
a1 = a.twoSum([1,2,3,10,0,22,55],32)
print (a1)  # 输出 [3, 5]

enumerate函数 是返回key-value对的感觉,对于其他类型也是一样

-- hashtable = dict() 不理解

-- 为什么 后面可以直接用 这里面感觉也没东西呀

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值