Leetcode100 二叉树的中序遍历

思路

这题的关键是同时满足两个要求:

  • O(1) 查找 :所以要用 HashMap。
  • 维护最近使用顺序 :所以要用 双向链表。

只用 HashMap 可以 O(1) 找到 key,但不知道哪个是"最近最少使用"的;

只用链表可以维护顺序,但查找是 O(n)。

所以要把两者结合起来。

数据结构设计

维护一个 双向链表,链表头部表示最近使用的,尾部表示最久未使用的。

同时维护一个 HashMap<Integer, Node>,用来 O(1) 找到某个 key 对应的节点。

  • get(key):

    • 如果 key 不存在,返回 -1。
    • 如果存在,把对应节点移到链表头部,表示最近使用过,然后返回它的值。
  • put(key, value):

    • 如果 key 已存在,更新node里的value值,并把节点移到链表头部。
    • 如果 key 不存在:
      • 先新建节点,插入链表头部,并放入 map。
      • 再判断如果超过容量,就删除链表尾部节点,同时从 map 中移除。

movetohead:把当前链表中有的节点移到最前面 先removenode再addtohead

addtohead:把新建的节点插入到头部

removetail:移除tail.prev即最后一个节点

removenode:移除链表中指定节点

初始:head <-> tail

head <-> 1 <-> tail

假设当前是 head <-> A <-> tail,要插入新节点 X:注意顺序

复制代码
执行前:  head <-> A <-> tail

第1步 node.next = head.next     X.next = A
第2步 node.prev = head          X.prev = head
第3步 head.next.prev = node     A.prev = X
第4步 head.next = node          head.next = X

执行后:  head <-> X <-> A <-> tail
removeNode(node)

假设当前是 head <-> A <-> B <-> C <-> tail,要删除 B:

复制代码
执行前:  A <-> B <-> C

第1步 A.next = C     (B的前驱指向B的后继)
第2步 C.prev = A     (B的后继指向B的前驱)

执行后:  A <-> C      (B被摘掉)
moveToHead(node)

就是先摘掉,再插到头部。比如:

复制代码
head <-> A <-> B <-> C <-> tail

moveToHead(B):

先 removeNode(B):  head <-> A <-> C <-> tail
再 addToHead(B):   head <-> B <-> A <-> C <-> tail
removeTail()

直接取 tail.prev,就是最久未使用的节点,然后把它摘掉。

class LRUCache {

复制代码
class Node{

    int key;

    int value;

    Node prev;

    Node next;

    public Node(int key,int value ){

        this.key=key;

        this.value=value;

    }

}

private final int capacity;

private final Map<Integer,Node> map;

private final Node head;

private final Node tail;



public LRUCache(int capacity) {

    this.capacity=capacity;

    this.map=new HashMap<>();

    head=new Node(0,0);

    tail=new Node(0,0);

    head.next=tail;

    tail.prev=head;

}

public int get(int key) {

    if(!map.containsKey(key))

        return -1;

    Node node=map.get(key);

    movetohead(node);

    return node.value;

}

public void put(int key, int value) {

    if(map.containsKey(key)){

        Node node=map.get(key);

        node.value=value;

        movetohead(node);

        return;

    }

    Node node=new Node(key,value);

    map.put(key,node);

    addtohead(node);

    if(map.size()>capacity){

       Node lastnode=tail.prev;

       removetail();

       map.remove(lastnode.key);

       return;

    }

}



private void addtohead(Node node){

    head.next.prev=node;

    node.prev=head;

    node.next=head.next;

    head.next=node;

}

private void removenode(Node node){

    node.prev.next=node.next;

    node.next.prev=node.prev;

}

private void movetohead(Node node){

    removenode(node);

    addtohead(node);

}

private Node removetail(){

    Node lastnode=tail.prev;

    removenode(lastnode);

    return lastnode;

}

}

/**

  • Your LRUCache object will be instantiated and called as such:

  • LRUCache obj = new LRUCache(capacity);

  • int param_1 = obj.get(key);

  • obj.put(key,value);

*/

二叉树的中序遍历

中序遍历:左根右

思路:

1.递归 dfs(左子树) 根 dfs(右子树)

  1. 先递归遍历左子树
  2. 再访问当前节点(把值加入结果)
  3. 最后递归遍历右子树

2.用栈模拟递归stack

  1. 一路向左:从当前节点开始,不断往左走,把沿途节点全部压栈。这样栈顶就是最左边的节点(应该最先访问)。
  2. 弹栈访问:弹出栈顶节点,把它的值加入结果(这就是"根"的位置)。
  3. 转向右边 :把当前指针移到这个节点的右子树,然后重复第1步。
    栈思路:只要当前节点不为空,或者栈也不为空,就先一路把左节点push进栈,当某一时刻为null时,将栈顶元素出栈,并且加入结果数组,然后指针转向右子树,重复循环,继续一路把左节点push进栈

保证了左 根 右

class Solution {

public List inorderTraversal(TreeNode root) {

ArrayList res=new ArrayList<>();

ArrayDeque stack=new ArrayDeque<>();

TreeNode cur=root;

while(cur!=null||!stack.isEmpty()){

while(cur!=null){

stack.push(cur);

cur=cur.left;

}

cur=stack.pop();

res.add(cur.val);

cur=cur.right;

}

return res;

}

}

相关推荐
paopaokaka_luck2 小时前
心理咨询管理系统(WebSocket实时沟通、基于用户的协同过滤、 ECharts多维分析、 预约业务流程、预约校验与定时处理)
java·vue.js·spring boot·mysql·spring·echarts·1024程序员节
2301_32241428042 小时前
springboot农产品交易平台的设计与实现08055-计算机毕设 原创(免费领源码+部署教程+开发环境)
java·spring boot·redis·mysql·tomcat·maven·mybatis
小郎君6582 小时前
MPTT树复选框的半选状态:一棵百万节点树从递归到计数器,再到区间覆盖
算法
酌沧2 小时前
MemT让记忆成为模型内生能力
jvm·人工智能·算法
考虑考虑3 小时前
JDK26的JEP 500属性
java·后端·java ee
LMT流氓兔3 小时前
第五单元——第十二课:把脚本整理成项目
java·数据库·python·langchain·1024程序员节
yuniko-n3 小时前
【JUC】四种内存屏障示例
java·1024程序员节
Shimeri3 小时前
越卷越像:我用遗传算法做了个"内卷"实验
算法·three.js·数据可视化·遗传算法·进化算法
Evand J4 小时前
【PMSM例程1】永磁同步电机(PMSM)转速与转子电角度观测算法|基于扩展卡尔曼滤波(EKF)的传感器状态估计与误差。附下载链接
算法·电机·1024程序员节·卡尔曼滤波
guo_wen_qiang4 小时前
二叉树的层序遍历-使用双端队列Deque
算法·leetcode·1024程序员节