题目描述
给定一个双向链表,请你实现功能:删除双向链表的中间节点,并返回被删除的中间节点的值、以及删除中间节点后的双向链表。
输入描述
第一行输入一个整数 N,表示接下来会输入 N 行节点信息
之后 N 行,每行输入格式如下:
value addr preAddr nextAddr
- value 表示当前节点值,为整数
- addr 表示当前节点地址值,为正整数,不大于10000
- preAddr 表示当前节点的上一个节点地址值,为正整数,头节点的 preAddr 为 0
- nextAddr 表示当前节点的下一个节点地址值,为正整数,尾节点的 nextAddr 为 0
最后一行输入单向链表的头节点的地址值,为正整数,保证头节点地址值存在
输出描述
第一行输入被删除的中间节点的值,如:3
- 注意:若链表长度是偶数,则需要第二个中间节点
第二行输入删除中间节点后的双向链表,即以空格分隔各节点值
- 注意:若删除后链表为空,则输出 null
用例
| 输入 |
5 1 123 0 124 2 124 123 125 3 125 124 0 4 200 0 201 5 201 200 0 123 |
| 输出 |
2 1 3 |
| 说明 | 无 |
题目解析
由于链表结构是链式(内存不连续),因此无法像数组(内存连续)通过索引快速找到某个元素。
比如我们需要找到第 index 个元素
- 数组内存是连续的,因此我们只要通过:arr[0] 元素的内存地址 + (index * 元素内存大小),即可找到 arr[index] 元素的内存地址,时间复杂度 O(1)。
- 链表内存是不连续的,因此我们只能从链表头节点(通过nextAddr)跳转 index 次,才能找到链表的第 index 个节点,时间复杂度O(n)。
因此,本题即使我们构建出链表L,得出了链表长度,计算出了链表的中间节点的位置,也还是需要从头节点开始遍历。
另外,本题需要注意的是,输入的 n 不一定是链表长度,因为题目备注说:会存在部分输入结点不属于链表 L 情况
在未知链表长度的情况下,我们可以使用快慢指针,通过一轮遍历,找到链表的中间节点
快慢指针:即我们可以定义两个指针:slow、fast,来遍历链表 L。
其中 slow 指针每次走一步,fast指针每次走两步,那么当 fast 越界时,此时slow 刚好处于链表中间节点位置。
上面图示中,有一个问题,当链表是偶数时,比如链表长度为2,那么 slow 需要跳 2 步到链表中间节点,相应的,fast需要跳 4 步,但是 fast 跳到第3步时就已经越界了。
为了避免 fast 发生多次越界的情况,初始时,我们可以让 slow、fast 处于head上,那么当 fast 到达链表尾节点或尾节点后面一个越界位置时,slow 刚好处于中间节点位置。
当我们找到双向链表的中间节点地址 mid 时,同时也可以直到前一个节点的地址 pre,下一个的地址 nxt
删除中间节点,即让 pre 的下一个节点地址变为 nxt,让 nxt 的上一个节点地址变为 pre。
JS算法源码
const rl = require("readline").createInterface({ input: process.stdin });
var iter = rl[Symbol.asyncIterator]();
const readline = async () => (await iter.next()).value;
void (async function () {
const n = parseInt(await readline());
const nodes = {};
for (let i = 0; i < n; i++) {
const [value, addr, preAddr, nextAddr] = (await readline())
.split(" ")
.map(Number);
nodes[addr] = [value, preAddr, nextAddr];
}
const head = parseInt(await readline());
// 快慢指针找链表中间节点
let slow = head;
let fast = head;
while (fast != 0 && nodes[fast][2] != 0) {
slow = nodes[slow][2];
fast = nodes[nodes[fast][2]][2];
}
// 打印中间节点值
console.log(nodes[slow][0]);
// 删除中间节点
if (slow == head) {
console.log("null");
} else {
const pre = nodes[slow][1];
const nxt = nodes[slow][2];
nodes[pre][2] = nxt;
if (nxt != 0) {
nodes[nxt][1] = pre;
}
// 打印链表
const lst = [];
for (let i = head; i != 0; i = nodes[i][2]) {
lst.push(nodes[i][0]);
}
console.log(lst.join(" "));
}
})();
Java算法源码
import java.util.HashMap;
import java.util.Scanner;
import java.util.StringJoiner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
HashMap<Integer, int[]> nodes = new HashMap<>();
for (int i = 0; i < n; i++) {
int val = sc.nextInt();
int addr = sc.nextInt();
int preAddr = sc.nextInt();
int nextAddr = sc.nextInt();
nodes.put(addr, new int[]{val, preAddr, nextAddr});
}
int head = sc.nextInt();
// 快慢指针找链表中间节点
int slow = head;
int fast = head;
while (fast != 0 && nodes.get(fast)[2] != 0) {
slow = nodes.get(slow)[2];
fast = nodes.get(nodes.get(fast)[2])[2];
}
// 打印中间节点值
System.out.println(nodes.get(slow)[0]);
// 删除中间节点
if (slow == head) {
System.out.println("null");
} else {
int pre = nodes.get(slow)[1];
int nxt = nodes.get(slow)[2];
nodes.get(pre)[2] = nxt;
if (nxt != 0) {
nodes.get(nxt)[1] = pre;
}
// 打印链表
StringJoiner sj = new StringJoiner(" ");
for (int i = head; i != 0; i = nodes.get(i)[2]) {
sj.add(nodes.get(i)[0] + "");
}
System.out.println(sj);
}
}
}
Python算法源码
if __name__ == '__main__':
# 输入获取
n = int(input())
nodes = {}
for _ in range(n):
val, addr, preAddr, nextAddr = map(int, input().split())
nodes[addr] = [val, preAddr, nextAddr]
head = int(input())
# 快慢指针找链表中间节点
slow = head
fast = head
while fast != 0 and nodes[fast][2] != 0:
slow = nodes[slow][2]
fast = nodes[nodes[fast][2]][2]
# 打印中间节点值
print(nodes[slow][0])
# 删除中间节点
if slow == head:
print("null")
else:
pre = nodes[slow][1]
nxt = nodes[slow][2]
nodes[pre][2] = nxt
if nxt != 0:
nodes[nxt][1] = pre
# 打印链表
lst = []
i = head
while i != 0:
lst.append(nodes[i][0])
i = nodes[i][2]
print(" ".join(map(str, lst)))
C算法源码
#include <stdio.h>
#define MAX_LEN 20000
int main() {
int n;
scanf("%d", &n);
int nodes[MAX_LEN][3];
for (int i = 0; i < n; i++) {
int val, addr, preAddr, nextAddr;
scanf("%d %d %d %d", &val, &addr, &preAddr, &nextAddr);
nodes[addr][0] = val;
nodes[addr][1] = preAddr;
nodes[addr][2] = nextAddr;
}
int head;
scanf("%d", &head);
// 快慢指针找链表中间节点
int slow = head;
int fast = head;
while (fast != 0 && nodes[fast][2] != 0) {
slow = nodes[slow][2];
fast = nodes[nodes[fast][2]][2];
}
// 打印中间节点值
printf("%d\n", nodes[slow][0]);
// 删除中间节点
if (slow == head) {
printf("null");
} else {
int pre = nodes[slow][1];
int nxt = nodes[slow][2];
nodes[pre][2] = nxt;
if (nxt != 0) {
nodes[nxt][1] = pre;
}
// 打印链表
for (int i = head; i != 0; i = nodes[i][2]) {
printf("%d ", nodes[i][0]);
}
}
return 0;
}
C++算法源码
#include <bits/stdc++.h>
using namespace std;
int main() {
int n;
cin >> n;
map<int, vector<int>> nodes;
for (int i = 0; i < n; i++) {
int val, addr, preAddr, nextAddr;
cin >> val >> addr >> preAddr >> nextAddr;
nodes[addr] = vector<int>{val, preAddr, nextAddr};
}
int head;
cin >> head;
// 快慢指针找链表中间节点
int slow = head;
int fast = head;
while (fast != 0 && nodes[fast][2] != 0) {
slow = nodes[slow][2];
fast = nodes[nodes[fast][2]][2];
}
// 打印中间节点值
cout << nodes[slow][0] << endl;
// 删除中间节点
if (slow == head) {
cout << "null";
} else {
int pre = nodes[slow][1];
int nxt = nodes[slow][2];
nodes[pre][2] = nxt;
if (nxt != 0) {
nodes[nxt][1] = pre;
}
// 打印链表
for (int i = head; i != 0; i = nodes[i][2]) {
cout << nodes[i][0] << " ";
}
}
return 0;
}

7880





