数据结构之Java单链表反转

        本文为数据结构基础,研究得不是很深。用Java实现单链表的反转,虽然本文研究得不是很深,但是因为是数据结构,所以必须是在对Java内存比较清楚的情况下才能真正的搞懂吃透,如果对Java内存不够清楚,那最多只能学形而不能学其内在。

         首先我们要搞清楚链表是啥玩意儿?先看看定义:

         讲链表之前我们先说说Java内存的分配情况:我们new对象的时候,会在java堆中为对象分配内存,当我们调用方法的时候,会将方法加载到方法区,在方法区保存了加载类的信息,常量,静态变量等等。搞明白这个我们再来讲链表。

       链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。 这个定义估计太过书面化,对初学者来说,不好理解,其实简单点说就是酱紫的。我们创建了一个类Node,这个类当中有两个变量,一个data用于存储数据,还有一个Node类型的变量next用于存储另一个对象在java堆中的地址然后new了很多个Node类的对象,我们通过setNext方法将第二个对象node2的地址给node1保存起来,同样的将第三个对象node3的地址交给node2保存起来。通过这种方式,我们就将很多个对象连成串了,形成了一种链状。这就是链表了

       这儿着重声明:在Java中,没有地址一说,只有hashCode。其实hashCode就是通过算法,将每一个对象的地址算成一个code转成一个特有的字符串。当我们没有复写Object类的toString方法的时候,该类的对象调用toString方法,打印出来,或者不调用toString方法,直接打印该类的对象,其实就是将hashCode打印出来了。这个hashCode就相当于是内存了。

     好了搞懂了这些我们就可以来看看实例了:


节点Node类,其实节点就是我们的对象,每一个节点就是一个对象

/**
 * 其实一个节点就对应我们java中的一个对象,我们在分析的时候需要注意除了next要存储一个地址外,自己也是对象自己也有地址
 * Created by PICO-USER dragon on 2017/3/16.
 */

public class Node {

    //数据域存储数据
    private int data;

    //指针域用于存储下一个节点的地址
    private Node next;

    public Node(int data) {

        this.data = data;
    }

    public int getData() {
        return data;
    }

    public void setData(int data) {
        this.data = data;
    }

    public Node getNext() {
        return next;
    }

    public void setNext(Node next) {
        this.next = next;
    }
}

反转链表的方法,当传入的节点为null的时候,直接染回null。如果只有一个节点,头尾都是它,直接返回该节点

public static Node reverseList(Node head) {

    if (head == null) {

        return null;
    }

    if (head.getNext() == null) {
        return head;
    }

    //previous上一个节点
    Node preNode = null;
    //current节点当前节点,并让它指向传进来的对象所在地址(是保存该对象的地址,不是它的next值)
    Node curNode = head;
    //next节点下一个节点
    Node nextNode = null;

    while (curNode != null) {

        //next节点指向后一个节点所在地址,并改变新地址的值(包括datanext        nextNode = curNode.getNext();
        if (nextNode != null) {
            System.out.print("nextNode data :" + nextNode.getData() + " next :" + nextNode.getNext() + " " + nextNode + "\n");
        }

        //current节点存储的地址(也就是next)的值改为preNode节点所指向的地址(这样就把指向箭头反转了)这儿有个误区
        //注意:是将preNode指向的地址给curNodenext,不是把preNodenext给它。
        curNode.setNext(preNode);
        if (curNode != null) {
            System.out.print("curNode data :" + curNode.getData() + " next :" + curNode.getNext() + " " + curNode + "\n");
        }

        //previous节点指向的地址向后移动一个单位,并改变新地址的值(包括datanext        preNode = curNode;
        if (preNode != null) {
            System.out.print("preNode data :" + preNode.getData() + " next :" + preNode.getNext() + " " + preNode + "\n");
        }

        //current节点的索引向后移动一个单位,并改变新地址的值包括(datanext        curNode = nextNode;
        if (curNode != null) {
            System.out.print("curNode data :" + curNode.getData() + " next :" + curNode.getNext() + " " + curNode + "\n");
        }

        System.out.print("-----------------------\n");
    }

    return preNode;
}

public class MainRun {

