Leetcode 刷题 一点点心得

前沿:作者近段时期刚完成跳槽,在准备期间也刷了一部分的题,对于里面部分的内容有一些小小的新的体会,特此记录以供后续查看,如果大家有兴趣也可以学习,希望会有帮助。

常用到的几个 包

1、from collections import deque

python 中的 collections 模块
里面包含了很多较为高级的数据结构,比如 队列 哈希表等,可以在刷题的时候使用

队列的使用方法
初始化 a =deuqe() or deque(list) ,pop()为弹出最右端(队尾),popleft为弹出队首元素(常用),len(),append()

2、import heapq

最大/最小 堆的基础使用方法

初始化/添加:q = [xxxx] heap.heapify(q) or q= [] for… heapq.heappush(q, number)

最小元素: q[0] 删除: heapq.heappop(q)

Python标准库模块之heapqleetcode中的heapq的使用-带key的排序

3、import sys

牛客/ACM提交格式
获取输入有两种格式: input() and string=sys.stdin.readline().strip() 只能获得一行的输入


常见到的题型:

树相关:
基本上就是树的几种遍历的来回变换,记住树的递归与迭代法的 前 中 后 层序遍历,要非常的熟悉,基本上就是这几道题,着重可以理解下非递归的思想
二叉树中的最大路径和,这一题比较经典,可以看看代码中的返回和处理结果是怎样进行的
数组相关:
一般使用指针来做,有几个删除重复项的题,关键就是双指针的思想,直接再原始数组上就可以修改和处理,遇到重复的慢的不动,否则一起加一,一起运动,同理荷兰国旗也是一样的道理
链表相关:
1. 两两反转链表,还是需要提前弄懂逻辑,在纸上先画好,然后再写代码就会好很多(25题 K个一组反转链表的简易模式)
2. 链表操作中需要注意的点,比如两个赋值,a = b,改变b或者a的next,其余的next也会跟着变化,相当于两个指针指向了一个地址,但是这两个指针可以改变指向的地址,这时候另外一个就不再随着变化了。
3. 链表中最常用的是 dummy 节点,使用场景很多,并且还有快慢指针不要忘了,两大神器
4. while 循环中如果再嵌套 while 循环的话,里面的while的条件需要把外界的加上,否则就回越界出错
5. 链表中涉及到倒数第几个节点得处理问题,要么先遍历获得所有的length,要么就使用快慢指针的方式,时间复杂度更低
英文字母相关:
涉及到 26 个字母相关的一把都用 哈希表 来解决,因为都是 常数级别 的 哈希
单调栈
是一个比较使用的数据结构,单调栈的介绍以及一些基本性质,其中我认为比较有用的一点是:使用单调栈可以找到元素向左遍历第一个比他小的元素,也可以找到元素向左遍历第一个比他大的元素。

单调栈的一些应用

关于递归的一些想法
  • 主要就是考虑好 结束条件就行,什么时候结束,然后 return的时候返回递归函数进行循环递归调用,不能单独的进行调用,而不做任何的操作
  • return 递归函数也行,或者return 定义的一个变量(重建二叉树),比如 定义root,root.left = 递归 , root.right=递归, 返回 root, 分不同的情况使用不同的方法,加油!
DFS为深度优先遍历,BFS为广度优先遍历
- 那么这两个的区别是什么啊?
- 深度优先遍历的话一般是一条道路往下走,不行再回溯,常见的实现方式就是递归,递归就是先一条道往下走,不满足条件再回溯。广度优先搜索就是优先进行层析的搜索,一般的实现方式就是使用双端队列,当访问到一个节点时候就将其附近的节点加到队列中,以此往复,相当于层次往外铺开,这里可以参考 leetcode 中机器人的运动范围 一题中的解法
  • Note:
    深度优先遍历 其实就是回溯的思想,回溯里面的关键点就在于先处理还是后处理,还是先处理完在后处理。
    广度优先遍历就是使用队列的思想不断的添加附近节点,然后删除再添加,直到没有符合条件的,队列为空退出。
    动态规划就是从下到上进行求解,关键点在于转移方程的具体含义的确定。
快排和归并的思考
快排和归并是两个相反的过程,一个是从上到下,另外一个是从下往上,所以对应的程序也是相反的,快排是先进行处理再递归,而归并是先进行递归再处理

这里有一个很清晰的程序-快排+归并

哈夫曼树
原本设计为了最短字符编码设计的,根据概率进行的分割,每个字符(变量)都是叶子节点,所以不同字符之间的编码不可能存在 比如添加一位就i是其他的字符,每个字符都是独立的,哈夫曼树原理,及构造方法
贪心算法
贪心算法的几个案例,都是可以通过数学表达式推到来的
特殊排序算法
有O(N)复杂度的排序算法,其中有计数排序,基数排序,桶排序,其中在 leetcode 164 最大间距 上这几个算法有应用,其中基数排序相对于计数排序来说,使用的空间相对较小,一般来说只用10*位数个大小就行,而计数排序则需要很大的空间(和bitmap的思想有点像)
动态规划
主要还是找到状态转移矩阵,做的时候的疑问就是转移矩阵的参数主要标识什么,是一维的还是二维的,解决了这些就好办了。根本上说:动态规划就是枚举,只不过是不重复的枚举

