49 # 用递归和非递归两种方式实现链表反转

简介: 49 # 用递归和非递归两种方式实现链表反转

递归方式反转

思路就是从后面两两开始反转,比如下面的就是先 B、C 反转再 A、B 反转

比如我们要实现链表里 A、B 的反转,可以用代码表示为:

head.next.next = head;
head.next = null;

递归方式反转 reverseLinkedList 整体代码实现如下

// 节点类
class Node {
    constructor(element, next) {
        this.element = element; // 存放的数据
        this.next = next; // next 指针表示下一个是谁
    }
}
class LinkedList {
    constructor() {
        this.head = null; // 链表的头
        this.size = 0; // 链表长度
    }
    // 可以直接在尾部添加内容,或者根据索引添加
    add(index, element) {
        // 传入一个参数是需要设置一下 index, element
        if (arguments.length === 1) {
            // 在尾部添加,传入的 index 就当做是 element
            element = index;
            // 然后把 this.size 当做索引
            index = this.size;
        }
        // 处理越界可能
        if (index < 0 || index > this.size) throw new Error("越界");
        // 在索引为 0 的位置上插入元素
        if (index === 0) {
            // 老的头
            let head = this.head;
            // 设置新头,将老的头变为当前节点的下一个
            this.head = new Node(element, head);
        } else {
            // 先找到当前索引的上一个
            let prevNode = this.getNode(index - 1);
            // 将当前上一个节点的 next 指向新的节点,新的节点的下一个指向上一个节点的 next
            prevNode.next = new Node(element, prevNode.next);
        }
        // 累加 size
        this.size++;
    }
    getNode(index) {
        // 从头开始找
        let current = this.head;
        // 不能向后找,找到索引的位置
        for (let i = 0; i < index; i++) {
            current = current.next;
        }
        return current;
    }
    // 递归方式反转
    reverseLinkedList() {
        const reverse = (head) => {
            // 停止条件
            if (head === null || head.next === null) return head;
            let newHead = reverse(head.next);
            head.next.next = head;
            head.next = null;
            return newHead;
        };
        this.head = reverse(this.head);
        return this.head;
    }
}
let ll = new LinkedList();
ll.add(0, 1);
ll.add(0, 2);
ll.add(3);
ll.add(1, 4);
// 不反转的:2 -> 4 -> 1 -> 3
console.log("不反转的---->");
console.dir(ll, { depth: 100 });
// 递归方式反转:3 -> 1 -> 4 -> 2
console.log("递归方式反转---->");
console.dir(ll.reverseLinkedList(), { depth: 100 });

非递归方式反转

思路就是利用一个循环从链表中一个一个取出来放到新的链表中。

用代码表示执行一个元素的效果

let head = this.head; // 获取原来的第一个元素
let newHead = null;
let temp = head.next; // 先保留 B
head.next = newHead; // A -> null
newHead = head; // newHead -> A
head = temp; // head -> B

非递归方式反转 reverseLinkedList2 整体代码实现如下

// 节点类
class Node {
    constructor(element, next) {
        this.element = element; // 存放的数据
        this.next = next; // next 指针表示下一个是谁
    }
}
class LinkedList {
    constructor() {
        this.head = null; // 链表的头
        this.size = 0; // 链表长度
    }
    // 可以直接在尾部添加内容,或者根据索引添加
    add(index, element) {
        // 传入一个参数是需要设置一下 index, element
        if (arguments.length === 1) {
            // 在尾部添加,传入的 index 就当做是 element
            element = index;
            // 然后把 this.size 当做索引
            index = this.size;
        }
        // 处理越界可能
        if (index < 0 || index > this.size) throw new Error("越界");
        // 在索引为 0 的位置上插入元素
        if (index === 0) {
            // 老的头
            let head = this.head;
            // 设置新头,将老的头变为当前节点的下一个
            this.head = new Node(element, head);
        } else {
            // 先找到当前索引的上一个
            let prevNode = this.getNode(index - 1);
            // 将当前上一个节点的 next 指向新的节点,新的节点的下一个指向上一个节点的 next
            prevNode.next = new Node(element, prevNode.next);
        }
        // 累加 size
        this.size++;
    }
    getNode(index) {
        // 从头开始找
        let current = this.head;
        // 不能向后找,找到索引的位置
        for (let i = 0; i < index; i++) {
            current = current.next;
        }
        return current;
    }
    // 非递归方式反转
    reverseLinkedList2() {
        // 获取原来的第一个元素
        let head = this.head;
        // 处理临界值:如果没有或者只有一个
        if (head === null || head.next === null) return head;
        let newHead = null;
        while (head !== null) {
            let temp = head.next; // 先保留 B
            head.next = newHead;
            newHead = head;
            head = temp;
        }
        this.head = newHead;
        return this.head;
    }
}
let ll = new LinkedList();
ll.add(0, 1);
ll.add(0, 2);
ll.add(3);
ll.add(1, 4);
// 不反转的:2 -> 4 -> 1 -> 3
console.log("不反转的---->");
console.dir(ll, { depth: 100 });
// 非递归方式反转:3 -> 1 -> 4 -> 2
console.log("非递归方式反转---->");
console.dir(ll.reverseLinkedList2(), { depth: 100 });

目录
相关文章
|
6月前
【每日一题Day287】LC24 两两交换链表中的节点 | 模拟 递归
【每日一题Day287】LC24 两两交换链表中的节点 | 模拟 递归
44 0
|
6月前
|
算法
算法系列--递归(一)--与链表有关(上)
算法系列--递归(一)--与链表有关
67 0
|
5月前
|
SQL 算法 数据可视化
LeetCode题目92:反转链表ll 【python 递归与迭代方法全解析】
LeetCode题目92:反转链表ll 【python 递归与迭代方法全解析】
|
6月前
|
算法
合并有序链&&反转链表(递归版)
合并有序链&&反转链表(递归版)
|
6月前
|
算法
算法系列--递归(一)--与链表有关(下)
算法系列--递归(一)--与链表有关(下)
35 0
|
6月前
|
存储 算法 C语言
【数据结构与算法】【约瑟夫问题】还在用递归?教你用链表秒杀约瑟夫
【数据结构与算法】【约瑟夫问题】还在用递归?教你用链表秒杀约瑟夫
|
6月前
|
算法
每日一题——排序链表(递归 + 迭代)
每日一题——排序链表(递归 + 迭代)
|
6月前
【每日一题Day286】LC21合并两个有序链表 | 链表模拟 递归
【每日一题Day286】LC21合并两个有序链表 | 链表模拟 递归
32 0
|
11月前
|
算法
数据结构单链表之查找链表的长度(迭代和递归) | 第七套
数据结构单链表之查找链表的长度(迭代和递归) | 第七套
88 0
C++递归解决两两交换链表中节点
C++递归解决两两交换链表中节点