链表的节点可以定义为:
class Node<T> {
T data;
Node next;
}
测试的输入数据,每次使用root作为方法的入参
Node<Integer> root = new Node<>();
root.data = 0;
Node<Integer> preNode = root;
for (int i = 1; i < 5; i++) {
Node<Integer> node = new Node<>();
node.data = i;
preNode.next = node;
preNode = node;
}
使用循环来翻转单链表
思路
翻转单链表,其实就是将当前节点指向当前节点的上一个节点,但是如果直接赋值(node.next = preNode
),那么我们会丢失当前节点和下一个节点的联系,循环继续不下去,所以在这之前需要保存当前节点的下一个节点信息(nextNode = node.next
),最后我们需要将指向当前节点的指针向前移动(也就是指向next),继续翻转,知道当前节点的next指向空。
public <T> Node<T> revertByLoop(Node<T> node) {
Node<T> preNode = null;
while (node != null) {
Node<T> nextNode = node.next;
node.next = preNode;
preNode = node;
node = nextNode;
}
return preNode;
}
使用递归来翻转单链表
思路
递归首先是直接“找到”最后一个节点“(反向链表的根节点)并返回,以后每次递归完成都返回这个根节点。假设此时程序执行到返回根节点处,下一步我们需要将根节点指向当前递归的节点(node.next.next = node。这里没有使用header.next = node是因为我们每次需要返回这个header,如果使用header.next那么每次赋值都会覆盖上一次的结果),并将当前节点的next置为null,然后返回根节点。
递归和循环的不同是,每次递归并没有覆盖当前节点的next,而是覆盖”当前节点的下一个节点(原始顺序)“的next
public <T> Node<T> revertByRecursive(Node<T> node) {
if (node.next == null) {
return node;
}
Node<T> root = revertByRecursive(node.next);
node.next.next = node;
node.next = null;
return root;
}
其他
其实递归也有一种和循环相似的写法,需要自己保存根节点,我这里是浅拷贝了一个根节点
public <T> Node<T> revert(Node<T> node) {
Node<T> root = new Node<>();
revert(root, node);
return root;
}
public <T> Node<T> revert(Node<T> root, Node<T> node) {
if (node.next != null) {
Node child = revert(root, node.next);
child.next = node;
} else {
root.data = node.data;
root.next = node.next;
return root;
}
node.next = null;
return node;
}