数据结构-双向链表

1.链表

循环链表:与单向链表的区别在于尾节点的地址域(指针域)不为null,它指向了首节点的引用。循环链表是另一种形式的链式存贮结构。它的特点是表中最后一个结点的指针域指向头结点,整个链表形成一个环。

优点:可以从链表的任意节点出发,都能够通过后移操作,而扫描整个循环链表。

双端链表:增加了一个对尾节点的指针。链表中保存着对最后一个链节点引用的链表。

 双向链表:

双向链表也叫双链表是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱。所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点。

优点:对于链表中的任意一个节点,可以从两个方向来进行操作。

缺点:每个节点增加了一个前驱节点的指针,所以说,在内存上,要更多的消耗存储空间。

next:指向后继节点的地址

prior:指向前驱节点的地址

删除双向链表的节点:B节点

  b.前驱节点.Next=B.Next指针;(A.next = B.next)

  b.后继节点.prior=B.Prior指针;(B.next.prior = B.prior)

插入E节点;

E.Prior=B节点;

E.Next=B.Next

B.Next.Prior(C节点)=E节点

B.Next=E节点

2.用java代码模拟双向链表的查询,删除,遍历(正向遍历和反向遍历),增加节点(在指定节点前增加增加新的节点)

package com.cym.datastructure.linked_list;

/**
 * 双向链表
 */
public class DoubleLInkedList {
    //定义头节点
    Node headNode;
    //定义链表的大小
    int sise;
    //定义尾节点
    Node endNode;

    /**
     * 内部类,定义链表的节点,元素
     */
    class Node {
        //定义节点存储的数据
        Object data;
        //定义下一个节点
        Node next;
        //定义前一个节点
        Node prior;

        public Node(Object data) {
            this.data = data;
        }
    }

    /**
     * 定义一个获得空节点节点的方法
     */
    public Node getNode(Object object) {
        return new Node(object);
    }

    /**
     * 链表的判空
     */
    public boolean isEmpty() {
        return sise == 0;
    }

    /**
     * 链表节点的插入(初始化)
     */
    public void addNode(Object object) {
        Node newNode = new Node(object);
        //判断链表是否为空,如果为空插入的节点作为头节点就可以了
        if (isEmpty()) {
            headNode = newNode;
            //同时尾节点也是第一个插入的节点
            endNode = newNode;
        } else {
            //如果链表中已经存在了节点,就把新加入的节点指向原有的的头节点(就是把头节点的地址存在新加入节点的newNode.next中)
            newNode.next = headNode;
            //把后继节点指向前驱结点(双向链表A<--->B)
            headNode.prior = newNode;
            //新加入的节点就作为该链表的新的头节点(因为链表的加入是从头节点开始插入的)
            headNode = newNode;
        }
        sise++;
    }

    /**
     * 遍历该链表(正向遍历)
     */
    public void traverseSingleLinkedListsByHeadNode() {
        traverseSingleLinkedList(headNode);
    }

    /**
     * 遍历该链表(犯贱向遍历)
     */
    public void traverseSingleLinkedListsByEndNode() {
        traverseSingleLinkedList(endNode);
    }
    /**
     * 遍历双向链表的方法
     * @param traverseNode 指定是从头节点开始还是尾节点遍历
     */

    public void traverseSingleLinkedList(Node traverseNode) {
        //定义当前节点并把节点等于头节点
        Node currentNode = traverseNode;
        if (!isEmpty()) {
            if (sise == 1) {
                System.out.println("[" + currentNode.data + "]");
                return;
            } else {
                //定义临时大小变量
                int tempSize = sise;
                System.out.print("[" + "\t");
                while (tempSize > 0) {
                    if (currentNode == headNode) {//头节点的时候
                        System.out.print(currentNode.data);
                        if (traverseNode == headNode) {
                            System.out.print("<--->");
                        }
                    } else if (currentNode.next == null) {//尾节点的时候
                        System.out.print(currentNode.data);
                        if (traverseNode == endNode) {
                            System.out.print("<--->");
                        }
                    } else {//中间节点的时候
                        System.out.print(currentNode.data + "<--->");
                    }
                    //正向遍历的时候指向当前节点的后继节点
                    if (traverseNode == headNode) {
                        currentNode = currentNode.next;
                    }
                    //反向遍历的时候时候指向当前节点的前驱节点
                    if (traverseNode == endNode) {
                        currentNode = currentNode.prior;
                    }
                    tempSize--;
                }
                System.out.print("\t" + "]");
            }
        } else {
            System.out.println("[]");
        }
    }

