leetcode个人刷题小总结(套路篇)

写的不对的敬请指正

# 1 | 动态规划

## 动态规划三要素:重叠子问题,最优子结构,状态转移方程

- 重叠子问题


比如求解斐波那契数列,不带备忘录的递归方法中,画出递归树后,发现某些值(例如图中18和17等)要重复计算很多次,这就是重叠子问题

- 最优子结构

    - 子结构之间相互独立

    学高中集合时,大家一定听过这句话:不重不漏。</br></br>

    在动态规划中,简而言之:**一定满足不漏**,要把所有情况考虑在内;**某些问题**,例如求和等还要不重,但求最值的子结构就**可以重复**


    - 子结构的最优解可以推出原问题的最优解

    子结构的最优解可以推出原问题的最优解不是动态规划独有的


- 状态转移方程

    - 描述状态间的转化关系


## 动态规划解题五步

1. **确定DP数组的含义,包括数组下标和数组值的含义**

一般来说,`dp[i]`表示当输入数据为`i`时的问题答案

2. **确定状态转移方程**

当前位置的状态怎么转移而来?

3. **确定初始值**

也就是base case的值,不能由状态转移方程得到

4. **确定遍历顺序和得到答案方式**

遍历时,应该考虑状态转移方程中得到当前位置值依赖哪些位置的值,所依赖的状态必须在当前位置之前被计算出来</br></br>

得到答案的方式指的是,某些题目的答案为遍历最后一个位置,有些题目的答案则要求取遍历过程中每一个位置的最大值等等

5. **举例推导DP数组**

验证答案的正确性

# 2 | 回溯算法

回溯算法的**本质是暴力枚举**,解决的是:某些问题想暴力遍历结果连代码都写不出来的问题</br></br>

回溯法解决的问题都可以抽象为树形结构,**遍历过程可以抽象为对决策树的遍历**</br></br>

## 需要思考三个问题:

- **终止条件**

决策树到叶子,此时的情况时没有选项可选或者路径已经不满足题目的要求

- **已走路径**

保存此时的已选项,一般情况下,到终止条件时直接把路径加到结果集合里就完事了

- **待选择列表**

可以选择的项目,这里关系到是否需要函数的参数列表是否需要`startIndex`和`for`循环中`i`的初始值

</br></br>

## 模板:

```java

    private List<List<Integer>> res = new ArrayList<>();


    public void solutionFuncion(){

        backtrack();

        return res;

    }

    private void backtrack(List<Integer> path){

        if (结束条件){

            res.add(path);

            return;

        }

        for (){

            // 选择

            backtrack();

            // 撤销选择

        }

    }

```

## 去重

- **选择列表去重**(树叶,同一层,横向)

需要排序和`used`数组,做选择和撤销选择时也要考虑`used`数组

```java

if (i > 0 && nums[i] == nums[i - 1] && used[i - 1] == false) {

    continue;

}

```

- **路径去重**(树枝,纵向)

需要在`for`循环中的递归函数传递`startIndex`时进行`+1`操作

# 3 | 双指针

双指针的应用在数组和链表的题目中尤为常见

## 3 . 1  | 快慢指针

一快一慢两个指针同向而行<br><br>

解决的问题:

- 判断链表是否成环

- 求出环入口

- 找链表中点

- 倒数第`k`个节点<br><br>

我的初始化习惯:一快一慢的指针从链表头和链表头下一个节点出发,快指针每次走两步,慢指针每次走一步

```java

ListNode slow = head, fast = head.next;

while (fast != null && fast.next != null){

    // do something

    slow = slow.next;

    fast = fast.next.next;

}

```

## 3 . 2  |左右指针

一左一右两个指针相向而行<br><br>

解决的问题:

- 二分查找

- 两数之和

- 反转数组

## 3 . 3  |二分查找

二分查找其实是左右指针的应用之一,但由于其**思想的简单和细节的困难**,值得单独注意<br>