    public static void main(String[] arg0) {

        //创建链表的节点,创建了三个对象,那就是三个节点
        Node node0 = new Node(1);
        Node node1 = new Node(2);
        Node node2 = new Node(3);
        //将这些节点,串连起来形成链表
        node0.setNext(node1);
        node1.setNext(node2);

        //链表的头结点代表了该链表,因为头结点能找到第二个,第二个能找到第三个,依次找下去,全都找到了
        Node head1 = node0;

        //先打印反转之前的链表的值,将hashCode一起打印出来,方便去每一行代码都对谁做了什么操作

        while (head1 != null) {

            System.out.print("data :" + head1.getData() + " next :" + head1.getNext() + "  " + head1.toString() + "\n");
            head1 = head1.getNext();
        }

        System.out.print("---++++++-----\n");

        //注意了,我们是从头开始反转,所以这儿不能用head1,因为head1在上面的while循环中已经成为最后一个节点了
        Node oldHead = node0;
        Node newHead = reverseList(oldHead);

        //打印反转后的节点
        while (newHead != null) {

            System.out.print("data :" + newHead.getData() + " next :" + newHead.getNext() + " " + newHead + "\n");
            newHead = newHead.getNext();
        }

    }
 
  

看看运行结果:

数据结构之Java单链表反转_第1张图片

下面给出分析结果,我自己用笔画的,网友可以根据这个分析步骤,跟着while循环的代码一句一句往下分析,每一行代码运行之后改动的值是什么?多看看,多分析分析就通了。

数据结构之Java单链表反转_第2张图片


第二种方法:递归调用实现单链表反转

/**
 * 因为递归的思想是直接更改当前节点的next的值为前一个节点所在的地址,所以需要用到两个参数,当前节点和前一个节点,
 * 这儿给外面用就只给一个方法,我们再自己封一个两个参数的方法。
 *
 * @param head
 * @return
 */
public static Node reverseList2(Node head) { return reverseListRecursively( null, head);} /** * 递归调用实现的思想很简单,就是直接改变 curNode next 的值。原本是指向后面一个节点的,现在需要改为前一个节点。 * 所以参与算法的人只有当前节点和当前的前一个节点,而下一个节点的作用只是用于让需要更换 next 的对象往后面移动 * * @param preNode * @param curNode * @return */ public static Node reverseListRecursively(Node preNode, Node curNode) { if (curNode == null) { return null; } if (curNode.getNext() == null) { curNode.setNext(preNode); return curNode; } // curNode 中保存的地址改成前一个节点所在的地址 curNode.setNext(preNode); // 如果当前节点有下一个节点就将该节点拿出来 Node nextNode = curNode.getNext(); // 递归调用本方法,相当于让 preNode curNode 指向的地址都向后移动一个单位,直到所有的节点都将自己保存的地址改为前一个为止 Node newNode = reverseListRecursively(curNode, nextNode); return newNode;}
public static void main(String[] arg0) {

    //创建链表的节点,创建了三个对象,那就是三个节点
    Node node0 = new Node(1);
    Node node1 = new Node(2);
    Node node2 = new Node(3);
    //将这些节点,串连起来形成链表
    node0.setNext(node1);
    node1.setNext(node2);

    //链表的头结点代表了该链表,因为头结点能找到第二个,第二个能找到第三个,依次找下去,全都找到了
    Node head1 = node0;

    //先打印反转之前的链表的值,将hashCode一起打印出来,方便去每一行代码都对谁做了什么操作

    while (head1 != null) {

        System.out.print("data :" + head1.getData() + " next :" + head1.getNext() + "  " + head1.toString() + "\n");
        head1 = head1.getNext();
    }

    System.out.print("---++++++-----\n");

    //注意了,我们是从头开始反转,所以这儿不能用head1,因为head1在上面的while循环中已经成为最后一个节点了
    Node oldHead = node0;
    Node newHead = reverseList2(oldHead);

    //打印反转后的节点
    while (newHead != null) {

        System.out.print("data :" + newHead.getData() + " next :" + newHead.getNext() + " " + newHead + "\n");
        newHead = newHead.getNext();
    }

}


你可能感兴趣的:(Java数据结构)