链表反转(Java三种实现方式)

本文详细介绍了链表反转的三种Java实现方法,包括递归和两种非递归算法。通过具体代码示例,深入解析每种方法的逻辑与效率,适合初学者和进阶开发者实践与学习。
该文章已生成可运行项目,

####Mackyhuang
本博文转移:链表反转(Java三种实现方式)
可移步查看

####链表这个数据结果经常遇见,这里提供一个链表反转的java代码实现,有三种算法,一种是递归的,俩种是非递归的。

####首先为了方便测试,在博文最后贴上递归实现链表创建的代码,以供读者快速上手测试,提供的代码可以复制以后直接测试

####先看看Node节点把

public class Node {
//链表用于存储值
private final int value;
//指向下一个节点 理解为Node next更加恰当
private Node node;

public Node(int value) {
    this.value = value;
    this.node = null;
}

public int getValue() {
    return value;
}

public Node getNode() {
    return node;
}

public void setNode(Node node) {
    this.node = node;
}

}

####链表反转递归方法

 public Node reverserLinkedList(Node node){
        if (node.getNode() == null || node == null){
            return node;
        }
        Node newdata = reverserLinkedList(node.getNode());
        node.getNode().setNode(node);
        node.setNode(null);
        return newdata;
    }
    //这个递归,返回值只是为了控制返回的是最后一个节点
    //然后通过递归通过栈的特性,这里就是让它可以从最后一个节点开始把自己的子节点的子节点改成自己
    //自己的子节点改为null

递归的实现总是这么的简单,代码简练就是递归的好处,而且逻辑易于处理,只要能够出找出一层的逻辑,然后找出特殊值和出口,一个递归就已经完成啦

这里出口显然就是那个if,为的是找到最后一个节点,然后就可以开始往前递归反转,同时这个if可以排除参数只有一个节点,参数为null的情况。

递归的栈累计到最高层的时候(递归本质是栈,每一次递归放入一个栈,如果这层运行结束,就会弹出,运行下一层),最后一个if结束以后, 开始反转, 反转的逻辑其实很简单, 吧当前节点的下一个节点指向自己,然后自己指向null

####说完了递归的算法,也了解递归其实就是栈,现在就用相同的逻辑,只不过把递归变成循环,用java本身实现的Stack数据结构编写一个更加高效的代码

public Node reverserLinkedList2(Node node){
        Stack<Node> nodeStack = new Stack<>();
        Node head = null;
        //存入栈中,模拟递归开始的栈状态
        while (node != null){
            nodeStack.push(node);
            node = node.getNode();
        }
        //特殊处理第一个栈顶元素(也就是反转前的最后一个元素,因为它位于最后,不需要反转,如果它参与下面的while, 因为它的下一个节点为空,如果getNode(), 那么为空指针异常)
        if ((!nodeStack.isEmpty())){
            head = nodeStack.pop();
        }
        //排除以后就可以快乐的循环
        while (!nodeStack.isEmpty()){
            Node tempNode = nodeStack.pop();
            tempNode.getNode().setNode(tempNode);
            tempNode.setNode(null);
        }
        return head;
    }

逻辑一目了然,备注上面的解释已经很清楚啦

####还有一个循环写法更加简单,使用俩个指针,不需要栈结构

public Node reverserLinkedList3(Node node){
		//指向空,可以想象成位于第一个节点之前
        Node newNode = null;
        //指向第一个节点
        Node curNode = node;

		//循环中,使用第三变量事先保存curNode的后面一个节点
		
        while (curNode != null){
            Node tempNode = curNode.getNode();
            //把curNode反向往前指
            curNode.setNode(newNode);
            //newNode向后移动
            newNode = curNode;
            //curNode 向后移动
            curNode = tempNode;
        }

        return newNode;
    }
这个的思路就是 俩个指针,把一个链表分成俩个部分, newNode是已经反转部分, curNode是为反转部分,然后通过俩个指针的配合,不断的右移直到前部反转

####现在贴其他代码部分啦,先贴链表构建的

public class LinkedListCreator {
	//构建函数
    public Node createLinkedList(List<Integer> list){
        if (list.isEmpty()){
            return null;
        }
        Node headNode = new Node(list.get(0));
        Node tempNode = createLinkedList(list.subList(1, list.size()));
        headNode.setNode(tempNode);
        return headNode;
    }

	//测试方便的打印函数
    public void printList(Node node){
        while (node != null){
            System.out.print(node.getValue());
            System.out.print(" ");
            node = node.getNode();
        }
        System.out.println();
    }
}

####main函数

public static void main(String[] args) {
        LinkedListCreator linkedListCreator = new LinkedListCreator();
        Node node = linkedListCreator.createLinkedList(Arrays.asList(1, 2, 3, 4, 5));
        Node node2 = linkedListCreator.createLinkedList(Arrays.asList(1, 2, 3, 4, 5));
        Node node3 = linkedListCreator.createLinkedList(Arrays.asList(1, 2, 3, 4, 5));
        LinkedListReverser linkedListReverser = new LinkedListReverser();

        Node res = linkedListReverser.reverserLinkedList(node);
        Node res2 = linkedListReverser.reverserLinkedList2(node2);
        Node res3 = linkedListReverser.reverserLinkedList3(node3);

        linkedListCreator.printList(res);
        linkedListCreator.printList(res2);
        linkedListCreator.printList(res3);
    }





