数据结构必学:反转链表的多种实现方法与图解
在数据结构和算法中,反转一个链表是一个有趣的问题。在本教程中,我们将讨论不同的算法来反转一个链表,并使用Java实现它们。
反转一个链表
链表是一种数据结构,它以线性的方式存储数据,尽管不以连续的方式存储。链表的每个元素包含数据部分和指向链表下一个元素的地址。链表的元素通常被称为节点。
双向链表存储两个地址,一个是前一个元素的地址,一个是后一个元素的地址。
为了在原地反转LinkedList,我们需要反转指针,使得下一个元素现在指向前一个元素。以下是输入和输出。LinkedList的头是第一个节点,没有其他元素存储了该节点的地址。LinkedList的尾是最后一个节点,这个节点中存储的下一个地址是空。我们可以通过以下方式反转LinkedList,使得头和尾也会改变:
- Iterative Approach
- Recursive Approach
使用迭代方法反转链表
要迭代地反转一个链表,我们需要存储下一个和前一个元素的引用,这样当我们交换链表中下一个元素的内存地址指针时,它们不会丢失。以下示例演示了我们通过改变引用来反转链表。
以下是反转LinkedList的步骤。创建 3 个实例:current、next、previous。循环以下步骤直到 current 不为 null。
- Save the next Node of the current element in the next pointer.
- Set the next of the current Node to the previous. This is the MVP line.
- Shift previous to current.
- Shift the current element to next.
最后,因为目前的指针已经超过了最后一个元素的位置,所以我们需要将头指针设为我们到达的最后一个元素。这个最后一个元素在之前已经被记录下来了。将头指针设置为之前的元素。因此,我们得到了新链表的头指针,即老链表的最后一个元素。
这是一个非常简单的LinkedList实现。请注意,这不是一个适用于生产环境的实现,我们将其保持简单,以便我们的重点仍然放在反转链表的算法上。
package com.Olivia.linkedlist.reverse;
public class MyLinkedList {
public Node head;
public static class Node {
Node next;
Object data;
Node(Object data) {
this.data = data;
next = null;
}
}
}
以下是一个逐个迭代地反转链表并打印其元素的Java程序:
package com.Olivia.linkedlist.reverse;
import com.Olivia.linkedlist.reverse.MyLinkedList.Node;
public class ReverseLinkedList {
public static void main(String[] args) {
MyLinkedList myLinkedList = new MyLinkedList();
myLinkedList.head = new Node(1);
myLinkedList.head.next = new Node(2);
myLinkedList.head.next.next = new Node(3);
printLinkedList(myLinkedList);
reverseLinkedList(myLinkedList);
printLinkedList(myLinkedList);
}
public static void printLinkedList(MyLinkedList linkedList) {
Node h = linkedList.head;
while (linkedList.head != null) {
System.out.print(linkedList.head.data + " ");
linkedList.head = linkedList.head.next;
}
System.out.println();
linkedList.head = h;
}
public static void reverseLinkedList(MyLinkedList linkedList) {
Node previous = null;
Node current = linkedList.head;
Node next;
while (current != null) {
next = current.next;
current.next = previous;
previous = current;
current = next;
}
linkedList.head = previous;
}
}
结果:
1 2 3
3 2 1
递归地反转一个链表
递归地反转链表时,我们需要将链表分为两部分:头部和剩余部分。头部最初指向第一个元素,剩余部分指向头部的下一个元素。
我们递归地遍历链表,直到倒数第二个元素。一旦我们到达最后一个元素,将其设置为头部。从那里开始,直到到达链表的起始点,我们执行以下操作。node.next.next = node; node.next = null; 为了不丢失原始头部的轨迹,我们将保存头部实例的副本。
以下是上述过程的示例。反转链表的Java程序是:
public static Node recursiveReverse(Node head) {
Node first;
if (head==null || head.next == null)
return head;
first = recursiveReverse(head.next);
head.next.next = head;
head.next = null;
return first;
}
为了到达链表的末尾,我们在递归调用中只传递head.next。一旦到达末尾,我们将倒数第二个元素设置为最后一个元素的下一个,并将倒数第二个元素的下一个指针设置为NULL。最后一个元素将被标记为新的头部。使用以下代码使用递归方法反转链表。
myLinkedList.head = recursiveReverse(myLinkedList.head);
输出将保持与先前的迭代方法相同。
反转链表的空间时间复杂度
时间复杂度为 O(n),空间复杂度为 O(1)。
您可以从我们的GitHub存储库中查看完整代码和更多的数据结构和算法示例。