zoukankan      html  css  js  c++  java
  • 日常学习随笔-自定义了一个双链表(注释蛮详细的)

    一、双链表结构

      最近总会抽出一些零碎的时间片段,尝试按照自己的想法自定一了一个双链表结构的集合。我发现,数组、单链表或者双链表,乃至其他结构,本质上就是一种思想,只要遵循结构的核心思想,实现方法会有很多种。数组和单链表就不多说了,前几篇也尝试自定义了,就双链表来说,就可以定义如下几种结构:

    (1)头结点+...数据结点.....+尾结点

    (2)头结点+...数据结点.....

    (3)头结点+...数据结点.....头结点

    (4)头结点+...数据结点.....+尾结点....头结点

    (5)其他结构

    本篇使用的是第(1)中结构,结构千千万,效率差异也很大,只要明白双链表的含义:

    (从网上找到了几张图,也算很清晰,如下)

    双链表结构:结构(3)

    如何删除一个结点

    如何新增一个结点

    接下来,我就直接贴出双链表-结构(1)的源码了哈!

    二、双链表:MyDoubleLinkedDefin源码

    双链表类:MyDoubleLinkedDefin.java

      1 package com.xfwl.algorithmAnalysis.linklsit;
      2 /**
      3  * 数据结构学习-双链表
      4  * @function  自定义一个双链表
      5  * @author 小风微凉
      6  * @time  2018-5-14 下午1:11:11
      7  */
      8 public class MyDoubleLinkedDefin<T> {    
      9     /**
     10      * 当前双链表的头结点
     11      */
     12     private Node head;    
     13     /**
     14      * 当前双链表的尾部结点
     15      */
     16     private Node bottom;
     17     /**
     18      * 当前链表的数据变化计数
     19      */
     20     private int modCount;
     21     /**
     22      * 当前链表中的结点个数(排除首尾这2个结点)
     23      */
     24     private int nodeCount;
     25     /**
     26      * 内置一个满足双链表的结点类
     27      */
     28     private class Node<T>{
     29         /**
     30          * 当前结点存放的数据域
     31          */
     32         public T data;
     33         /**
     34          * 当前结点的前驱结点的引用域
     35          */
     36         public Node<T> prev;
     37         /**
     38          * 当前结点的后驱结点的引用域
     39          */
     40         public Node<T> next;
     41         /**
     42          * 结点构造器
     43          */
     44         @SuppressWarnings("unchecked")
     45         public Node(T data,Node prev,Node next){
     46             this.data=data;
     47             this.prev=prev;
     48             this.next=next;
     49         }
     50     }
     51     /**
     52      * 构造器
     53      */
     54     @SuppressWarnings("unchecked")
     55     public MyDoubleLinkedDefin(){
     56         //初始化计数
     57         modCount=0;
     58         nodeCount=0;
     59         //初始化头结点和尾结点
     60         head=new Node<T>(null,null,null);
     61         bottom=new Node<T>(null,null,null);
     62         //清空双链表的数据:重置头尾结点的关联关系
     63         this.reset();
     64         System.out.println("头结点:"+head);
     65         System.out.println("尾结点:"+bottom);
     66     }
     67     /**
     68      * 重置双链表,重置头结点,尾结点
     69      */
     70     @SuppressWarnings("unchecked")
     71     private void reset(){
     72         //重置头结点
     73         this.head.data=null;
     74         this.head.prev=null;
     75         this.head.next=bottom;
     76         //重置尾结点
     77         this.bottom.data=null;
     78         this.bottom.next=null;
     79         this.bottom.prev=head;
     80     }
     81     /**
     82      * 向双链表中添加一个新的结点(追加到尾部)
     83      * @param data
     84      */
     85     public void add(T data){
     86         //判断当前数据的合法性
     87         if(data==null){
     88             throw new IllegalArgumentException("添加的参数不合法!");
     89         }
     90         //添加规则:添加当前结点到尾部结点的前驱结点
     91         Node curNode=new Node(data,this.bottom.prev,this.bottom);
     92         curNode.prev.next=curNode;
     93         this.bottom.prev=curNode;
     94         //计数++
     95         modCount++;
     96         nodeCount++;
     97         System.out.println("新加结点的前驱结点:"+curNode.prev);
     98         System.out.println("新加结点的后驱结点:"+curNode.next);
     99     }
    100     /**
    101      * 向双链表中的指定索引位置添加一个新的结点
    102      * @param index    位置索引值
    103      * @param data    结点数据域存储的值
    104      * 分析说明:
    105      *       链表和数组的结构明显存在差异,数组很容易根据索引下标的值找到指定位置的元素,但是链表却不可以,
    106      * 结构使然,所以只能从头结点,或者尾部结点开始向下或向上找到指定位置的元素。
    107      *      这里我提出一个想法,能不能在双链表中再维护一个数组,这个数组用于查找指定位置的元素,而链表结构用于
    108      * 新增,删除,修改元素!不过这也存在一个很明显的弊端,那就是,为了保证数组和链表结构中得数据统一性,这有
    109      * 增加了复杂度,降低了程序的运行效率。显然有点不合理!
    110      */
    111     public void add(int index,T data){
    112         //检测位置索引的合法性:也可抽成公共一个方法
    113         if(index<0 || index>= this.nodeCount){
    114             throw new IllegalArgumentException("位置索引值不在合理范围内!");
    115         }
    116         //创建一个新结点
    117         Node newNode=new Node(data,null,null);
    118         //找到要插入位置的结点
    119         Node tmpNode=this.findNode(index);
    120         //开始移动
    121         try{
    122             tmpNode.prev.next=newNode;
    123             newNode.prev=newNode;
    124             newNode.next=tmpNode;
    125             tmpNode.prev=newNode;
    126         }catch(Exception e){
    127             e.printStackTrace();
    128             throw e;
    129         }finally{
    130             //计数++
    131             modCount++;
    132             nodeCount++;
    133         }
    134     }
    135     /**
    136      * 从双链表中移除指定索引位置的结点
    137      * @param index 索引位置
    138      */
    139     public void remove(int index){
    140         //检测位置索引的合法性:也可抽成公共一个方法
    141         if(index<0 || index>= this.nodeCount){
    142             throw new IllegalArgumentException("位置索引值不在合理范围内!");
    143         }
    144         //找到指定位置的结点
    145         Node<T> tmpNode=this.findNode(index);
    146         //处理结点
    147         tmpNode.prev.next=tmpNode.next;
    148         tmpNode.next.prev=tmpNode.prev;
    149         //计数++
    150         modCount--;
    151         nodeCount--;
    152     }
    153     /**
    154      * 获取当前链表的尺寸大小
    155      * @return
    156      */
    157     public int size(){
    158         return this.nodeCount;
    159     }
    160     /**
    161      * 打印双链表
    162      */
    163     public void print(){
    164         if(this.nodeCount==0){
    165             System.out.println("当前双链表没有任何数据!");            
    166         }else{
    167             Node tmpNode=head;
    168             int count=0;
    169             System.out.println("**************开始打印***************************");
    170             while(tmpNode.next!=null){
    171                 tmpNode=tmpNode.next;
    172                 if(count==this.nodeCount){//过滤尾结点
    173                     break;
    174                 }
    175                 System.out.println("索引值:"+count+",结点数据:"+tmpNode.data);                
    176                 count++;
    177             }
    178             System.out.println("***************打印结束**************************");            
    179         }
    180     }
    181     /**
    182      * 找到双链表中指定位置的结点(先折半后再查找)
    183      * @param index    指定的位置索引值
    184      * @return    返回位置上的结点
    185      */
    186     public Node<T> findNode(int index){
    187         //检测位置索引的合法性
    188         if(index<0 || index>= this.nodeCount){
    189             throw new IllegalArgumentException("位置索引值不在合理范围内!");
    190         }
    191         //取链表一半的数量(计算的是索引值范围)
    192         int midCount=(this.nodeCount)/2;
    193         //计数起点
    194         int count=0;
    195         //拿到第一个结点数据
    196         Node tmp=head.next;
    197         //如果在前一半范围:[0,midCount]
    198         if(index>=0 && index<=midCount){            
    199              while(index!=count){//要找的位置!=计数位置   一直累计叠加到相等位置
    200                  tmp=tmp.next;
    201                  count++;
    202              }             
    203         }else if(index>midCount && index<this.nodeCount){//如果在后一半范围内:[midCount,this.nodeCount)
    204             //计数起点调高
    205             count=this.nodeCount-1;
    206             //从尾结点向前推
    207             tmp=bottom.prev;
    208             while(index!=count){//要找的位置!=计数位置   一直累计叠加到相等位置
    209                 tmp=tmp.prev;
    210                 count--;
    211             }    
    212         }
    213         return tmp;        
    214     }
    215     /**
    216      * 找到双链表中的指定结点所在的位置
    217      * @param node    要检索的结点对象
    218      * @return    返回位置索引值
    219      * 优化意见:
    220      *         这里的实现方法很中规中矩,可以优先判断当前检索的结点是否是头尾结点,过滤常用的检索情况。
    221      */
    222     public int findNodeIndex(Node node){
    223         int index=0;
    224         //拿到头结点
    225         Node tmp=head;
    226         while(tmp.next!=null){
    227             tmp=tmp.next;            
    228             index++;
    229         }
    230         return index;        
    231     }
    232 }

    测试运行类:Test2.java

     1 package com.xfwl.algorithmAnalysis.linklsit;
     2 /**
     3  * 双链表学习测试:
     4  * @function  
     5  * @author 小风微凉
     6  * @time  2018-5-17 下午7:06:16
     7  */
     8 public class Test2 {
     9     /**
    10      * 开始测试
    11      * @param args
    12      */
    13     public static void main(String[] args) {
    14         MyDoubleLinkedDefin<Object> myList=new MyDoubleLinkedDefin<>();
    15         //按照顺序加入结点
    16         myList.add("第1个结点");
    17         myList.add("第2个结点");
    18         myList.add("第3个结点");
    19         myList.add("第4个结点");
    20         myList.add("第5个结点");
    21         myList.print();
    22         //在第三个索引值上插入一个结点
    23         myList.add(4,"第6个结点");
    24         myList.print();
    25         //移除一个结点
    26         System.out.println("--------remove测试---------");
    27         for(int i=0;i<6;i++){
    28             myList.remove(0);
    29             myList.print();
    30         }
    31     }
    32 }

    运行结果:

    头结点:com.xfwl.algorithmAnalysis.linklsit.MyDoubleLinkedDefin$Node@6504e3b2
    尾结点:com.xfwl.algorithmAnalysis.linklsit.MyDoubleLinkedDefin$Node@515f550a
    新加结点的前驱结点:com.xfwl.algorithmAnalysis.linklsit.MyDoubleLinkedDefin$Node@6504e3b2
    新加结点的后驱结点:com.xfwl.algorithmAnalysis.linklsit.MyDoubleLinkedDefin$Node@515f550a
    新加结点的前驱结点:com.xfwl.algorithmAnalysis.linklsit.MyDoubleLinkedDefin$Node@626b2d4a
    新加结点的后驱结点:com.xfwl.algorithmAnalysis.linklsit.MyDoubleLinkedDefin$Node@515f550a
    新加结点的前驱结点:com.xfwl.algorithmAnalysis.linklsit.MyDoubleLinkedDefin$Node@5e91993f
    新加结点的后驱结点:com.xfwl.algorithmAnalysis.linklsit.MyDoubleLinkedDefin$Node@515f550a
    新加结点的前驱结点:com.xfwl.algorithmAnalysis.linklsit.MyDoubleLinkedDefin$Node@1c4af82c
    新加结点的后驱结点:com.xfwl.algorithmAnalysis.linklsit.MyDoubleLinkedDefin$Node@515f550a
    新加结点的前驱结点:com.xfwl.algorithmAnalysis.linklsit.MyDoubleLinkedDefin$Node@379619aa
    新加结点的后驱结点:com.xfwl.algorithmAnalysis.linklsit.MyDoubleLinkedDefin$Node@515f550a
    **************开始打印***************************
    索引值:0,结点数据:第1个结点
    索引值:1,结点数据:第2个结点
    索引值:2,结点数据:第3个结点
    索引值:3,结点数据:第4个结点
    索引值:4,结点数据:第5个结点
    ***************打印结束**************************
    **************开始打印***************************
    索引值:0,结点数据:第1个结点
    索引值:1,结点数据:第2个结点
    索引值:2,结点数据:第3个结点
    索引值:3,结点数据:第4个结点
    索引值:4,结点数据:第6个结点
    索引值:5,结点数据:第5个结点
    ***************打印结束**************************
    --------remove测试---------
    **************开始打印***************************
    索引值:0,结点数据:第2个结点
    索引值:1,结点数据:第3个结点
    索引值:2,结点数据:第4个结点
    索引值:3,结点数据:第6个结点
    索引值:4,结点数据:第5个结点
    ***************打印结束**************************
    **************开始打印***************************
    索引值:0,结点数据:第3个结点
    索引值:1,结点数据:第4个结点
    索引值:2,结点数据:第6个结点
    索引值:3,结点数据:第5个结点
    ***************打印结束**************************
    **************开始打印***************************
    索引值:0,结点数据:第4个结点
    索引值:1,结点数据:第6个结点
    索引值:2,结点数据:第5个结点
    ***************打印结束**************************
    **************开始打印***************************
    索引值:0,结点数据:第6个结点
    索引值:1,结点数据:第5个结点
    ***************打印结束**************************
    **************开始打印***************************
    索引值:0,结点数据:第5个结点
    ***************打印结束**************************
    当前双链表没有任何数据!

    自认为代码注释的还算仔细的哈,如有不足,不吝指正!

  • 相关阅读:
    Java中的四种内部类
    用输入/输出写一个程序,让用户输入一些姓名和电话号码
    分批读取大数据问题
    Linux产生序列数字
    两个有序链表的合并
    int和Integer的区别
    wait()和sleep()的区别
    Unix和Windows文件格式转化
    截取字符串的代码实现
    查看系统信息
  • 原文地址:https://www.cnblogs.com/newwind/p/9052762.html
Copyright © 2011-2022 走看看