【LeetCode 随笔】面试经典 150 题【中等+困难】持续更新中。。。

在这里插入图片描述

🌈你好呀!我是 山顶风景独好
💕欢迎来到我的博客,很高兴能够在这里和您见面!
💕希望您在这里可以感受到一份轻松愉快的氛围!
💕这里不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。
🚀 欢迎一起踏上探险之旅,挖掘无限可能,共同成长!

61.【中等】旋转链表

题目描述
给你一个链表的头节点 head ,旋转链表,将链表每个节点向右移动 k 个位置。

示例 1:
在这里插入图片描述
输入:head = [1,2,3,4,5], k = 2
输出:[4,5,1,2,3]

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {  
    // 定义一个方法,用于将链表的尾部移动到头部,实现链表的右旋  
    public ListNode rotateRight(ListNode head, int k) {  
        // 如果k为0,或者链表为空,或者链表只有一个节点,则无需旋转,直接返回原链表  
        if (k == 0 || head == null || head.next == null) {  
            return head;  
        }  
  
        // 初始化一个计数器n,用于记录链表的长度  
        int n = 1;  
        // 定义一个迭代器iter,初始化为链表的头节点  
        ListNode iter = head;  
  
        // 使用iter遍历链表,直到链表尾部,同时计算链表长度n  
        while (iter.next != null) {  
            iter = iter.next;  
            n++;  
        }  
  
        // 计算实际需要旋转的次数,因为k可能大于链表长度n,所以需要取余  
        int add = n - k % n;  
  
        // 如果计算后的add等于n,说明k是n的倍数,旋转后链表不变,直接返回原链表  
        if (add == n) {  
            return head;  
        }  
  
        // 将链表的尾部和头部连接起来,形成环  
        iter.next = head;  
  
        // 移动iter到新的尾部位置,即原链表的倒数第k个节点  
        while (add-- > 0) {  
            iter = iter.next;  
        }  
  
        // iter的下一个节点即为新的头节点  
        ListNode ret = iter.next;  
  
        // 断开链表环,形成新的链表  
        iter.next = null;  
  
        // 返回新的头节点  
        return ret;  
    }  
}

86.【中等】分隔链表

题目描述

  • 给你一个链表的头节点 head 和一个特定值 x ,请你对链表进行分隔,使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前。
  • 你应当 保留 两个分区中每个节点的初始相对位置。

示例 1:
在这里插入图片描述
输入:head = [1,4,3,2,5,2], x = 3
输出:[1,2,2,4,3,5]


示例 2:
输入:head = [2,1], x = 2
输出:[1,2]

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {  
    // 将链表按照值是否小于x划分为两部分,小于x的节点在前,大于等于x的节点在后  
    public ListNode partition(ListNode head, int x) {  
        // 创建两个哑节点(dummy nodes),分别作为小于x的链表和大于等于x的链表的头部  
        ListNode smlDummy = new ListNode(0), bigDummy = new ListNode(0);  
        // sml和big分别作为两个链表的尾节点指针,初始时都指向各自的哑节点  
        ListNode sml = smlDummy, big = bigDummy;  
  
        // 遍历原链表  
        while (head != null) {  
            // 如果当前节点的值小于x  
            if (head.val < x) {  
                // 将当前节点接到小于x的链表的尾部  
                sml.next = head;  
                // 将小于x的链表的尾节点指针后移  
                sml = sml.next;  
            } else {  
                // 如果当前节点的值大于等于x  
                // 将当前节点接到大于等于x的链表的尾部  
                big.next = head;  
                // 将大于等于x的链表的尾节点指针后移  
                big = big.next;  
            }  
            // 继续遍历原链表  
            head = head.next;  
        }  
  
        // 将小于x的链表的尾部与大于等于x的链表的头部连接起来  
        // 此时bigDummy.next指向大于等于x的链表的第一个节点  
        sml.next = bigDummy.next;  
  
        // 将大于等于x的链表的尾部设置为null,断开与哑节点的连接  
        big.next = null;  
  
        // 返回小于x的链表的头部,即整个重新排序后的链表的头部  
        return smlDummy.next;  
    }  
}

146.【中等】LRU 缓存

题目描述
请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。
实现 LRUCache 类:

  • LRUCache(int capacity) 以 正整数 作为容量 capacity 初始化 LRU 缓存
  • int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 。
  • void put(int key, int value) 如果关键字 key 已经存在,则变更其数据值 value ;如果不存在,则向缓存中插入该组 key-value 。如果插入操作导致关键字数量超过 capacity ,则应该 逐出 最久未使用的关键字。

函数 get 和 put 必须以 O(1) 的平均时间复杂度运行。

