算法系列
1、链表反转
一、核心思路
现在有一个单向链表,链表的第一个节点指向第二个,第二个节点指向第三个,以此类推,组成一个链表,按理说这个链表能无限大,只要内存足够大。
什么是链表反转?就是把链表倒过来,最后一个节点变成了头节点,倒数第二个节点变成正序第二个,这样把整个链表进行反转过来。
那怎么实现呢,有两种方法,第一种是循环来操作链表进行反转,第二种是递归的方式进行反转。
二、循环反转
先看思路,请看下图:
看图说话,思路是这样子的,循环遍历一遍链表,把链表的每一个节点指针指向前一个节点,再把下一个节点的 next 属性指向上一个节点。
这里我们需要借助两个变量,prev 和 next 来操作。
比如在第一次遍历的时候,当前的节点就是 1 这个节点,先把节点 1 的 next 属性存放到一个临时变量中;
next = curr.next;
然后把节点 1 指向上一个节点(头结点没有上一个节点,则为 null);
curr.next = prev;
上一个节点此时是没有的(也就是一个null),所以需要给上一个节点指定节点,就是把当前的节点指到上一个节点;
prev = curr;
这时候当前节点就是下一个节点,下一个节点就是刚才的临时变量 next;
curr = next;
2.1 示例代码
实现代码如下:
代码中首先定义一个 Node 内部类,作为节点,节点里面是一个 int 值,和下一个节点 next,reverse() 方法用来实现列表的反转,main() 方法中定义 5 个节点,通过 reverse() 方法来反转。
public class ReverseLinkedList {
/**
* 定义节点 Node
*/
static class Node{
public int value;
public Node next;
public Node(int value, Node next) {
this.value = value;
this.next = next;
}
}
public static void main(String[] args) {
Node node5 = new Node(5, null);
Node node4 = new Node(4, node5);
Node node3 = new Node(3, node4);
Node node2 = new Node(2, node3);
Node node1 = new Node(1, node2);
Node curr = node1;
while (curr != null){
System.out.printf("%d -> ", curr.value);
curr = curr.next;
}
curr = reverse(node1);
System.out.println();
System.out.println("反转后的链表:");
while (curr != null){
System.out.printf("%d -> ", curr.value);
curr = curr.next;
}
}
/**
* 实现反转
* @param curr 当前节点,传进来的是头结点
* @return 返回是反转后的头结点
*/
private static Node reverse(Node curr){
Node prev = null, next;
while (Objects.nonNull(curr)){
//用临时变量保存当前的下一个
next = curr.next;
//把当前节点的 next 指向上一个节点
curr.next = prev;
//经过前两步操作,当前节点的 next 已经指向下一个了,指针往后移动,prev 变成当前节点
prev = curr;
//当前节点则指向刚才临时变量 next,也就是下一个节点
curr = next;
}
return prev;
}
}
测试结果:
1 -> 2 -> 3 -> 4 -> 5 ->
反转后的链表:
5 -> 4 -> 3 -> 2 -> 1 ->
三、递归反转
先看思路,请看下图:
看图说话,思路是这样子的,感觉这个比循环反转简单,递归时如果节点中的 next 不为空,说明当前节点不是最后一个节点,则递归调用自己,传当前的 next 作为参数,知道当前节点的 next 为空则返回节点。
每次操作,如果当前节点的 next 是 null,则返回,否则继续调用并传参 curr.next,则当前节点是下一个节点;
Node curr = recurrence(head.next);
然后是当前节点的next 的 next 则指向当前节点;
head.next.next = head;
到第一个节点是,前面再没有节点;
head.next = null;
最终返回 当前节点;
return curr;
3.1 示例代码
实现代码如下:
其他代码和上面一样,实现方法不一样,递归实现反转代码如下:
private static Node recurrence(Node head){
if(Objects.isNull(head.next) || Objects.isNull(head)){
return head;
}
Node curr = recurrence(head.next);
head.next.next = head;
head.next = null;
return curr;
}
四、总结
算法是一种思想,是一种解决问题的方法,不限制任何工具和语言,多联系算法则在日常的工作中思路更多。