DataStructure

156 阅读8分钟

第三章:数组中的问题最常见

3-2

Eg:283 移动零
练:

3-5

数据结构与算法C++精解

Eg:75 颜色分类

  1. 计数排序
  2. 三路快排

练:

3-6

Eg:167 两数之和 II - 输入有序数组

  1. 暴力解法,双层遍历O(n^2)
  2. 当看到有序的数组时,我们要想到用二分搜索提速
  3. 对撞指针

练:

3-7、3-8

Tip: 双索引技术的应用之滑动窗口

Eg:209 长度最小的子数组

  1. 暴力解:遍历所有的连续子数组 [i...j],计算其和sum,验证sum >= s, O(n^3)-> 如何优化到O(n^2)
  2. 滑动窗口

练:

第四章:查找表相关问题

4-1、4-2

查找类问题:

  1. 查找有无?-某元素知否存在?Set

  2. 查找对应关系(键值对应)-某元素出现了几次?Map

Eg:

思考🤔:对于👆🏻这两个问题,如果数组是有序的,用什么方法更简洁?

4-3、4-4、4-5、4-6

map和set的底层实现为平衡二叉树
哈希表查找的时间复杂度是O(1),但是没有顺序性

练:

Eg:1 两数之和(一个使用查找表的经典问题)

  1. 暴力解法 O(n^2)
  2. 排序后,对撞指针O(nlogn) 📢:排序后如何保留原有索引值
  3. 查找表

练:

Eg:454 四数相加 II

  1. 暴力解法 O(n^4) ❌
  2. 查找表 O(n^3) ❌
  3. 优化查找表到 O(n^2)

练:49 字母异位词分组

Eg:447 回旋镖的数量

练:149 直线上最多的点数

4-7、4-8

滑动窗口+查找表

Eg:219 存在重复元素 II

练:217 存在重复元素

Eg:220 在重复元素 III

第五章:在链表中穿针引线

5-1**、5-2、5-3**

Key:链表相关(穿针引线:操纵next指针的指向问题)

Eg:206 反转链表

改变一个链表节点的指针指向,需要三个指针(pre, cur, next)

练:

Key:设置链表的虚拟头节点(使用完记得删除释放)

Eg:203 移除链表元素(如果删除的节点是头节点,要特殊处理)

练:

5-4

5-5  特殊的情况下,可以通过改变节点的值来处理链表问题

  • 237. 删除链表中的节点 (题目给定的就是要删除的节点,我们无法知道要删除节点的前一个节点,所以不能通过改变前一个节点next指针的方式删除,我们就只能改变当前节点的值=当前节点的下一个节点值,再删除当前节点的下一个节点。注意⚠️:边界的处理:node = null; node.next = null即node是最后一个节点)

5-6 链表与双指针

  1. 方法一:先遍历一边计算链表长度;再遍历一边删除倒数第n个节点

  2. 方法二:虚拟节点+双指针

第六章:栈,队列,优先队列

6-1 栈和队列的使用

**6-2 、**6-3 栈和递归的紧密关系

6-4 队列 Queue

队列的基本应用:广度优先遍历

树: 层序遍历

图:无权图的最短路径

6-5 BFS和图的最短路径

6-6 优先队列

****普通队列:先进先出,后进后出
****优先队列:寻找队列中的最大值或最小值优先出队

优先队列的底层实现:使用的是 这种数据结构
对于堆的底层实现,要重视,做到白板编程的程度(使用一个数组来模拟一棵树)

6-7

第七章:二叉树和递归

7-1 二叉****树和递归

7-2

满二叉树:所有层的节点数达到最大

完全二叉树:除了最后一层,所有层的节点数达到最大,与此同时,最后一层的所有节点都在最左侧。(堆使用完全二叉树)

**平衡二叉树:**每一个节点的左右子树的高度差不超过1

7-3 递归的终止条件

7-4

7-5

7-6  二分搜索树

**二分搜索树:**每个节点的键值大于左孩子;每个节点的键值小于右孩子;以左右孩子为根的子树仍为二分搜索树

第八章:递归和回溯法

8-1 8-2 树形问题

回溯法是暴力解法的有个主要实现手段

8-3 回溯算法的应用

8-4 组合问题

第九章:动态规划基础

9-1 斐波那契数列

// 递归
function fib (n) {  
    if(n == 0) return 0;
    if(n == 1) return 1;
    return fib(n-1) + fib(n-2);
}
时间复杂度:O(2^n)

// 记忆化搜索--自上而下的解决问题
// 优化:避免重复计算
var memo = []
function fib (n) {  
    if(n == 0) return 0;
    if(n == 1) return 1;
    if(memo[n]) {
        memo[n] = fib(n-1) + fib(n-2)
    }
    return memo[n];
}
时间复杂度:O(n), fib函数会执行2n-1

动态规划--自下而上的解决问题

function fib (n) {
    let memo = [];
    memo[0] = 0;
    memo[1] = 1;
    for(let i = 2; i <= n; i++) {
        memo[i] = memo[i-1] + memo[i-2];
    }
    return memo[n]
}
时间复杂度:O(n),不存在递归调用

动态规划:将原问题拆解成若干子问题,同时保存子问题的答案,使得每个子问题只求解一次,最终获得原问题的答案

9-2

9-3

通过求子问题的最优解,可以得到原问题的最优解

9-4 状态和状态转移

9-5 01背包问题

第十章:贪心算法