• 数据结构-链表的那些事(下)(二)


    一、谈一下博客这一问题?

          博客这个我相信已经成为大家了解和学习技术的一个手段,但是真真正正有多少人去按照这个操作了,我看到一条1000+人看的博客,我发现一个简单的小错误,但是我之前的1000多人难道没发现嘛?这个问题值得我们深思?我不是指着作者问题,因为写在作者,看在你,我们无权干涉,当然写就会出现错误,毕竟程序还有BUG,我只是想说有些时候不是只要看一遍就可以我们需要是真实操作,毕竟好记性不然烂笔头,这个问题就讨论学习这个深刻的话题上来了,就远离我们的主题了,我只是将这个问题抛出啦,引发下大家的深思,具体该怎么做,就是大家的事,因为毕竟每个人的特色都不同,当然就有自己的社会主义道路,一切按照自己的来就好,回归正题,不知道为啥我上一篇被移除去了,但是我的群里还是收获了2个小伙伴,这就是我写下的动力。

    二、谈一下双链表

          双链表的优点:对于链表中一个节点来说,都可以从两个方向操作。当然这个问题自然就带了另外一个缺点,因为前节点的存在导致更多的内存开销,另外在插入和删除的时候会更加的麻烦,需要更多时间,所以说这个世界就是这样有利就有弊,接下来我们看一下双链表的结构,用张图表示:

        头部节点首部为空,尾部节点的下一个节点指向为空,中间节点上尾指下头,下头指上尾;

        1)增加一个节点,有2种情况在节点之前和节点之后;

        2)增加节点的有3种情况首节点,尾节点和中间节点;

        3)删除同增加一样;

        以下是Java实现的双链表和测试代码,大家可以看一下

        这个是定义的节点

     1 package com.wtz.dataStruct;
     2 
     3 /**
     4  * Created by wangt on 2017/6/29.
     5  * 定义一个双链表的节点
     6  */
     7 public class Node<T> {
     8     public T getItem() {
     9         return item;
    10     }
    11 
    12     public void setItem(T item) {
    13         this.item = item;
    14     }
    15 
    16     public Node<T> getPervious() {
    17         return pervious;
    18     }
    19 
    20     public void setPervious(Node<T> pervious) {
    21         this.pervious = pervious;
    22     }
    23 
    24     public Node<T> getNext() {
    25         return next;
    26     }
    27 
    28     public void setNext(Node<T> next) {
    29         this.next = next;
    30     }
    31 
    32     private T item;
    33     private Node<T> pervious;
    34     private Node<T> next;
    35     public Node(){
    36 
    37     }
    38     public Node(T item){
    39         this.item=item;
    40     }
    41 
    42 }
    View Code

       这个是具体实现

    package com.wtz.dataStruct;
    
    /**
     * Created by wangt on 2017/6/29.
     */
    public class DoubleLinkList<T> {
        private Node<T> first;//头节点
        private int count;//总节点的个数
    
        public  DoubleLinkList()
        {
            //初始化为空的链表
            this.count=0;
            this.first=null;
        }
    
        //返回节点的个数
        public  int size(){
            return  count;
        }
        //根据索引获取节点
        public Node<T> getNodeIndex(int index){
            if(index<0&&index>=count){
                throw new IndexOutOfBoundsException("超出索引");
            }
            Node<T> tempNode=this.first;
            for (int i=0;i<index;i++){
                tempNode=tempNode.getNext();
            }
            return  tempNode;
        }
        //在未节点之后插入节点
        public void addAfter(T value){
            Node<T> node=new Node<T>(value);
            if(this.first==null){
                this.first=node;
            }else {
                Node<T> lastNode=getNodeIndex(count);
                lastNode.setNext(node);
                node.setPervious(lastNode);
                count++;
            }
    
        }
        //末节点之前插入
        public void addBefore(T value){
            Node<T> node=new Node<T>(value);
            if (this.first==null){
                this.first=node;
            }else {
                //获取最后一个节点和倒数第二个节点
                Node<T> lastNode=getNodeIndex(count);
                Node<T> lastTwoNode=lastNode.getPervious();
    
                //将倒数第二个节点的下一个节点设置为插入节点
                lastTwoNode.setNext(node);
                node.setPervious(lastTwoNode);
                //将最后一个节点上一个节点指向插入的节点
                lastNode.setPervious(node);
                node.setNext(lastNode);
                count++;
            }
        }
        //指定位置之前插入
        public void addIndexBefore(T value,int index){
            Node<T> node=new Node<T>(value);
            if(index==0){
                if(this.first==null){
                    this.first=node;
                }else {
                    node.setNext(first);
                    first.setPervious(node);
                    first=node;
                    count++;
                }
            }else {
                Node<T> nextNode=getNodeIndex(index);
                Node<T> afterNode=nextNode.getPervious();
    
                afterNode.setNext(node);
                node.setPervious(afterNode);
    
                node.setNext(nextNode);
                nextNode.setPervious(node);
                count++;
            }
    
        }
    
        //指定位置之后插入
        public void addIndexAfter(T value,int index){
            Node<T> node=new Node<T>(value);
            if (index==0){
                if (first==null){
                    first=node;
                }else {
                    node.setPervious(first);
                    node.setNext(first.getNext());
                    first.setNext(node);
                    count++;
                }
            }else {
                Node<T> afterNode=getNodeIndex(index);
                Node<T> nextNode=afterNode.getNext();
    
                afterNode.setNext(node);
                node.setPervious(afterNode);
    
                node.setNext(nextNode);
                nextNode.setPervious(node);
                count++;
            }
        }
    
        //删除指定位置
        public void  removeIndex(int index){
            if(index<0&&index>=count){
                throw new  IndexOutOfBoundsException("超出范围");
            }
            if(index==0){
                first=first.getNext();
            }else {
                Node<T> beforeNode=getNodeIndex(index-1);
    
                Node<T> deleteNode=beforeNode.getNext();
                Node<T> nextNode=deleteNode.getNext();
    
                beforeNode.setNext(nextNode);
                nextNode.setPervious(beforeNode);
            }
            count--;
        }
    
    }
    View Code

      这个是测试代码

    package com.wtz.dataStruct;
    
    /**
     * Created by wangt on 2017/6/30.
     */
    public class TestDemo {
        public static void main(String[] args) {
            DoubleLinkList<Integer> doubleLinkList=new DoubleLinkList<Integer>();
            //最后一个节点之后
            doubleLinkList.addAfter(2);
            doubleLinkList.addAfter(5);
            doubleLinkList.addAfter(10);
            for (int i=0;i<=doubleLinkList.size();i++){
                System.out.print(doubleLinkList.getNodeIndex(i).getItem());
            }
            System.out.print("-------------------------------------------------");
            doubleLinkList.addBefore(10);
            doubleLinkList.addBefore(13);
            doubleLinkList.addBefore(18);
            doubleLinkList.addBefore(17);
            doubleLinkList.addBefore(16);
            for (int i=0;i<=doubleLinkList.size();i++){
                System.out.print(doubleLinkList.getNodeIndex(i).getItem());
            }
            System.out.print("-------------------------------------------------");
            doubleLinkList.removeIndex(2);
            for (int i=0;i<=doubleLinkList.size();i++){
                System.out.print(doubleLinkList.getNodeIndex(i).getItem());
            }
            System.out.print("-------------------------------------------------");
            doubleLinkList.addIndexAfter(111,0);
            for (int i=0;i<=doubleLinkList.size();i++){
                System.out.print(doubleLinkList.getNodeIndex(i).getItem());
            }
            System.out.print("-------------------------------------------------");
            doubleLinkList.addIndexBefore(444,0);
            for (int i=0;i<=doubleLinkList.size();i++){
                System.out.print(doubleLinkList.getNodeIndex(i).getItem());
            }
        }
    
    }
    View Code

    二、循环链表

        循环链表我们这里用单向循环列表来描述这一问题,来述说下循环链表与链表的差距,循环的意思就是从从头到尾,从尾到头连起来,我们已经知道单链表的概念,那么二者差距就很明显了,单向循环链表就是之前的尾部的空节点指向首部节点这。还是用图表示下单向循环链表:

         套路还是和之前一样用代码实现单向循环链表,这里需要提醒下注意下循环的时候,如果不消息很容易照成死循环,大家在使用循环链表的时候需要很注意这一点。另外我们当查找最后一个节点的时候就没有必要遍历所有节点,因为尾部的指针的下一个节点是首部,这样就可以将第一个和最后一个节点控制到O(1)访问时间。下面就是代码的实现。

    public class CirNode<T> {
        public T getItem() {
            return item;
        }
    
        public void setItem(T item) {
            this.item = item;
        }
    
        public CirNode<T> getNext() {
            return next;
        }
    
        public void setNext(CirNode<T> next) {
            this.next = next;
        }
    
        private T item;
        private CirNode<T> next;
        public CirNode(T item){
            this.item=item;
        }
    
    
    }
    
    package com.wtz.dataStruct;
    
    import java.util.Currency;
    
    /**
     * Created by wangt on 2017/6/30.
     */
    public class SingleCircularList<T> {
        private int count;
        private CirNode<T> pointer;//尾部节点的指针这里为什么不用节点大家思考一下哈哈
    
        public SingleCircularList()
        {
            this.count=0;
            this.pointer=null;
        }
        //返回节点的个数
        public  int size()
        {
            return  count+1;
        }
        //获取当前的节点
        public CirNode<T> getIndexNode(int index)
        {
            if(index<0&&index>count){
                throw  new  IndexOutOfBoundsException("超过索引范围");
            }
            CirNode<T> node=pointer;
            for (int i=0;i<index;i++){
                node=node.getNext();
            }
            return  node;
        }
        //当前节点之前插入
        public void addBefore(T value)
        {
            CirNode<T> cirNode=new CirNode<T>(value);
            if(pointer==null){
                pointer=cirNode;
                pointer.setNext(cirNode);
            }else {
                cirNode.setNext(pointer.getNext());
                pointer=cirNode;
                count++;
            }
        }
        //当前节点之后插入
        public void addAfter(T value)
        {
            CirNode<T> cirNode=new CirNode<T>(value);
            if(pointer==null){
                pointer=cirNode;
                pointer.setNext(cirNode);
            }else {
                cirNode.setNext(pointer.getNext().getNext());
                pointer.getNext().setNext(cirNode);
                count++;
            }
        }
        //在某节点之前插入
        public  void addIndexBefore(T value,int index)
        {
            CirNode<T> cirNode=new CirNode<T>(value);
            if (index==0){
                if (pointer==null){
                    pointer=cirNode;
                    pointer.setNext(cirNode);
                }else {
                    pointer=cirNode;
                    cirNode.setNext(pointer.getNext());
                    count++;
                }
            }else {
                CirNode<T> nextNode=getIndexNode(index-1);
                CirNode<T> afterNode=getIndexNode(index-2);
    
                afterNode.setNext(cirNode);
                cirNode.setNext(nextNode);
                count++;
             }
    
        }
        //在某节点之后插入
        public  void addIndexAfter(T value,int index){
            CirNode<T> node=new CirNode<T>(value);
            if (index==0){
                if (pointer==null){
                    pointer=node;
                    pointer.setNext(node);
                }else {
                    node.setNext(pointer.getNext().getNext());
                    pointer.getNext().setNext(node);
                    count++;
                }
            }else {
                CirNode<T> currentNode=getIndexNode(index-1);
                node.setNext(currentNode.getNext());
                currentNode.setNext(node);
                count++;
            }
        }
    
        public  void deleteIndex(int index){
            if(index>0&&index>count){
                throw  new  IndexOutOfBoundsException("超过索引范围");
            }
            if (index==0){
                pointer=pointer.getNext().getNext();
            }else {
                CirNode<T> beforeNode=getIndexNode(index-2);
                CirNode<T> deleteNode=beforeNode.getNext();
                CirNode<T> nextNode=deleteNode.getNext();
    
                beforeNode.setNext(nextNode);
                deleteNode=null;
            }
            count--;
    
        }
    
    
    
    
    }
    package com.wtz.dataStruct;
    
    /**
     * Created by wangt on 2017/7/4.
     */
    public class TestCirTest {
        public static void main(String[] args) {
            SingleCircularList<Integer> singleCircularList = new SingleCircularList<Integer>();
            singleCircularList.addBefore(5);
            singleCircularList.addBefore(2);
            for (int i=0;i<singleCircularList.size();i++){
                System.out.print(singleCircularList.getIndexNode(i).getItem());
            }
            System.out.print("-------------------------------------------------");
            singleCircularList.addAfter(9);
            singleCircularList.addAfter(6);
            for (int i=0;i<singleCircularList.size();i++){
                System.out.print(singleCircularList.getIndexNode(i).getItem());
            }
            System.out.print("-------------------------------------------------");
    
            singleCircularList.addIndexAfter(3,2);
            for (int i=0;i<singleCircularList.size();i++){
                System.out.print(singleCircularList.getIndexNode(i).getItem());
            }
            System.out.print("-------------------------------------------------");
            singleCircularList.addIndexBefore(4,2);
            for (int i=0;i<singleCircularList.size();i++){
                System.out.print(singleCircularList.getIndexNode(i).getItem());
            }
            System.out.print("-------------------------------------------------");
            singleCircularList.deleteIndex(2);
            for (int i=0;i<singleCircularList.size();i++){
                System.out.print(singleCircularList.getIndexNode(i).getItem());
            }
            System.out.print("-------------------------------------------------");
        }
    
    }
    View Code

    三、LinkedList源码解读

        老套路先看继承结构,

    public class LinkedList<E>
        extends AbstractSequentialList<E>
        implements List<E>, Deque<E>, Cloneable, java.io.Serializable

      AbstractSequentialList这个实现了get,set,add,remove等这些方法,都可以随机访问集合,这些操作允许将链接列表用作堆栈、队列或双端队列。接下来说下List接口这个接口与ArrayList一样实现List接口,只是ArrayList是List接口的大小可变数组的实现,LinkedList是List接口链表的实现。这样的结构使得插入和删除会优于ArryList,但是在读取的时候会比List差,这个在上一篇那个对比的时候相信大家可以看出来。Deque这个接口相当于是对这个扩展,提供先进先出队列操作,以及其他堆栈和双端队列操作。

      接下来看构造函数:一个空参构造,一个是集合,这里会调用addAll的方法添加到列表中;

        public LinkedList() {
        }
        public LinkedList(Collection<? extends E> c) {
            this();
            addAll(c);
        }

      接下来我们看一下内部类:

        private static class Node<E> {
            E item;
            Node<E> next;
            Node<E> prev;
    
            Node(Node<E> prev, E element, Node<E> next) {
                this.item = element;
                this.next = next;
                this.prev = prev;
            }
        }

       这里我们很快就想起我们那个双链表结构了,不多说。

       接下来我们看一下属性:

        transient int size = 0;
    //元素的个数
    transient Node<E> first; //头节点 transient Node<E> last;
    //尾节点

        接下来的API接口是没有什么好说的都比较简单,只要能看懂我上面双链表的应该看这个不是问题,这里不做过多说明。

    四、结束语

        最近在搞一些基本Liunx命令和SSH,感觉进度优点缓慢,转还是有点费劲,不过集合写完还会有多线程,这两块还是必须搞定的,还是推荐下QQ群,上次有2个小伙伴加入了,动力呀438836709

  • 相关阅读:
    Idea默认的全局设置,如Maven等
    mybatis中Parameter index out of range (1 > number of parameters, which is 0).
    SpringBoot入门-2(两种热部署方式)
    Java中关于static语句块的理解
    HashMap源码剖析
    keytool用法总结
    Tomcat配置https
    git的安装及其使用
    java中Arrays类的应用
    三次握手四次挥手
  • 原文地址:https://www.cnblogs.com/wtzbk/p/7098982.html
Copyright © 2020-2023  润新知