删除双向链表的中间节点

567 篇文章

已下架不支持订阅

473 篇文章

已下架不支持订阅

141 篇文章

已下架不支持订阅

题目描述

给定一个双向链表,请你实现功能:删除双向链表的中间节点,并返回被删除的中间节点的值、以及删除中间节点后的双向链表。

输入描述

第一行输入一个整数 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;
}

已下架不支持订阅

评论 22
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

程序员阿甘

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

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

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

打赏作者

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

抵扣说明:

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

余额充值