本文章已经生成可运行项目
### 单向链表反转的基本思想 单向链表反转的核心思想是通过改变链表中每个节点的指针方向,将原本指向下一个节点的指针改为指向前一个节点。这一过程需要三个指针来辅助完成:当前节点、前一个节点和后一个节点,以确保在指针方向改变后仍能正确遍历链表。 ### Java实现单向链表反转的方法 #### 方法一:迭代法 迭代法是最常用的实现方式,适用于大多数场景。其基本步骤如下: 1. 初始化三个指针:`prev`(前一个节点)设为`null`,`current`(当前节点)设为链表头节点,`next`(后一个节点)用于临时保存当前节点的下一个节点。 2. 遍历链表,直到当前节点为`null`(即到达链表末尾)。 3. 在每次迭代中,将当前节点的`next`指针指向`prev`,然后将`prev`、`current`和`next`依次向后移动。 以下是Java代码实现: ```java public class Node { int data; Node next; public Node(int data) { this.data = data; } public void setData(int data) { this.data = data; } public int getData() { return data; } public void setNext(Node next) { this.next = next; } public Node getNext() { return next; } } public class NodeList { private Node head; public Node getHead() { return head; } public void setHead(Node head) { this.head = head; } // 反转链表方法 public void reverseList() { Node prev = null; Node current = head; Node next; while (current != null) { next = current.getNext(); // 保存下一个节点 current.setNext(prev); // 将当前节点的next指针指向前一个节点 prev = current; // 移动prev和current current = next; } head = prev; // 反转完成后,prev指向新的头节点 } } ``` #### 方法二:递归法 递归法是另一种实现方式,适用于需要简洁代码的场景。其基本思想是递归地反转链表的剩余部分,然后将当前节点的`next`指针指向它的前一个节点。 以下是Java代码实现: ```java public class NodeList { private Node head; public Node getHead() { return head; } public void setHead(Node head) { this.head = head; } // 递归反转链表方法 public Node reverseListRecursive(Node current, Node prev) { if (current == null) { return prev; // 当到达链表末尾时,prev指向新的头节点 } Node next = current.getNext(); // 保存下一个节点 current.setNext(prev); // 将当前节点的next指针指向前一个节点 return reverseListRecursive(next, current); // 递归处理下一个节点 } public void reverseList() { head = reverseListRecursive(head, null); // 调用递归方法 } } ``` ### 方法三:使用栈 使用栈的方法是通过将链表节点依次压入栈中,然后依次弹出重新连接。这种方法较为直观,但会占用额外的内存空间。 以下是Java代码实现: ```java import java.util.Stack; public class NodeList { private Node head; public Node getHead() { return head; } public void setHead(Node head) { this.head = head; } // 使用栈反转链表方法 public void reverseListUsingStack() { if (head == null) { return; // 空链表直接返回 } Stack<Node> stack = new Stack<>(); Node current = head; // 将所有节点压入栈中 while (current != null) { stack.push(current); current = current.getNext(); } // 弹出栈顶元素重新连接 head = stack.pop(); current = head; while (!stack.isEmpty()) { current.setNext(stack.pop()); current = current.getNext(); } current.setNext(null); // 最后一个节点的next设为null } } ``` ### 测试类 测试类`TestNode.java`用于验证链表反转的正确性。以下是一个简单的测试用例: ```java public class TestNode { public static void main(String[] args) { NodeList list = new NodeList(); // 创建链表 1 -> 2 -> 3 -> 4 -> null Node node1 = new Node(1); Node node2 = new Node(2); Node node3 = new Node(3); Node node4 = new Node(4); node1.setNext(node2); node2.setNext(node3); node3.setNext(node4); list.setHead(node1); // 打印原始链表 System.out.println("Original list:"); Node current = list.getHead(); while (current != null) { System.out.print(current.getData() + " "); current = current.getNext(); } // 反转链表 list.reverseList(); // 可替换为reverseListRecursive或reverseListUsingStack // 打印反转后的链表 System.out.println("\nReversed list:"); current = list.getHead(); while (current != null) { System.out.print(current.getData() + " "); current = current.getNext(); } } } ``` ### 注意事项 - **引用[^2]中提到的问题**:在反转链表时,需要注意方法结束时`head`指向的地址是否仍然有效。通过将`head.next`指向反转后的链表头节点,可以确保方法结束后`head`仍然指向正确的节点。 - **性能优化**:迭代法通常比递归法更高效,因为递归法可能会导致栈溢出,尤其是在链表非常长的情况下。栈方法虽然直观,但会占用额外的内存空间。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值