以这题为例:[[704. 二分查找]](https://leetcode-cn.com/problems/binary-search/)

### 整形溢出

```java

        int a = Integer.MAX_VALUE;

        int b = Integer.MAX_VALUE;

        System.out.println((a + b) / 2);        // 错误,整形溢出

        System.out.println(a + (a - b) >> 1);    // 错误,位运算优先级最低

        System.out.println(a + (a - b) / 2);    // 正确

        System.out.println((a + b) >>> 1);      // 正确,>>> 就算溢出也能得到正确答案

```

<br>

### 循环不变量

循环不变量指的是在循环中我们应该保持的定义,理解循环不变量对二分的细节很重要!<br><br>

对窗口的定义是确定循环不变量的第一步,个人来说一半选择`左闭右闭`区间,即窗口是`[left, right]`

- 初始化

因为`左闭右闭`区间的窗口是`[left, right]`,所以初始化

```java

int left = 0;

int right = nums.length - 1;

```

- 循环条件

循环条件为窗口满足定义的状态,`left == right`时窗口内有一个元素,满足条件,很显然循环条件为

```java

while(left <= right)

```

- 循环中收缩方式

为什么是`left = middle + 1`和`right = middle - 1`呢?

我们知道,此时的`middle`不满足题目要求,所以要进行收缩,因为`middle`已经不满足被排除在外了,自然在`[middle + 1,rihgt]`和`[left,middle - 1]`内搜索就可以了

```java

if (nums[middle] == target) return middle;

else if (nums[middle] < target) left = middle + 1;

else if ((nums[middle] > target)) right = middle - 1;

```

- 循环结束时状态

根据循环结束条件,推出循环时不满足`left <= right`并且循环内的收缩方式是每次收缩一个方向的一位,所以循环结束时,有`left = right + 1`<br><br>

循环结束条件有时需要用于对答案的判断,不过二分查找倒是用不到

### 完整代码:

```java

class Solution {

    public int search(int[] nums, int target) {

        // 特殊条件提前返回

        if (target < nums[0] || target > nums[nums.length - 1])

            return -1;

        // 搜索区间为[left, right],左闭右闭

        int left = 0, right = nums.length - 1;

        while (left <= right) {

            // 不能放溢出,但能防溢出出错

            int middle = (left + right) >>> 1;

            if (nums[middle] == target)

                return middle;

            else if (nums[middle] < target)

                left = middle + 1; // 注意

            else if (nums[middle] > target)

                right = middle - 1; // 注意

        }

        return -1;

    }

}

```

```java

class Solution {

    public int search(int[] nums, int target) {

        if (target > nums[nums.length - 1] || target < nums[0])

            return -1;

        // now the window is [left, right)

        int left = 0, right = nums.length;

        // if left == right, no element in the window because of [)

        while (left < right){

            int middle = left +  (right - left) / 2;

            if (nums[middle] == target)

                return middle;

            else if (nums[middle] < target)

                left = middle + 1; // [left + 1, right]

            else if (nums[middle] > target)

                right = middle;  // [left, right)

        }

        return -1;

    }

}

```

### 变形

[33. 搜索旋转排序数组](https://leetcode-cn.com/problems/search-in-rotated-sorted-array)<br><br>

[34. 在排序数组中查找元素的第一个](https://leetcode-cn.com/problems/find-first-and-last-position-of-element-in-sorted-array)<br><br>

[35. 搜索插入位置](https://leetcode-cn.com/problems/search-insert-position)

## 3 . 4  |滑动窗口

思想:增大窗口,满足条件时,试图缩小窗口,直到条件不再满足,再增大窗口……<br><br>

`for`循环找可行解,`while`循环找最优解<br><br>

难点:窗口状态的维护和细节<br><br>

模板:

```java

int left = 0;

for (int right = 0; right < array.length; right++){

    char ch = array[right];

    /* 维护添加ch后的窗口状态 */

    // 因为是for循环所以不需要right++

    while (满足题目要求){

        char delete = array[left];

        /*  维护删除delete后的窗口状态 */

        left++;

    }

}

```

例题:[leetcode题解](https://leetcode-cn.com/problems/find-all-anagrams-in-a-string/solution/zheng-zong-hua-dong-chuang-kou-by-zhengl-deue/)

# 4 | DFS

再次理解二叉树的遍历:<br><br>

何为先序?对节点的处理在进入节点前<br><br>

何为后序?对节点的处理在出节点后<br><br>

之前说过,**回溯算法就是对决策树的遍历**,所有**做出选择在调用递归方程前**,**撤销选择在调用递归方程后**<br><br>

刷题这么久才领悟到这一点,实在惭愧

# 5 | BFS

BFS的二叉树模板:

```java

public List<List<Integer>> levelOrder(TreeNode root) {

        Deque<TreeNode> queue = new ArrayDeque<>();

        if (root == null)

            return list;

        queue.offerLast(root);

        while (!queue.isEmpty()){

            // 一定要用零时遍历储存size,因为队列的大小是在动态变化的

            int size = queue.size();

            List<Integer> each = new ArrayList<>();

            for (int i = 0; i < size; i++){

                TreeNode node = queue.pollFirst();

                /*  对节点的处理  */

                // 例如 eachLevel.add(node.val);

                if (node.left != null)

                    queue.offerLast(node.left);

                if (node.right != null)

                    queue.offerLast(node.right);

            }

            /*  对层的处理  */

            // 例如 level++,res.add(eachLevel)等等

        }

        return list;

    }

```

理解:

- BFS适用于**找最短路径**,因为所有节点是**齐头并进**的,一旦某个节点找到了重点就可以停止<br><br>

- 如果是在**图**的BFS中,要用`visited`哈希表来**去重**,防止走回头路,但在二叉树中不需要因为没有子节点指向父节点的指针<br><br>

- 对于知道终点的BFS,可以优化为双向BFS来用空间换时间,不过渐进时间复杂度和空间复杂度都不变<br><br>

- 空间复杂度较高,为`O(n)`<br><br>

- DFS也可以找最短路径,空间复杂度为`O(lgn)`,因为递归产生的栈最多为二叉树的高度

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容

  • 动态规划 111. 爬楼梯思路类似斐波那契数列注意考虑第 0 阶的特殊情况 272. 爬楼梯 II思路类似上题,只...
    6默默Welsh阅读 2,409评论 0 1
  • 从尾到头打印链表输入一个链表的头节点,从尾到头反过来返回每个节点的值(用数组返回)。示例 1:输入:head = ...
    拼搏男孩阅读 520评论 0 0
  • 思路总结 数组: 数组内顺序: 是否有序? 如果排序,是否会有额外的性质? 递增、递减在该题内的含义? prefi...
    童言铜盐阅读 1,150评论 0 0
  • 做题,实际写出例子,然后分析可能遇到的情况,慢慢的,思路就会出来了。 线性表 33. Search in Rota...
    小碧小琳阅读 1,576评论 0 2
  • 3-1.数组中重复的数字 思路分析:如果不考虑时间复杂度,则可以先对数组排序(需要 的时间),然后再从中找重复的...
    oneoverzero阅读 366评论 0 1