链表——Java实现

来源:互联网 发布:淘宝集市店 编辑:程序博客网 时间:2024/05/16 19:28

 链表的基本结构

public class LNode {    public String data;//节点数据    LNode next;//下一个节点}

这是最基本的链表结构,还有双向链表和循环链表会在文章最后提到。

建立单链表

头部插入建立单链表

头部插入建立方式的思想是,首先建立一个指针,
1.然后新建第一个节点,让指针指向这个节点
2.然后再新建一个节点,通过指针拿到第一个点的地址,让新建的下一个地址指向第一个节点。
3.最后把指针移动到第一个节点上。
后面重复这个过程就可以
这里写图片描述
大家可以根据这张图来理解一下。

下面是实现的代码

// 在头部插入    public LNode Creat_LinkListHead() {        LNode head = null;// 头部,空表        System.out.println("现在使用头部插入法,请输入数据,以0为结束");        String newDate;        while (!(newDate = in.next()).equals("0")) {            LNode newNode = new LNode();//新建一个节点            newNode.data = newDate;            newNode.next = head;//通过指针找到上一个节点地址,让新节点的下一个地址指向新节点            head = newNode;//让指针移动到新建的节点上        }        System.out.println("录入完毕");        return head;    }

在尾部插入建立链表

在头部插入的方式,有一个很大的问题,就是最后我指针的位置,在整个链表逻辑上 的最后位,遍历的时候,会反过来,比方说建立的顺序是ABCDE,遍历的时候就是EDCBA了
在尾部插入建立的方式就是为解决这个问题
尾部插入的思路是:首先我们建立一个头节点和一个尾节点,让头节点和尾节点一起指向第一个节点,之后的话头结点不再变动,尾节点用头部插入的方式建立起链表,可以发现这个方式和头部插入没有什么区别,主要就是增加了一个头节点。
下面是实现的代码

// 在尾部插入    public LNode Creat_LinkListTail() {        LNode H = null;// 头指针        LNode R = null;// 尾指针        String newDate;        System.out.println("现在使用尾部插入法,以0为结束");        while (!(newDate = in.next()).equals("0")) {            LNode newNode = new LNode();            newNode.data = newDate;            // 第一个指针            if (H == null) {                H = newNode;            } else {                R.next = newNode;            }            //指针移动到创建的新节点            R = newNode;        }        //让随后一个节点指向null        if (R != null)            R.next = null;        return H;    }

带头节点的尾部插入

上面方式也有一个问题,第一个节点由于没有前驱,所以我们在很多操作的时候都要考虑这个特殊的点,这样在增加删除的时候都会比较麻烦,比较好的解决方式,我们可以第一个节点前面在加一个节点,这个节点没有数据,只是指向原来的第一个节点。如下图
这里写图片描述

H代表的是头指针
R代表的是尾指针

新增一个头结点后就可以很好的解决这个问题,但在遍历,定位节点的时候要注意哥不带头结点的链表的区别,这个头结点 只是用来作为前驱存放原本第一个节点的地址的。

public LNode Creat_LinkListTailWithHead() {        LNode H = null;//头指针        LNode R = null;//尾指针        //建立头结点        LNode headNode = new LNode();        headNode.next = null;        H = headNode;        R = headNode;        System.out.println("现在使用带头结点的的链表尾部插入,输入0为结束");        String newDate;        while(!(newDate=in.next()).equals("0")){            LNode newNode = new LNode();            newNode.data =  newDate;            newNode.next = null;            R.next = newNode;            R=newNode;        }        R.next = null;        return H;    }

遍历链表

遍历的思路是从头结点开始,输出节点的数据, 之后让指针移动到下一个节点

//遍历链表    public void look(LNode head) {        if (head == null) {            System.out.println("链表为空");            System.exit(0);        }        LNode flag = head;//用一个指针来拿到头结点的地址        do {            System.out.print(flag.data);            flag = flag.next;//下移到下一个节点        } while (flag != null);//如果节点为空结束循环    }

当然这个遍历是有问题的,没有判断链表有没有带头结点,大家可以自行修改一下。

计算链表长度

这个的思路的遍历差不多,只不过我们需要定义一个变量用来存储长度

    //计算带头结点的链表的长度    public int lenth_haveHead(LNode H){        int len = 0 ;//用来存放长度        while(H.next!=null){            H=H.next;            len++;        }        return len;    }    //不带头结点的链表的长度    public int lenth_nothaveHead(LNode H){        int len = 0;//用来存放长度        //判断链表是否为空        if(H==null){            System.out.println("链表为空");            return 0;        }        len = 1;        while(H.next!=null){            H = H.next;            len++;        }        return len;    }

可以看出,带头节点的链表在遍历上就比不带的方便很多。

查询指定节点

    //查询指定节点    //n 代表第几个节点    public LNode findLNode(int n,LNode H){        LNode myH;        if(H.data == null){            //带头结点            myH = H.next;        }else{            //不带头结点            myH = H;        }        int len = 0;        while(myH!=null){            len++;            if(len == n) break;            myH = myH.next;        }        return myH;    }

首先我们知道带头节点的链表的第一个节点,没有数据,用这个作为标志来判断链表是哪种链表。
如果是带头结点的,那链表的第一位应该是头结点.next

后排插入

后排插入就是在一个节点的后面,插入一个新节点
思路如下
我们先找到这个节点
通过这个节点的next得到下一个节点的地址,让新节点的next指向下一个节点。
让被插的节点将next指向新的节点
这里写图片描述
下面给出实现代码

    //在第n的节点后插入data    public void insertBehind(String data,int n,LNode H){        MyLinkList list = new MyLinkList();        LNode newNode = new LNode();//新建节点        newNode.data = data;        newNode.next = null;        LNode node = list.findLNode(n, H);//找到要插入的位置        if(node == null) {            System.out.println("找不到节点");            return;        }        //获取前驱的next,也就是下一个节点        newNode.next = node.next;        //让前驱指向新的节点        node.next = newNode;    }

前排插入

前排插入的思想和后排插入差不多,只不过我们再拿到节点后,需要再去查找一次,找到这的节点的前驱,之后以这个前驱做后排插入就行了。

/*     * 在第n个节点前面插入一个节点     * */    public void insertFront(String data,int n,LNode H){        LNode newNode = new LNode();        newNode.data = data;        newNode.next = null;        MyLinkList list = new MyLinkList();        //找到要插入的节点        LNode node = list.findLNode(n, H);        if(node==null){            System.out.println("找不到节点");            return;        }        //找到node节点的前驱        LNode myH = H;        while(myH.next!=node){            myH = myH.next;        }        newNode.next = node;        myH.next = newNode;    }

删除节点

删除节点的思路如下:
找到要删除的节点
再找到他的前驱节点
让前驱的next指向这个节点的next,这个时候要删除的节点已经被架空了
最后我们给这个要删除的节点赋值null 释放空间
这里写图片描述

    MyLinkList list = new MyLinkList();        LNode node = list.findLNode(n, H);        if(node == null) {            System.out.println("找不到要删除的节点");            return;        }        //找到这个节点的前驱        LNode frontNode = list.findLNode(n-1, H);        if(frontNode==null){            System.out.println("找不到前驱");            return;        }        frontNode.next = node.next;        node = null;    }

循环链表

循环链表就是将最后一个节点,原来指向空,改成指向头部,实质上并没有什么区别,主要是判断是不能用null来做链表结束的条件了

双向链表

单向的连接再很多时候会有麻烦,比如我们的前排插入,我们要得到前驱,不得不再做一次遍历,双向链表正是为了解决这个问题,我们再结构中再加一个指针让他指向前驱就可以了。