示例:

  • 输入
    [“LRUCache”, “put”, “put”, “get”, “put”, “get”, “put”, “get”, “get”, “get”]
    [[2], [1, 1], [2, 2], [1], [3, 3], [2], [4, 4], [1], [3], [4]]
  • 输出
    [null, null, null, 1, null, -1, null, -1, 3, 4]
  • 解释
    LRUCache lRUCache = new LRUCache(2);
    lRUCache.put(1, 1); // 缓存是 {1=1}
    lRUCache.put(2, 2); // 缓存是 {1=1, 2=2}
    lRUCache.get(1); // 返回 1
    lRUCache.put(3, 3); // 该操作会使得关键字 2 作废,缓存是 {1=1, 3=3}
    lRUCache.get(2); // 返回 -1 (未找到)
    lRUCache.put(4, 4); // 该操作会使得关键字 1 作废,缓存是 {4=4, 3=3}
    lRUCache.get(1); // 返回 -1 (未找到)
    lRUCache.get(3); // 返回 3
    lRUCache.get(4); // 返回 4
// LRU Cache 类定义
public class LRUCache {
    // 定义双向链表节点类,用于构建双向链表
    class DLinkedNode {
        int key; // 节点存储的键
        int value; // 节点存储的值
        DLinkedNode prev; // 指向前一个节点的指针
        DLinkedNode next; // 指向后一个节点的指针
        public DLinkedNode() {} // 默认构造函数
        public DLinkedNode(int _key, int _value) {key = _key; value = _value;} // 带参数的构造函数
    }

    // 使用HashMap作为缓存,键为int类型,值为DLinkedNode类型
    private Map<Integer, DLinkedNode> cache = new HashMap<Integer, DLinkedNode>();
    private int size; // 当前缓存中元素的数量
    private int capacity; // 缓存的最大容量
    private DLinkedNode head, tail; // 双向链表的头节点和尾节点

    // LRUCache构造函数,初始化缓存容量
    public LRUCache(int capacity) {
        this.size = 0;
        this.capacity = capacity;
        // 初始化双向链表的伪头部和尾部节点
        head = new DLinkedNode();
        tail = new DLinkedNode();
        head.next = tail;
        tail.prev = head;
    }

    // 获取键为key的值,如果不存在则返回-1
    public int get(int key) {
        DLinkedNode node = cache.get(key); // 通过哈希表查找节点
        if (node == null) {
            return -1;
        }
        // 如果找到了节点,将其移到链表头部
        moveToHead(node);
        return node.value;
    }

    // 插入或更新键值对
    public void put(int key, int value) {
        DLinkedNode node = cache.get(key); // 查找是否存在该节点
        if (node == null) {
            // 如果不存在,创建新节点
            DLinkedNode newNode = new DLinkedNode(key, value);
            // 将新节点加入哈希表
            cache.put(key, newNode);
            // 将新节点添加到链表头部
            addToHead(newNode);
            ++size;
            // 如果超过容量,删除尾部节点
            if (size > capacity) {
                DLinkedNode tailNode = removeTail();
                // 并从哈希表中移除
                cache.remove(tailNode.key);
                --size;
            }
        } else {
            // 如果存在,则更新节点的值并移到头部
            node.value = value;
            moveToHead(node);
        }
    }

    // 将节点添加到链表头部
    private void addToHead(DLinkedNode node) {
        node.prev = head;
        node.next = head.next;
        head.next.prev = node;
        head.next = node;
    }

    // 从链表中移除节点
    private void removeNode(DLinkedNode node) {
        node.prev.next = node.next;
        node.next.prev = node.prev;
    }

    // 将节点移动到链表头部
    private void moveToHead(DLinkedNode node) {
        removeNode(node);
        addToHead(node);
    }

    // 删除并返回链表尾部节点
    private DLinkedNode removeTail() {
        DLinkedNode res = tail.prev;
        removeNode(res);
        return res;
    }
}

105.【中等】从前序与中序遍历序列构造二叉树

题目描述
给定两个整数数组 preorder 和 inorder ,其中 preorder 是二叉树的先序遍历, inorder 是同一棵树的中序遍历,请构造二叉树并返回其根节点。

示例 1:
在这里插入图片描述

  • 输入: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]
  • 输出: [3,9,20,null,null,15,7]

示例 2:

  • 输入: preorder = [-1], inorder = [-1]
  • 输出: [-1]
