leetcode常用思路总结与文章索引

数据结构:一维数据结构、二维数据结构(树和图)、特殊;顺序存储结构和链式存储结构是按照存储方式不同来划分的;

算法:if-else & for loop & 递归—>找重复单元;树的遍历采用递归,因为无法有效的进行循环;
深度优先搜索、广度优先搜索、启发式搜索(优先级优先)A*、动态规划、二分查找(有序)、贪心、排序、数学、几何

两个限制:输入数据的规模和运行时间
时间复杂度:常数、对数、线性、平方、立方、指数、阶乘
常数阶<对数阶<线性阶<线性对数阶<平方阶<立方阶<指数阶<阶乘阶;
注意:
1、不考虑系数,指数级的时间复杂度不用在意他的底数
2、时间复杂度考虑的是,一条语句(可以是赋值,输出等语句)执行了多少次,而不是某条语句的复杂度

如何分析递归的时间复杂度?通过主定理计算分治和递归的时间复杂度
举例:二分查找O(logn),前提是有序;二叉树的遍历O(n);矩阵二分查找O(n),前提是有序;归并排序O(nlogn),所有排序的最优时间复杂度;二叉树、图、DFS和BFS遍历的时间复杂度都是O(n),n为(搜索空间)节点总数

空间复杂度:数组的长度:一维是n;二维是n^2;递归的深度
时间复杂度:子问题总数 * 每个子问题的时间;

数组、链表、跳表
数组元素访问的时间复杂度是O(1)、插入和删除时间复杂度是O(n)
链表元素访问的时间复杂度是O(n)、插入和删除时间复杂度是O(1)?双链表(不仅包含next指针,还包括prev指针,头结点的prev指向NULL);循环链表(尾指针指向头部);
跳表元素访问的时间复杂度是O(logn)、随着插入和删除的进行指针结构被破坏,导致插入和删除时间复杂度是O(logn),空间复杂度有所增加但仍然是O(n);由跳表触发的思考:升维,空间换时间

二叉搜索树:因有序使遍历和查找更加方便;递归生成;N叉树生成先确定孩子个数;python关于二叉树的解法:力扣 
具体有序的形式:一棵空树,或者满足一下特性:
左子树上所有结点的值均小于它的根节点的值,右子树上所有结点的值均大于它的根节点的值,以此类推左右子树也分别为二叉搜索树。(这就是重复性!)
普通二叉树遍历的时间复杂度是O(n);二叉搜索树很重要的一点是中序遍历是升序!因为具有二分的思想,所以查找、插入(创建)和删除的时间复杂度为O(logn);非叶子节点的删除:用右子树中最小的结点(或者左子树中最大的元素)替换删除结点

递归、分治和动态规划三者类似
递归:函数自己调自己,在递归状态树中发现了重复的计算,要学会使用缓存—>空间换时间
分治:把一个问题分开,分而治之,一般也是用递归,最后再合在一起
动态规划:递归和分治的思想+缓存(分治+记忆化搜索);【状态】、【选择】、求最值(最长子序列、最小编辑距离、最长公共子串)、自底向上or自顶向下(备忘录)
列递归树,发现重叠子问题;base case、dp table(备忘录)、自底向上;状态转移方程和问题本身有关;最优子问题:子问题的最优能导出原问题的最优;用dp table解决所有类型的股票买卖问题;
分治和回溯是一种特殊的递归或较为复杂的递归;回溯:做选择->添加路径->撤销选择;两个要点:结束条件and剪枝策略;
递归——>判定为递归问题后,先快速把模板写下来
1、抵制人肉递归的诱惑
2、最近重复子问题——>找到最近最简方法,将其拆解成可重复解决的问题(重复子问题)
3、数学归纳法思维
问题思考点:1、是否有重复计算;2、是否包含所有情况
斐波那契数列:1、傻递归;2、记忆化递归(使用内存,要么返回记忆,要么更新记忆,记忆要持续作为参数);3、动态规划(使用数组进行递归)
编写代码时的注意点:1、写递归的整体思路(模板);2、输入参数的不断优化;3、递归的化简(代码的优化)
搜索:遍历所有的结点,保证每个节点访问一次,最后找到结果。递归和栈有一定的联系;栈除了计算机维护,也可以程序员自己维护;递归相当于程序自动帮你维护一个栈;DFS用递归实现,或者用手动维护一个栈实现,或者通过循环结构(非递归)实现;BFS用队列实现;
暴力循环(迭代)and递归:递归自顶向下,迭代自底向上;
嵌套循环:确定好内外索引的边界;双重循环i=0和j=i+1
递归要义:1、递归终止条件;2、调用函数自己的函数的位置(是斐波那契数列那样求和的关系,还是二叉搜素树那样放在12、13、23位置的关系);
能用递归就不要用递归,要用空间换时间,而且空间往往可以缩减到常数级别(斐波那契数列具有滑动的特点,初始化是001,第一次更新后是011);
递归练习:22;易犯错误:1、scanf函数读取变量值时忘了写&,不需要去接收scanf函数的返回值;

