软件制作权seo快速收录快速排名
单向链表
- 题目描述
- 步骤:
- 法1:单向链表(建议)
- 法2:双向链表
- java
- 单向链表
- 双向链表
- 普通
- 缓冲流
- 注意:
- IOException
- java.io
- BufferedWriter
- getOrDefault
- c++
B3631 单向链表
题目描述
实现一个数据结构,维护一张表(最初只有一个元素 1)。需要支持下面的操作,其中 x 和 y 都是 1 到 10 6
范围内的正整数,且保证任何时间表中所有数字均不相同,操作数量不多于 10 5:
- 1 x y :将元素 y 插入到 x 后面;
- 2 x :询问 x 后面的元素是什么。如果 x 是最后一个元素,则输出 0;
- 3 x:从表中删除元素 x 后面的那个元素,不改变其他元素的先后顺序。
输入格式
第一行一个整数 q 表示操作次数。
接下来 q 行,每行表示一次操作,操作具体见题目描述。
输出格式
对于每个操作 2,输出一个数字,用换行隔开。
输入输出样例
输入
6
1 1 99
1 99 50
1 99 75
2 99
3 75
2 1
输出
75
99
步骤:
这个数据结构需要高效地处理插入、查询和删除,尤其是操作次数可能达到1e5次,所以时间复杂度必须很低,最好是O(1)的操作。如果用普通的链表结构,比如LinkedList,插入和删除的时间复杂度是O(n),这样对于1e5次操作来说可能会超时。所以必须找一个更高效的方法。
这时候想到,可以用哈希表来模拟链表的指针。因为每个节点都是唯一的,所以可以用一个哈希表来存储每个节点的下一个节点。比如,用一个HashMap<Integer, Integer> next,其中next.get(x)返回x的下一个节点。这样,插入、查询和删除操作都可以在O(1)的时间内完成
法1:单向链表(建议)
初始化:初始时表中只有一个元素 1,其后继为 0。
插入操作(操作1):读取 x 和 y,将 y 插入到 x 之后。保存 x 的原后继节点,更新 x 的后继为 y,并将 y 的后继设为原后继节点。
查询操作(操作2):直接读取 x 的后继节点,若不存在则返回 0。
删除操作(操作3):读取 x,找到其后继节点 y 及其后继节点 z。将 x 的后继节点更新为 z,跳过 y。
这种方法通过哈希表实现了高效的节点访问和指针调整,确保每个操作的时间复杂度为 O(1),能够高效处理最多 10^5 次操作。
法2:双向链表
使用哈希表维护前驱和后继节点以实现高效操作:
- 使用两个HashMap保存前驱和后继。
- 初始化元素1的前驱和后继为0。
- 读取每个操作,并根据类型处理:
- 插入操作:更新x和y的前驱、后继,以及原x的后继的前驱。
当操作符op等于1时,表示要在元素x的后面插入元素y。代码的步骤如下:
1.读取输入的x和y。
2.获取x当前的后继节点originalNext,使用getOrDefault方法,如果x不存在于next哈希表中,默认值为0。
3.将x的后继设置为y,即next.put(x, y)。
4.将y的前驱设置为x,即prev.put(y, x)。
5.将y的后继设置为originalNext,即next.put(y, originalNext)。
6.如果originalNext不为0,说明x原来有后继节点,现在需要更新这个原后继节点的前驱为y,即prev.put(originalNext, y)。 - 查询操作:获取x的后继,输出或0。
- 删除操作:获取x的后继y,以及y的后继z,更新x的后继为z,z的前驱为x。
- 处理输入输出高效。
java
单向链表
import java.util.*;public class Main {public static void main(String[] args) {Scanner sc = new Scanner(System.in);int q = sc.nextInt();// 使用哈希表维护节点的后继关系Map<Integer, Integer> next = new HashMap<>();next.put(1, 0); // 初始只有元素1,后继为0for (int i = 0; i < q; i++) {int op = sc.nextInt();if (op == 1) {// 插入操作:将y插入到x后面int x = sc.nextInt();int y = sc.nextInt();int originalNext = next.getOrDefault(x, 0);next.put(x, y); // x的后继改为ynext.put(y, originalNext); // y的后继设为原x的后继} else if (op == 2) {// 查询操作:输出x的后继int x = sc.nextInt();System.out.println(next.getOrDefault(x, 0));} else if (op == 3) {// 删除操作:删除x的后继节点int x = sc.nextInt();int y = next.getOrDefault(x, 0);if (y != 0) {int z = next.getOrDefault(y, 0);next.put(x, z); // x的后继直接指向y的后继}}}}
}
双向链表
普通
import java.util.*;public class Main {public static void main(String[] args) {Scanner sc = new Scanner(System.in);int q = sc.nextInt();// 用两个哈希表维护链表结构Map<Integer, Integer> prev = new HashMap<>();Map<Integer, Integer> next = new HashMap<>();// 初始化元素1prev.put(1, 0);next.put(1, 0);for (int i = 0; i < q; i++) {int op = sc.nextInt();if (op == 1) {// 插入操作:x后面插入yint x = sc.nextInt();int y = sc.nextInt();int originalNext = next.getOrDefault(x, 0);next.put(x, y);prev.put(y, x);next.put(y, originalNext);if (originalNext != 0) {prev.put(originalNext, y);}} else if (op == 2) {// 查询操作:x的后继int x = sc.nextInt();int res = next.getOrDefault(x, 0);System.out.println(res == 0 ? 0 : res);} else if (op == 3) {// 删除操作:删除x的后继int x = sc.nextInt();int y = next.getOrDefault(x, 0);if (y != 0) {int z = next.getOrDefault(y, 0);next.put(x, z);if (z != 0) {prev.put(z, x);}// 清理被删除节点的关系next.remove(y);prev.remove(y);}}}}
}
缓冲流
import java.io.*;
import java.util.*;public class Main {public static void main(String[] args) throws IOException {BufferedReader br = new BufferedReader(new InputStreamReader(System.in));BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));// 使用两个哈希表维护前驱和后继关系HashMap<Integer, Integer> prev = new HashMap<>();HashMap<Integer, Integer> next = new HashMap<>();// 初始化元素1prev.put(1, 0);next.put(1, 0);int q = Integer.parseInt(br.readLine());List<String> output = new ArrayList<>();while (q-- > 0) {String[] parts = br.readLine().split(" ");int op = Integer.parseInt(parts[0]);if (op == 1) {// 插入操作:x后面插入yint x = Integer.parseInt(parts[1]);int y = Integer.parseInt(parts[2]);int originalNext = next.getOrDefault(x, 0);next.put(x, y);prev.put(y, x);next.put(y, originalNext);if (originalNext != 0) {prev.put(originalNext, y);}} else {int x = Integer.parseInt(parts[1]);if (op == 2) {// 查询操作:x的后继元素int res = next.getOrDefault(x, 0);output.add(res == 0 ? "0" : Integer.toString(res));} else if (op == 3) {// 删除操作:删除x后面的元素int y = next.getOrDefault(x, 0);if (y != 0) {int z = next.getOrDefault(y, 0);next.put(x, z);if (z != 0) {prev.put(z, x);}// 清理被删除节点的关系(可选)next.remove(y);prev.remove(y);}}}}// 输出所有结果bw.write(String.join("\n", output));bw.flush();}
}
注意:
IOException
java.io
BufferedWriter
当需要处理大量输出时(如算法题),建议使用 BufferedWriter:
// 高效输出配置
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
// 输出内容并换行
bw.write("Hello World");
bw.newLine();
// 批量刷新输出(减少 I/O 次数)
bw.flush();
getOrDefault
HashMap 的 getOrDefault 方法用于安全地获取指定键对应的值,若键不存在则返回预设的默认值。以下是分步说明和使用示例:
c++
#include<bits/stdc++.h>
using namespace std;
list<int>::iterator pos[1000005];
int main()
{list<int>l;int n;pos[1]=l.insert(l.end(),1);cin>>n;for (int i=0;i<n;++i){int a;cin>>a;if (a==1){int x,y;cin>>x>>y;list<int>::iterator temp=pos[x];pos[y]=l.insert(++temp,y);}else {int x;cin>>x;if (a==2){list<int>::iterator temp=pos[x];if (++temp!=l.end()){cout<<*temp<<endl;}else cout<<0<<endl;}if (a==3){list<int>::iterator temp=pos[x];if (++temp!=l.end())l.erase(temp);}} }return 0;
}