【A】1.2 链表
和数组不同的是,链表并不需要一块连续的内存空间,它可以通过指针
将一组零散的内存块串联起来使用。链表的基本操作是最考验逻辑思维能力的,尤其需要注意:
- 浅拷贝相当于引用
- 哨兵/保护结点作为访问入口
- 边界与异常检查
刷题清单 | 备注 |
---|---|
21. 合并两个有序链表 | |
203. 移除链表元素 | |
206.反转链表 | |
707.设计链表 | |
*25.K 个一组翻转链表 | 保护结点 |
24. 两两交换链表中的节点 | |
141.环形链表 | 快慢指针|哈希表 |
*142.环形链表 II | 快慢指针|哈希表|数学 |
*19. 删除链表的倒数第 N 个结点 | 快慢指针 |
AC136.邻值查找
对序列中每一个数,求在前的数与的最小差值,以及对应下标(若最小差值对应下标不唯一,则返回最小下标)。例如,输入1, 5, 3, 4, 2
。
treemap
一个可能的想法是,对前个数维护一个有序
集合,二分查找的前驱与后继,比较差值,返回最小差值与对应下标:
1, 5
:添加5,前驱为1
,后继为\
,最小差值为4
,对应下标1
1,3,5
:添加3,前驱为1
,后继为5
,最小差值为2
,对应下标1
1,3,4,5
:添加4,前驱为3
,后继为5
,最小差值为1
,对应下标2
1,2,3,4,5
:添加2,前驱为1
,后继为3
,最小差值为1
,对应下标1
import java.io.*;
import java.util.Map;
import java.util.TreeMap;
public class Main {
public static void main(String[] args) throws Exception {
StreamTokenizer st = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
BufferedWriter br = new BufferedWriter(new OutputStreamWriter(System.out));
st.nextToken();
int n = (int) st.nval;
// 平衡树
TreeMap<Integer, Integer> map = new TreeMap<>();
for (int i = 1; i <= n; i++) {
st.nextToken();
int val = (int) st.nval;
// 添加关键字和对应的值,即数值与下标
map.put(val, i);
if (i != 1) {
// 返回大于等于 key 的最小元素
Map.Entry<Integer, Integer> up = map.ceilingEntry(val + 1);
// 返回小于等于 key 的最大元素
Map.Entry<Integer, Integer> down = map.floorEntry(val - 1);
int res = Integer.MAX_VALUE, pos = -1;
if (down != null) {
res = val - down.getKey();
pos = down.getValue();
}
if (up != null && up.getKey() - val < res) {
res = up.getKey() - val;
pos = up.getValue();
}
br.write(res + " " + pos);
br.newLine();
}
}
br.flush();
}
}
doubly-linked list
顺序添加元素可行,那倒序删除呢?依然是维护一个有序序列:
图1.2.1 样例及实现
数组
:按下标索引,记录每个下标对应的链表结点,便于快速访问双链表
:按值排序,倒序考虑每个下标,前后比较,删除相应结点
注意,虽然Java内置LinkedList,但是容器接口没有暴露具体的实现方法,也无法获取前驱后继。
所以,双链表需要自己手动实现。
import java.io.*;
import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Deque;
class Main {
static int n;
// 双链表,便于删除和查询前驱后继
static Node head;
static Node tail;
// 数组,便于根据索引快速访问结点
public static void main(String[] args) throws IOException {
StreamTokenizer st = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
st.nextToken();
n = (int) st.nval;
Node[] pile;
pile = new Node[n + 1];
// 虚拟/保护结点
head = new Node(-1, -1);
tail = new Node(-1, -1);
// 空双链表初始化
head.next = tail;
tail.prev = head;
// 按值排序
Node[] nodes = new Node[n];
for (int i = 1; i <= n; i++) {
st.nextToken();
Node node = new Node((int) st.nval, i);
nodes[i - 1] = node;
pile[i] = node;
}
Arrays.sort(nodes, (o1, o2) -> o1.val - o2.val);
for (int i = n - 1; i >= 0; i--) {
// 头插
Node node = nodes[i];
node.next = head.next;
node.prev = head;
head.next.prev = node;
head.next = node;
}
Deque<String> stack = new ArrayDeque<>();
for (int i = n; i >= 2; i--) {
Node node = pile[i];
int pos = -1;
int diff = Integer.MAX_VALUE;
// 优先考虑前驱
if (node.prev != head) {
diff = node.val - node.prev.val;
pos = node.prev.index;
}
if (node.next != tail && node.next.val - node.val < diff) {
diff = node.next.val - node.val;
pos = node.next.index;
}
stack.push(diff + " " + pos);
// 删除结点
node.prev.next = node.next;
node.next.prev = node.prev;
}
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
while (!stack.isEmpty()) {
bw.write(stack.pop());
bw.newLine();
}
bw.flush();
bw.close();
}
static class Node {
int val;
int index;
Node prev;
Node next;
public Node(int val, int index) {
this.val = val;
this.index = index;
}
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?