图的表示方式:
1、链接矩阵,无向图是对称矩阵,有向图是非对称矩阵
2、链接表(链表)和链接矩阵相对应,对于有权的,将权值放在数值域

二叉堆(注意和二叉排序树的关系,只保证了最大值(或最小值在root处))。
它和二叉搜索树的任务完全不一样。它只是用来找最大值(或最小值),优先队列),要求较低,完全二叉树,python和java中都有现成的实现heap。分为最大堆和最小堆:
访问最大堆中的最大值的复杂度为O(1),访问最大堆中的最小值的复杂度为O(n),
访问最小堆中的最小值的复杂度为O(1),访问最小堆中的最大值的复杂度为O(n),
插入操作:按照完全二叉树的顺序先插入到末尾,再和它的根节点进行比较调整;
完全二叉树导致它存储结构上有一些特点:可以用数组进行存储,
根节点和左右子树节点的下标索引关系:i、2i和2i+1(注意根节点从下标1开始存储,而不是下标0,这样代码更优美)

按照下标来随机访问(look up)VS查找元素(search)
look up(下标已知,元素未知):链表O(n),数组O(1),跳表O(logn)?
search(元素已知,下标未知):链表和数组都是O(n),如果元素有序,则数组的二分查找为O(logn),链表不支持二分查找

栈(stack):先进后出,添加和删除的时间复杂度是O(1);查询的时间复杂度是O(n),因为元素无序;手动栈(递增栈84配合前后哨兵(避免特殊情况的讨论)and单调栈(42:接雨水,单调递减栈))42VS84:84是先计算再弹栈;42题是记录再弹栈再计算;and最小栈:每次入最小元素配合入栈元素一起入栈;有效括号20;python的dict为空时,pop()会报错,因此需要额外添加键值对:'?': '?';
队列(queue):先进先出,添加和删除的时间复杂度是O(1);查询的时间复杂度是O(n),因为元素无序;层序遍历(队列,一个不断出队和进队的过程,用DFS实现层序遍历102):1、二叉树为空则return NULL;2、二维数组每一维度都要分配空间;3、每遍历完一层要将列索引置零;4、要先判断结点是否有孩子结点再进行入队操作;
双端序列(deque):两端都可以进行push和pop;添加和删除的时间复杂度是O(1);查询的时间复杂度是O(n);
优先队列:填入元素的时间复杂度是O(1);取出元素的时间复杂度是O(logN),因为是按照优先级取出;
单调队列;
循环队列:解决顺序队列的假溢出问题;计算元素个数时,分两种情况判断:
Q.rear>= Q.front:元素个数为Q.rear-Q.front;
Q.rear<Q.front:元素个数为Q.rear-Q.front+ Maxsize;
队列中元素个数:(Q.rear-Q.front+Maxsize)% Maxsize