实现细节:

1、python 添加列表的时候出现值为空或者需要 append(list(A)),这是因为 append 只是添加引用,并没有拷贝,当引用对象发生改变时,对应的值也会改变。资料链接
2、比如在一个函数中定义另外一个内部函数,在内部函数中如果想用到外部函数定义的变量,有两种情况,对于列表等,直接在外部函数中定义,就可以在内部函数中使用,对于常规变量,必须在外部函数中定义或者在init函数中定义,且必须加上 self 才能使用
3、BFS 和 DFS 的用法十分相似,常见的 DFS 有两种实现,分别是递归的方式和迭代的方式(非递归),其中递归的方式没有任何的问题,直接模板就行,但是迭代的方式也是使用 列表的方式,和BFS的主要区别在于,DFS的stack是直接尾部添加然后尾部pop,但是BFS从头部进行的pop(0)(或者使用的popleft)(这两种迭代方式我个人都是基于list实现的),细细想想就是这样的效果,基于迭代的方式两种方法切换特别方便
4、 list中使用pop比使用remove的时间更短一些,使用remove可能会超时,list中如果索引越界,就是空数组,不调用的话不会报错的
5、二叉搜索树的左右子树是有序的,如果想排序,直接对应着 中序遍历

刷题顺序:

先把树都刷完了,了解并掌握树的思想和框架(该处可以掌握递归和回溯的思想,对于后续理解动态规划等其他系列会有很大的帮助)。
然后就可以看 labuladong 的各个系列,一定要实际写代码,有很多的细节,比如二分查找,二分查找左右边界等

资料:

labuladong 的 github

负雪明烛的博客,刷了1K+题,还有题模板

上面群主建立的一个监督书体打卡和交流的地方




易出错且高频相关代码:

#********************************************************

前序遍历

#********************************************************

class Solution:
    def preorderTraversal(self, root: TreeNode) -> List[int]:
        # 根 左 右
        if not root: return 
        stack, res = [], []
        stack = [root]
        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

#********************************************************

中序遍历

#********************************************************

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

#********************************************************

后序遍历

#********************************************************

class Solution:
    def postorderTraversal(self, root: TreeNode) -> List[int]:
        # 左右根,压入顺序为 根 右 左
        if not root: return 
        stack, res = [], []
        stack = [(root, False)]
        while stack:
            node, visited = stack.pop()
            if visited:
                res.append(node.val)
            else:
                stack.append((node, True))
                if node.right: stack.append((node.right, False))
                if node.left: stack.append((node.left, False))
        return res

#********************************************************

快速排序

#********************************************************

def partition(nums, start, end):
    if len(nums)<=1 or start>=end: return
    node = nums[end]
    left = start
    right = end - 1
    while left <= right:  # 一定是小于+等于
        while left<=right and nums[left]<=node: left +=1
        while left<=right and nums[right]>=node: right -=1
        if left<=right: # 此处一定要有一个判断,否则会排序出错的
            nums[left], nums[right] = nums[right], nums[left]
        
    nums[left], nums[end] = nums[end], nums[left]

    return left

def quick_soret(nums, start, end):
    if len(nums)<=1 or start>=end: return
    left = partition(nums, start, end)
    quick_soret(nums, start, left-1)
    quick_soret(nums, left+1, end)

#********************************************************

归并排序

#********************************************************

实际写的时候和快速排序不一样, 这里不用索引,用到索引反而麻烦
def merge_core(left, right):
    i, j = 0, 0
    result = []
    if not left: return right  # 这句话一定要加上,不然会报错的
    if not right: return left
    while i<len(left) and j<len(right):
        if left[i]<=right[j]:
            result.append(left[i])
            i+=1
        else:
            result.append(right[j])
            j+=1
               
    result += left[i:]
    result += right[j:]
    return result

def merge(nums):
    if len(nums)<=1:
        return nums
    index = len(nums)//2
    left = merge(nums[:index])
    right = merge(nums[index:])
    return merge_core(left, right)

#********************************************************

二分法获得左右边界

#********************************************************

def get_left_index(nums, target):
    left, right = 0, len(nums)-1
    while left<=right:
        mid = left+(right-left)//2
        if target<=nums[mid]:
            right = mid-1
        else:
            left = mid+1
    print(left)
def get_right_index(nums, target):
    left, right = 0, len(nums)-1
    while left<=right:
        mid = left+(right-left)//2
        if target<nums[mid]:
            right = mid-1
        else:
            left = mid+1
    print(right)
    ```
已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 精致技术 设计师:CSDN官方博客 返回首页