zoukankan      html  css  js  c++  java
  • Java 链表

        按链表的组织形式分有ArrayList和LinkList两种。ArrayList内部其实是用数组的形式实现链表,比较适合链表大小确定或较少对链表进行增删操作的情况,同时对每个链表节点的访问时间都是constant;而LinkList内部以一个List实现链表,比较适合需要频繁对链表进行操作的情况,对链表节点的访问时间与链表长度有关O(N)。
        另外,根据实现形式可以分为直接式(想不出什么合适的名字,姑且这样吧)和使用Iterator(迭代模式)两种方法。直接式的实现方法和C/C++中的写法差不多;而使用Iterator时,需要实现java.lan中的Iterable接口(或者也可以自己在链表内部定义自己的Iterator method)

     使用迭代模式的优点:
               1,实现功能分离,简化容器接口。让容器只实现本身的基本功能,把迭代功能委让给外部类实现,符合类的设计原则。
               2,隐藏容器的实现细节。
               3,为容器或其子容器提供了一个统一接口,一方面方便调用;另一方面使得调用者不必关注迭代器的实现细节。
               4,可以为容器或其子容器实现不同的迭代方法或多个迭代方法。
        我觉得第4点说的很好,对于一堆数据而言,不同的人(或业务逻辑)使用它的方式也不尽相同,定义一个theIterator继承Iterator,不仅提供next,hasNext 以及remove这个最小的操作集合,同时也可以提供更多的其它方法。在theIterator的实现类中,具体实现不同的迭代方法,比如顺序、逆序或根据其它语义进行遍历等,再通过类厂的方式将一个theIterator实现的对象交给用户使用。
    下面我给出两个例子:
        首先是直接式实现链表的例子,这个例子只提供了几种链表操作的基本方法,仅用于示意:
    [java] view plain copy
     
     print?在CODE上查看代码片派生到我的代码片
    1. public class MyList<AnyType>  {  
    2.   
    3. private class Node<AnyType>{  
    4.   
    5. public  Node<AnyType> pre;  
    6.   
    7. public  Node<AnyType> next;  
    8.   
    9. public  AnyType      data;  
    10.   
    11. public Node(AnyType d, Node<AnyType>p, Node<AnyType> n){}  
    12.   
    13. public Node(){}  
    14.   
    15. }  
    16.   
    17. private int theSize;  
    18.   
    19. private Node<AnyType> Header;  
    20.   
    21. private Node<AnyType> Tail;  
    22.   
    23. public MyList(){}  
    24.   
    25. public void add(AnyType item){}  
    26.   
    27. public boolean isEmpty(){}  
    28.   
    29. public int size(){}  
    30.   
    31. public AnyType get( int idx){}  
    32.   
    33. public void print(){}  
    34.   
    35. }  

    Node<AnyType>类定义了双向链表中节点的结构,它是一个私有类,而其属性和构造函数都是公有的,这样,其父类可以直接访问其属性,而外部类根本不知道Node类的存在。Data是节点中的数据与,pre指向前一个Node节点,next指向后一个Node节点。其构造函数的实现如下,不解释:

     


     

    1. public Node(AnyType d, Node<AnyType>p, Node<AnyType> n){  
    2.   
    3. this.data = d;  
    4.   
    5. this.pre = p;  
    6.   
    7. this.next = n;  
    8.   
    9. }  
    10.   
    11. public Node(){  
    12.   
    13. this.data = null;  
    14.   
    15. this.pre = null;  
    16.   
    17. this.next = null;  
    18.   
    19. }  



     

     

    下面我们看一下链表的构造函数实现:

     

    [java] view plain copy
     
     print?在CODE上查看代码片派生到我的代码片
    1. public MyList(){  
    2.   
    3. theSize = 0;  
    4.   
    5. Header = new Node<AnyType>(null,null,null);  
    6.   
    7. Tail   =  new Node<AnyType>(null,Header,null);  
    8.   
    9. Header.next = Tail;  
    10.   
    11. }  



     

    我们构造了一个带有头、尾节点的双向链表,头节点的Next指向尾节点,为节点的pre指向头节点。链表长度起始为0。

    继续贴上链表类其它方法的实现,不解释了,应该比较清楚:

     

     

    [java] view plain copy
     
     print?在CODE上查看代码片派生到我的代码片
    1. public void add(AnyType item){  
    2.   
    3.      Node<AnyType> aNode = new Node<AnyType>(item,null,null);  
    4.   
    5.      Tail.pre.next = aNode;  
    6.   
    7.      aNode.pre = Tail.pre;  
    8.   
    9.      aNode.next = Tail;  
    10.      
    11.      Tail.pre = aNode;  
    12.   
    13.      theSize++;  
    14.   
    15. }  
    16.   
    17. public boolean isEmpty(){  
    18.   
    19.      return ( theSize == 0);  
    20.   
    21. }  
    22.   
    23. public int size(){  
    24.   
    25.      return theSize;  
    26.   
    27. }  
    28.   
    29. public AnyType get( int idx){  
    30.   
    31.      if(idx > theSize-1 || idx < 0)  
    32.   
    33.            throw new IndexOutOfBoundsException();  
    34.   
    35.    
    36.   
    37.      Node<AnyType> current = new Node<AnyType>(null,Header,null);  
    38.   
    39.    
    40.   
    41.     for(int i = 0; i<idx; i++)  
    42.   
    43.        current = current.next;  
    44.   
    45.    return current.data;  
    46.   
    47. }  
    48.   
    49. public void print(){  
    50.   
    51.     Node<AnyType> current = Header.next;  
    52.   
    53. while(current.next != null){  
    54.    //如果AnyType是你自己定义 //的数据类型,那么请务必提供  
    55.    //一个toString方法,要么就不  
    56.    //要在链表里实现print方法。  
    57.   System.out.println(current.data.toString());   
    58.     
    59.   current = current.next;  
    60.   
    61. }  
    62.   
    63. }  



     

    第二个例子是用迭代方法实现链表的例子,下面是类定义:

     

    [java] view plain copy
     
     print?在CODE上查看代码片派生到我的代码片
    1. public class MyListItr<Type> implements Iterable<Type> {  
    2.   
    3. private class Node<Type>{  
    4.   
    5. public Type data;  
    6.   
    7. public Node<Type> pre;  
    8.   
    9. public Node<Type> next;  
    10.   
    11. public Node(){}  
    12.   
    13. public Node(Type d, Node<Type> p, Node<Type> n){}  
    14.   
    15. }  
    16.   
    17.    
    18.   
    19.    
    20.   
    21. private Node<Type> Header;  
    22.   
    23. private Node<Type> Tail;  
    24.   
    25. private int theSize;  
    26.   
    27.    
    28.   
    29. public MyListItr(){}  
    30.   
    31.    
    32.   
    33.    
    34.   
    35. public void add(Type item){}  
    36.   
    37.    
    38.   
    39. public void print(){}  
    40.   
    41.    
    42.   
    43. public int size(){}  
    44.   
    45. @Override  
    46.   
    47. public Iterator<Type> iterator() {}  
    48.   
    49.    
    50.   
    51. private class MyListIterator implements Iterator<Type>{  
    52.   
    53. @Override  
    54.   
    55. public boolean hasNext() {}  
    56.   
    57. @Override  
    58.   
    59. public Type next() {}  
    60.   
    61. @Override  
    62.   
    63. public void remove() {}  
    64.   
    65. }  
    66.   
    67. }  



     

      这里主要说一下与前面例子不同的地方:MyListItr类实现了Java.lan.Itrable接口,因此我们必须实现其public Iterator<Type> iterator() {}方法,它的返回值是一个迭代器对象,那么我就定义一个内部私有类——MyListIterator,这个类实现了iterator接口。在public Iterator<Type> iterator() 方法中,我就构造这么一个MyListIterator的对象并返回。

        Iterator接口有三个方法是我们必须实现的,hasNext,next和remove,这是迭代操作的最小集合了。对于不同的迭代器执行方式,我们可以定义多个类似MyListIterator这样的实现类,只要在链表类中的iterator() 方法中把具体实现类的对象返回给用户就OK了。

       罗嗦两句:我感觉如果我们定义链表类时,如果不继承Iterable接口,而是直接在类内部提供 public theIterator Iterator() 方法,theIterator是一个自定义接口,继承自Iterator接口,这样我们可以提供遵循theIterator接口的各种迭代方式的不同实现类,并通过一个类厂方法在链表的public theIterator Iterator() 方法中把具体迭代实现类的对象交给用户,以此就可以根据不同的需求,提供链表的多种迭代方式。我觉得这种方法是可行的,但并没有具体实验。

       下面只贴出链表iterator()方法和迭代实现类的MyListIterator代码,其它方法就不重复贴了:

     

    [java] view plain copy
     
     print?在CODE上查看代码片派生到我的代码片
      1. @Override  
      2.   
      3. public Iterator<Type> iterator() {  
      4.   
      5. return new MyListIterator();  
      6.   
      7. }  
      8.   
      9. private class MyListIterator implements Iterator<Type>{  
      10.   
      11. Node<Type> current = Header.next;  
      12.   
      13. @Override  
      14.   
      15. public boolean hasNext() {  
      16.   
      17. return (current != Tail);  
      18.   
      19. }  
      20.   
      21. @Override  
      22.   
      23. public Type next() {  
      24.   
      25.   if(!hasNext())  
      26.   
      27.     throw new IndexOutOfBoundsException();    
      28.   
      29.   Type item = current.data;  
      30.   
      31.   current = current.next;  
      32.   
      33.   return item;  
      34. }  
      35.   
      36. @Override  
      37.   
      38. public void remove() {  
      39.   
      40.   if( !hasNext())  
      41.   
      42.     throw new NoSuchElementException();  
      43.   
      44.   current.pre.next = current.next;  
      45.   current.next.pre = current.pre;  
      46.   current = current.next;  
      47.   
      48.   theSize--;  
      49. }  
      50.   
      51. }  
  • 相关阅读:
    代码管理模型概况
    循环链表
    队列

    链表
    java 2020-10-12T11:22:49.000+0800 字符串转换成正常时间格式
    动态数组
    mysql练习
    复杂度与LeetCode
    记一次带逗号的数字类型处理
  • 原文地址:https://www.cnblogs.com/Smile-123/p/5549944.html
Copyright © 2011-2022 走看看