【路飞】链表反转

120 阅读2分钟

1、基础链表反转,给你单链表的头节点head ,请你反转链表,并返回反转后的链表,示例如下:

image.png

很容易想到的是遍历整个链表,将当前节点的next指针指向上一个节点,因为第一个节点没有上一个节点,故而第一个节点的next指向为null,具体代码如下

var reverseList = function (head) {
    let node = null;
    while (head) {
        const next = head.next;
        head.next = node;
        node = head;
        head = next;
    }
    return node;
};

2、在此基础上,若只想反转链表中的部分连续节点,如从2-4反转,继而引出链表反转升级版,给你单链表的头指针 head 和两个整数 left 和 right ,其中 left <= right 。请你反转从位置 left 到位置 right 的链表节点,返回 反转后的链表,示例如下:

image.png

此处我们需要将反转子链表的首尾节点定位到,将其传入到类似1中的反转函数中,但是由于需要将反转后的链表重新接回到原链表,因此我们需要对1中函数进行改造,改造如下:

var reverseList = function (head, end) {
    if (!head || !end) {
        return head;
    }
    let node = null;
    const newStart = head;
    while (head) {
        const next = head.next;
        head.next = node;
        node = head;
        head = next;
    }
    return [end, newStart];
};

当然,我们也可以不对1中函数进行改造,直接遍历起返回链表然后接回到原链表中,但是在改造后明显时间复杂度上会减少一次对子链表的遍历,解法更优。接下来所需要做的事便是取出原链表中子链表的首尾节点及其连接节点,可通过一次遍历几个节点取出,具体代码如下:

var reverseBetween = function (head, left, right) {
    if (left === right) {
        return head;
    }
    let count = 1;
    let ret = new ListNode();
    ret.next = head;
    let start;
    let end;
    let pre = ret,
        next;
    while (head) {
        if (count === left) {
            start = head;
        }
        if (count < left) {
            pre = pre.next;
        }
        if (count === right) {
            end = head;
            next = head.next;
            head.next = null;
        }
        head = head.next;
        count++;
    }
    const [newEnd, newStart] = reverseList(start, end);
    pre.next = newEnd;
    newStart.next = next;
    return ret.next;
};

以上还有部分变量可以优化,但是整体的时间复杂度上已经优化为O(n * (right - left)),可以继续优化为O(right * (right - left))

3、难度继续升级,在2基础上继续添加参数,将一个链表分成K个一组,满K个节点进行反转一次,最后输出链表,示例如下:

image.png

image.png

那么根据2很容易得出,我们只需对输入链表先进行节点数量统计,再按K个一组分成Math.floor(n / K)组进行遍历反转并重新接回原链表即可得出答案,那么过程很明显,即时对2的一个扩展。代码如下:

function reverseKGroup(head, k) {
    const hair = new ListNode(0);
    hair.next = head;
    let pre = hair;
    let cur = head;
    let n = 0;
    let count;
    while (cur) {
        cur = cur.next;
        n++;
    }
    count = Math.floor(n / k);
    while (count) {
        let tail = pre;
        for (let i = 0; i < k; i++) {
            tail = tail.next;
        }
        const next = tail.next;
        [head, tail] = reverseList(head, tail);
        if (count === Math.floor(n / k)) {
            hair.next = tail;
        }
        pre.next = head;
        tail.next = next;
        pre = tail;
        head = tail.next;
        count--;
    }
    return hair.next;
}

function reverseList(head, tail) {
    let prev = tail.next;
    let cur = head;
    while (prev !== tail) {
        const next = cur.next;
        cur.next = prev;
        prev = cur;
        cur = next;
    }
    return [tail, head];
}