📜  C ++程序以块方式旋转链表(1)

📅  最后修改于: 2023-12-03 15:13:45.779000             🧑  作者: Mango

C++程序以块方式旋转链表

在这篇文章中,我将向您展示如何在 C++ 中旋转链表。旋转链表意味着将链表的所有节点向右移动k个位置,其中k是非负数。例如,如果您有链表1 -> 2 -> 3 -> 4 -> 5和k = 2,则应将其旋转为4 -> 5 -> 1 -> 2 -> 3。

我们将按照以下步骤实现:

  1. 找到要移动的节点的位置。
  2. 将链表拆分为两个部分。
  3. 将第二部分放在第一部分的前面。
  4. 返回新链表的头。

下面是源代码及其解释。

1. 找到要移动的节点的位置。

我们将从找到旋转位置的节点开始。如果链表的长度为l,则旋转k次相当于旋转(l-k)%l次。例如,如果链表的长度为5(上面的例子),并且我们要将它旋转2个位置,则它将旋转三次。这个方法可以为我们提供线性时间复杂度。

ListNode* findEnd(ListNode* head) {
    while (head && head->next) {
        head = head->next;
    }
    return head;
}

ListNode* rotateRight(ListNode* head, int k) {
    if (!head || !head->next || k == 0) return head;
    int length = 1;
    ListNode* endNode = findEnd(head);
    ListNode* newHead;
    while (k > 0 && length < INT_MAX) {
        newHead = head->next;
        head->next = nullptr;
        endNode->next = head;
        endNode = head;
        head = newHead;
        k--;
        length++;
        if (!newHead) {
            // 如果我们到达了链表的末尾,从开头再次开始
            head = newHead = head->next;
        }
    }
    return head;
}

以上代码中,findEnd函数返回链表的最后一个节点。rotateRight函数接受链表头和要旋转的位置k作为输入,并返回新链表的头。在这里,我们使用while循环将链表旋转k次。

2. 将链表拆分为两个部分。

为了确定链表的“前半部分”和“后半部分”,我们需要找到前半部分的最后一个节点。这个节点标志着我们分隔链表的地方。接下来,我们连接新的链表头到前半部分的最后一个节点,并将后半部分的最后一个节点的下一个指向旧的链表头。

ListNode* rotateRight(ListNode* head, int k) {
    if (!head || !head->next || k == 0) return head;
    int length = 1;
    ListNode* endNode = findEnd(head);
    k = k % length;
    ListNode* newHead;
    while (k > 0 && length < INT_MAX) {
        newHead = head->next;
        head->next = nullptr;
        endNode->next = head;
        endNode = head;
        head = newHead;
        k--;
        length++;
        if (!newHead) {
            // 如果我们到达了链表的末尾,从开头再次开始
            head = newHead = head->next;
            k = k % length;
        }
    }
    ListNode* newEnd = head;
    while (newEnd->next) {
        newEnd = newEnd->next;
        length++;
    }
    length = length - k;
    ListNode* oldEnd = findEnd(head);
    oldEnd->next = head;
    for (int i = 0; i < length; i++) {
        oldEnd = oldEnd->next;
    }
    ListNode* newHead = oldEnd->next;
    oldEnd->next = nullptr;
    return newHead;
}

在这个版本的rotateRight中,我们添加了一个新的变量来跟踪链表的长度。我们通过使用findEnd函数来“找出”链表的末尾。另外,我们还检查了链表的长度是否大于k,并根据需要更新k。在循环之后,我们重新计算链表的长度,并找到前半部分的最后一个节点。然后,我们将新的链表头连接到最后一个节点,并断开旧的链表头与它的连接。返回新的链表头即可。

3. 将第二部分放在第一部分的前面。

最后,我们需要将链表的后半部分连接到前半部分的最后一个节点处。为此,我们需要先找到旧的链表的末尾,然后将它的下一个节点连接到新链表的头部。

4. 返回新链表的头

最终,在将新链表连接到旧链表的末尾后,我们将返回新链表的头节点即可。

完整代码
ListNode* findEnd(ListNode* head) {
    while (head && head->next) {
        head = head->next;
    }
    return head;
}

ListNode* rotateRight(ListNode* head, int k) {
    if (!head || !head->next || k == 0) return head;
    int length = 1;
    ListNode* endNode = findEnd(head);
    k = k % length;
    ListNode* newHead;
    while (k > 0 && length < INT_MAX) {
        newHead = head->next;
        head->next = nullptr;
        endNode->next = head;
        endNode = head;
        head = newHead;
        k--;
        length++;
        if (!newHead) {
            // 如果我们到达了链表的末尾,从开头再次开始
            head = newHead = head->next;
            k = k % length;
        }
    }
    ListNode* newEnd = head;
    while (newEnd->next) {
        newEnd = newEnd->next;
        length++;
    }
    length = length - k;
    ListNode* oldEnd = findEnd(head);
    oldEnd->next = head;
    for (int i = 0; i < length; i++) {
        oldEnd = oldEnd->next;
    }
    ListNode* newHead = oldEnd->next;
    oldEnd->next = nullptr;
    return newHead;
}

这是我们的完整程序。这种方法的时间复杂度是O(n),因为我们只遍历链表一次。它不需要使用额外的空间,也就是说,它的空间复杂度为O(1)。

感谢您阅读本文,我希望您在将来的代码中能够使用这个技巧!