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