算法之链表反转

算法系列

1、链表反转

一、核心思路

现在有一个单向链表,链表的第一个节点指向第二个,第二个节点指向第三个,以此类推,组成一个链表,按理说这个链表能无限大,只要内存足够大。
什么是链表反转?就是把链表倒过来,最后一个节点变成了头节点,倒数第二个节点变成正序第二个,这样把整个链表进行反转过来。
那怎么实现呢,有两种方法,第一种是循环来操作链表进行反转,第二种是递归的方式进行反转。

二、循环反转

先看思路,请看下图:
循环反转链表

看图说话,思路是这样子的,循环遍历一遍链表,把链表的每一个节点指针指向前一个节点,再把下一个节点的 next 属性指向上一个节点。
这里我们需要借助两个变量,prevnext 来操作。

比如在第一次遍历的时候,当前的节点就是 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;
    }

四、总结

算法是一种思想,是一种解决问题的方法,不限制任何工具和语言,多联系算法则在日常的工作中思路更多。

猜你喜欢

转载自blog.csdn.net/qq_19283249/article/details/129910598