class Solution {  
    // 根据先序遍历和中序遍历结果构建二叉树  
    public TreeNode buildTree(int[] preorder, int[] inorder) {  
        // 如果先序遍历数组为空,则返回空树  
        if (preorder == null || preorder.length == 0) {  
            return null;  
        }  
        // 先序遍历数组的第一个元素是根节点的值,创建根节点  
        TreeNode root = new TreeNode(preorder[0]);  
        // 使用栈来辅助构建二叉树,首先将根节点压入栈中  
        Deque<TreeNode> stack = new LinkedList<TreeNode>();  
        stack.push(root);  
        // 初始化中序遍历数组的索引  
        int inorderIndex = 0;  
  
        // 从先序遍历数组的第二个元素开始遍历(因为第一个元素已经用于创建根节点)  
        for (int i = 1; i < preorder.length; i++) {  
            // 获取当前先序遍历的值  
            int preorderVal = preorder[i];  
            // 获取栈顶元素,即当前遍历节点的父节点  
            TreeNode node = stack.peek();  
  
            // 如果栈顶元素的值不等于当前中序遍历的值  
            // 说明当前先序遍历的值应该作为栈顶元素的左子节点  
            if (node.val != inorder[inorderIndex]) {  
                // 创建左子节点  
                node.left = new TreeNode(preorderVal);  
                // 将左子节点压入栈中  
                stack.push(node.left);  
            // 如果栈顶元素的值等于当前中序遍历的值  
            // 说明已经遍历到当前节点的右子节点或者更右边的节点  
            // 需要先弹出栈中所有已经处理过的节点,直到找到当前节点的父节点  
            } else {  
                while (!stack.isEmpty() && stack.peek().val == inorder[inorderIndex]) {  
                    // 弹出栈顶元素  
                    node = stack.pop();  
                    // 更新中序遍历的索引  
                    inorderIndex++;  
                }  
                // 创建右子节点  
                node.right = new TreeNode(preorderVal);  
                // 将右子节点压入栈中  
                stack.push(node.right);  
            }  
        }  
        // 返回构建好的二叉树的根节点  
        return root;  
    }  
}

106.【中等】从中序与后序遍历序列构造二叉树

题目描述
给定两个整数数组 inorder 和 postorder ,其中 inorder 是二叉树的中序遍历, postorder 是同一棵树的后序遍历,请你构造并返回这颗 二叉树 。

示例 :
在这里插入图片描述

  • 输入:inorder = [9,3,15,20,7], postorder = [9,15,7,20,3]
  • 输出:[3,9,20,null,null,15,7]
class Solution {  
    // 创建一个HashMap来存储中序遍历数组中每个节点值对应的索引  
    HashMap<Integer,Integer> inorderArrayMap = new HashMap<>();  
    // 用来存储后序遍历数组的引用,方便在递归中直接使用  
    int[] post;  
  
    // 主方法,用于构建二叉树  
    public TreeNode buildTree(int[] inorder, int[] postorder) {  
        // 遍历中序遍历数组,将每个节点值及其索引存储在HashMap中  
        for(int i = 0;i < inorder.length; i++) {  
            inorderArrayMap.put(inorder[i], i); //将节点值及索引全部记录在哈希表中  
        }  
  
        // 将后序遍历数组赋值给类的成员变量post 
        post = postorder;  
        // 调用递归方法,从整个中序和后序遍历数组的范围开始构建二叉树  
        TreeNode root = buildTree(0, inorder.length - 1, 0, post.length - 1);  
        return root;  
    }  
  
    // 递归方法,根据给定的中序和后序遍历的范围来构建二叉树的子树  
    public TreeNode buildTree(int inorderStart, int inorderEnd, int postorderStart, int postorderEnd) {  
        // 如果中序遍历的范围或后序遍历的范围无效,返回null  
        if(inorderEnd < inorderStart || postorderEnd < postorderStart) return null;  
  
        // 根据后序遍历的最后一个元素确定当前子树的根节点  
        int root = post[postorderEnd]; //根据后序遍历结果,取得根节点  
        // 从HashMap中获取根节点在中序遍历数组中的索引  
        int rootIndexInInorderArray = inorderArrayMap.get(root); //获取对应的索引  
  
        // 创建当前根节点  
        TreeNode node = new TreeNode(root); //创建该节点  
  
        // 递归构建左子树,注意更新中序和后序遍历的范围  
        node.left = buildTree(inorderStart, rootIndexInInorderArray - 1, postorderStart, postorderStart + rootIndexInInorderArray - inorderStart - 1);  
  
        // 递归构建右子树,同样注意更新中序和后序遍历的范围  
        node.right = buildTree(rootIndexInInorderArray + 1, inorderEnd, postorderStart + rootIndexInInorderArray - inorderStart, postorderEnd - 1);  
  
        // 返回当前构建的节点(即子树的根节点)  
        return node; //注意!返回的是新建的node!  
    }  
}

✨ 这就是今天要分享给大家的全部内容了,我们下期再见!😊

🏠 我在CSDN等你哦!我的主页😍

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

山顶风景独好

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

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

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

打赏作者

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

抵扣说明:

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

余额充值