映射和集合是哈希表更抽象的数据结构;
哈希表:查询、添加和删除的时间复杂度都是O(1);
哈希表的size越小,哈希函数的选择不好,容易出现碰撞;碰撞——>拉链式解决冲突法(拉出一条链表);
在哈希表基础上抽象出来的数据结构map(python中的dict)和set;
map(dict):键值对(key-value)的存在,同时key不重复;set:不重复元素的集合;
treemap&treeset(由严格平衡的红黑树实现)的所有操作的时间复杂度O(log(n));hashmap&hashset的查询复杂度O(1);

贪心算法:局部最优导致全局最优;一旦一个问题可以通过贪心法来解决,那么贪心法一般是解决这个问题的最好办法。复杂度低?
贪心:当下做局部最优判断,不能回退;回溯:能够回退;动态规划:最优判断+回退,保存之间的运算结果;
适用贪心算法的场景:问题能够分解成子问题来解决,子问题的最优解能够递推到最终问题的最优解。贪心算法解决最优化问题:最小生成树、求哈夫曼编码;coin change:硬币之间存在整除的关系;分发饼干、买卖股票的最佳时机、55跳跃游戏(从后往前贪心);
这种子问题最优解称为最优子结构;如何判断问题可以使用贪心算法?将问题转化为可用贪心算法解决?从后往前使用贪心算法?

二分查找:
前提条件:1、目标函数单调性;2、存在上下届;3、能够通过索引访问;细节:1、mid的更新方式防止溢出;2、right = size or size-1 ?明确退出条件;3、left < right or left <= right ? 易犯错误:1、忘写没找到情况下的返回值了;2、循环条件写反了;
牛顿迭代法;
69.平方根;367.有效的完全平方数;74.搜索二维矩阵;
33.搜索旋转排序数组:
1、暴力:还原O(logN)—>升序—>二分:O(logN)
2、正解:二分查找

双指针
1、要考虑的问题是每一次循环是移动左指针还是右指针,2、留意指针重合是终止条件;
三数之和15:对数组排序+双指针;指向相邻的两个int类型的数据的指针相减等于1;
快慢指针判断链表是否有环141;
滑动窗口:场景:子串、子数组;need,window、valid
1、当移动right扩大窗口,即加入字符时,应该更新哪些数据?
2、什么条件下,窗口应该暂停扩大,开始移动left缩小窗口?
3、当移动left缩小窗口,即移除字符时,应该更新哪些数据?
4、我们要的结果应该在扩大窗口时还是缩小窗口时进行更新?

排序:
快排VS归并VS堆排序
快排:没有使用额外的空间,数组原地排序;
快排:1、临时变量推荐临时定义的写法;2、忘了写递归终点;3、pivot-1;
归并排序:分段排序;快排:只做交换;
归并:1、单行条件判断写?不用写if;2、判断大小时写小于等于号;3、在进行抄写的时候要写left+p,而不能用left++,因为left同时作为循环判断中的变量,使用前者会发生冲突;

C与python的数据结构对比:
C语言使用的系统栈对应python里面的list:C语言的系统栈靠编译器自动弹栈;python的list需要手动调用方法进行弹栈,但是python不需要手动维护索引;
python里面的dict在C语言里面需要用函数来实现;
python中判空和判NULL的方式是if not;整数除法用"//";
python的list可以通过调用sort方法进行排序;
python中交换两元素非常简单:a, b = b, a;

看看股票买卖、打家劫舍、N数之和的题解、背包问题、滑动窗口问题、凑零钱;LRU cache146,就是用双端链表(双端链表可能是一种更默认的数据结构)实现的;

本人刷题github地址(C和Python版本):GitHub - changshunyu/leetcode


 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

艺术家常

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

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

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

打赏作者

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

抵扣说明:

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

余额充值