区间反转链表的实现与解析 - 详细步骤与代码示例

2025/3/15
本文详细介绍了如何实现区间反转链表,包括问题描述、解决思路、代码实现及其解释。通过使用虚拟节点和双指针技巧,可以高效地反转链表中的指定区间。
一个程序员正在电脑前编写代码,屏幕上显示着链表反转的示意图。

区间反转链表是一个常见的算法问题,通常用于面试和实际开发中。给定一个单链表和两个整数 leftright,要求反转从位置 leftright 的链表节点。以下是实现这一功能的详细步骤和代码示例。

问题描述

给定一个单链表和两个整数 leftright,反转从位置 leftright 的链表节点。要求只进行一次遍历。

示例

输入: 1 -> 2 -> 3 -> 4 -> 5, left = 2, right = 4
输出: 1 -> 4 -> 3 -> 2 -> 5

解决思路

  1. 定位反转区间的前一个节点:首先找到 left 位置的前一个节点 prev,以便在反转后能够正确连接。
  2. 反转区间内的节点:从 leftright 的节点进行反转。
  3. 重新连接链表:将反转后的区间重新连接到原链表中。

代码实现

以下是使用 JavaScript 实现的代码:

function reverseBetween(head, left, right) {
    if (!head || left === right) return head;

    // 创建一个虚拟节点,简化边界条件处理
    const dummy = new ListNode(0);
    dummy.next = head;
    let prev = dummy;

    // 移动到 left 的前一个节点
    for (let i = 0; i < left - 1; i++) {
        prev = prev.next;
    }

    // 开始反转
    let start = prev.next; // 反转区间的第一个节点
    let then = start.next; // 将要移动的节点

    for (let i = 0; i < right - left; i++) {
        start.next = then.next;
        then.next = prev.next;
        prev.next = then;
        then = start.next;
    }

    return dummy.next;
}

// 链表节点定义
class ListNode {
    constructor(val, next = null) {
        this.val = val;
        this.next = next;
    }
}

// 示例用法
const head = new ListNode(1);
head.next = new ListNode(2);
head.next.next = new ListNode(3);
head.next.next.next = new ListNode(4);
head.next.next.next.next = new ListNode(5);

const result = reverseBetween(head, 2, 4);

// 打印结果
let current = result;
while (current) {
    console.log(current.val);
    current = current.next;
}

代码解释

  1. 虚拟节点:创建一个虚拟节点 dummy,并将其 next 指向链表的头节点。这样可以简化边界条件的处理,特别是当 left 为 1 时。
  2. 定位 prev:通过循环将 prev 移动到 left 位置的前一个节点。
  3. 反转区间:使用 startthen 两个指针来反转区间内的节点。start 始终指向反转区间的第一个节点,then 指向 start 的下一个节点。通过交换 thenprev.next 的位置,逐步将 then 移动到 prev.next 的位置,实现反转。
  4. 返回结果:最终返回 dummy.next,即反转后的链表头节点。

复杂度分析

  • 时间复杂度:O(n),其中 n 是链表的长度。我们只需要遍历链表一次。
  • 空间复杂度:O(1),只使用了常数个额外的指针。

总结

区间反转链表是一个经典的链表操作问题,通过使用虚拟节点和双指针技巧,可以高效地解决这个问题。理解并掌握这种技巧对于处理链表相关的问题非常有帮助。

标签:算法
上次更新:

相关文章

TypeScript 映射类型常见问题与解决方案 | 提升代码维护性

本文探讨了在使用 TypeScript 时,映射类型的不当使用可能导致的问题,如代码难以维护、类型推断不准确或性能问题,并提供了相应的解决方案和最佳实践。

·编程语言

TypeScript 交叉类型与联合类型:区别与最佳实践

本文详细解释了 TypeScript 中交叉类型(Intersection Types)和联合类型(Union Types)的区别,提供了使用场景、类型守卫、避免过度使用交叉类型的建议,以及如何通过工具辅助解决混淆问题。

·编程语言

TypeScript 类继承中的常见类型问题及解决方案 | TypeScript 开发指南

本文详细探讨了在 TypeScript 中使用类继承时可能遇到的常见类型问题,包括类型兼容性、构造函数、方法重写、访问修饰符、泛型类继承、抽象类以及类型断言等问题,并提供了相应的解决方案和代码示例。

·编程语言

TypeScript 函数重载:常见问题与解决方案

本文探讨了 TypeScript 中函数重载的常见问题,包括签名与实际实现不匹配、重载签名过多、与泛型结合时的类型推断问题等,并提供了相应的解决方案。

·编程语言

TypeScript 类型与泛型约束冲突的解决方法 | 技术指南

本文详细介绍了在 TypeScript 中解决类型与泛型约束冲突的多种方法,包括明确泛型参数的类型约束、使用类型断言、条件类型、默认类型参数等,帮助开发者有效处理类型推断问题。

·编程语言

TypeScript 类型工具函数常见错误及解决方法 | 详细指南

本文详细介绍了在使用 TypeScript 类型工具函数时可能遇到的常见错误,包括类型推断错误、类型工具函数未定义、参数错误、返回类型错误等,并提供了相应的解决方法。

·编程语言

TypeScript 类型与运行时值不匹配的解决策略与最佳实践

本文详细介绍了在 TypeScript 开发中解决类型与运行时值不匹配问题的多种策略和最佳实践,包括类型断言、类型保护、类型推断、运行时类型检查、使用 `unknown` 类型、第三方库、避免 `any` 类型、`as const` 常量断言、`never` 类型处理以及 `readonly` 和 `ReadonlyArray` 的使用。

·编程语言

TypeScript 枚举类型常见问题及解决方案 | 最佳实践指南

本文详细介绍了在使用 TypeScript 枚举类型时可能遇到的常见问题,如枚举值类型不一致、重复、未定义等,并提供了相应的解决方案和最佳实践,帮助开发者编写更健壮和可维护的代码。

·编程语言

TypeScript 类型扩展与合并技巧 - 实用指南

本文详细介绍了在 TypeScript 中处理类型扩展与合并的多种方法,包括使用 `interface`、`type`、`extends`、`Partial`、`Pick`、`Omit`、`Record`、映射类型、条件类型、实用类型和 `namespace`。这些技巧有助于更好地管理和扩展复杂的类型系统。

·编程语言

TypeScript 类型断言的滥用问题及解决策略 | 提高代码类型安全性和可维护性

本文探讨了 TypeScript 中类型断言的滥用问题,并提供了多种解决策略,包括优先使用类型推断、类型保护、类型声明、避免使用 `any` 类型、使用 `unknown` 类型、重构代码、使用工具进行代码审查以及团队培训和代码审查。

·编程语言