    /**
     * 查询指定的内容(这里是正向遍历查询,当然也可以反向,参考上述的反向遍历)
     *
     * @param object 待查询的内容
     * @return
     */
    public Object findNode(Object object) {
        //判断链表是不为null
        if (!isEmpty()) {
            //指定当前节点为头节点
            Node currentNode = headNode;
            //定义链表的临时大小变量
            int tempSize = sise;
            while (tempSize > 0) {
                //判断查询的值是否和当前节点的值一样
                if (object.equals(currentNode.data)) {
                    //返回当前节点的地址的码
                    return currentNode;
                }
                //当前节点指向下一个节点
                currentNode = currentNode.next;
                tempSize--;
            }
        }
        return null;
    }

    /**
     * 在指定的节点之前插入(当然也可以在指定节点之后插入,不过在这我就不实现了,都是类型的)
     *
     * @param newNode
     * @param object
     */
    public void addNodeBySelect(Node newNode, Object object) {
        //获得指定的节点
        Node selectNode = (Node) findNode(object);
        //如果是头节点直接调用初始化链表的方法
        if (selectNode == headNode) {
            this.addNode(newNode.data);
            return;
        }
        //让新增的节点指向后继节点
        newNode.next = selectNode;
        //添加反向链
        newNode.prior = selectNode.prior;
        //让新插入的节点的前驱节点指向新加入的节点
        selectNode.prior.next = newNode;
        //添加反向链
        selectNode.prior = newNode;
        sise++;
    }

    /**
     * 删除头节点
     */
    public void deleteHeadNode() {
        if(!isEmpty()){
            //让头节点后面的一个节点成为新的头节点
            headNode = headNode.next;
            //把新的头节点的前驱结点的指针置为null
            headNode.prior = null;
            //链表大小减1
            sise--;
        }else {
            System.out.println("链表是空的");
        }
    }

    /**
     * 删除指定的节点(正向遍历删除)
     * @param object
     */
    public boolean deleteNodeByData(Object object){
        if (isEmpty()){return false;}
        //定义当前节点为头节点
        Node currentNode = headNode;
        //定义前驱结点为头节点
        Node precursorNode = headNode;
        //判断要删除的值是否和当前节点的存放的值一样,如果不同则继续循环下去
        while (!object.equals(currentNode.data)){
            if(currentNode == null){
                return false;
            }else {
                //是前驱结点等于当前节点
                precursorNode = currentNode;
                //当前节点指向下一个节点
                currentNode = currentNode.next;
            }
        }
        //判断如果删除的节点是头节点直接调用删除头节点的方法
        if(currentNode == headNode){
            this.deleteHeadNode();
        }else if (currentNode.next == null){//尾节点的时候
            //直接的把新的尾节点的后置节点置为null 如 a->b>->c,删除c把b的后置next=null
            precursorNode.next = null;
            //新的尾节点赋给全局变量尾节点的引用
            endNode = precursorNode;
        }else {//如果是中间节点节点
            /*
             *   让前驱节点的下一个节点指向当前节点的下一个节点  如 a-->b-->c-->d 删除的时候b节点
             *   前驱节点b和当前节点是c,然后b的下一个节点原来是c的但被删除了,所以指向当前节点c的下一个节点d
             */
            precursorNode.next = currentNode.next;
            //新的后继节点(c)指向前驱节点(a)
            currentNode.next.prior = precursorNode;
        }
        sise--;
        return true;
    }

    /**
     * 测试类主方法
     *
     * @param args
     */
    public static void main(String[] args) {
        DoubleLInkedList doubleLInkedList = new DoubleLInkedList();
        doubleLInkedList.addNode("A");
        doubleLInkedList.addNode("B");
        doubleLInkedList.addNode("C");
        doubleLInkedList.addNode("D");
        doubleLInkedList.traverseSingleLinkedListsByHeadNode();
        System.out.println();
        doubleLInkedList.traverseSingleLinkedListsByEndNode();
        System.out.println();
        Object obj = doubleLInkedList.findNode("B");
        System.out.println(obj);
        doubleLInkedList.addNodeBySelect(doubleLInkedList.getNode("E"), "D");
        doubleLInkedList.traverseSingleLinkedListsByHeadNode();
        System.out.println();
        doubleLInkedList.traverseSingleLinkedListsByEndNode();
        System.out.println();
        doubleLInkedList.deleteNodeByData("A");
        doubleLInkedList.traverseSingleLinkedListsByHeadNode();
        System.out.println();
        doubleLInkedList.traverseSingleLinkedListsByEndNode();
    }
}

输出:

[	D<--->C<--->B<--->A	]
[	A<--->B<--->C<--->D	]
com.cym.datastructure.linked_list.DoubleLInkedList$Node@154617c
[	E<--->D<--->C<--->B<--->A	]
[	A<--->B<--->C<--->D<--->E	]
[	E<--->D<--->C<--->B	]
[	B<--->C<--->D<--->E	]

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值