链表

数组是编程语言中最常见也是很有用的数据结构,允许随机存取,相比于链表,数组能节约空间。但是他有两个缺陷:

  (1).数组的长度是固定的,改变数组的大小意味着需要创建一个新的数组,并从原数组中拷贝数据到新数组中;

  (2).数组数据在内存中一次连续存储,在实现插入删除操作会移动其他数据,使得开销变大。

  采用链式结构可以很好的克服这一局限,链式结构是存储数据和其他节点的链指针的节点集合。

1.单向链表

  一个节点只包含节点数据和指向后继节点的指针,这样的链表成为单向链表。

  一个节点的java定义:

public class IntNode{
  public int info;
  public IntNode next;
  public IntNode(int i){
    this(i,null);
  }
  public IntNode(int i,IntNode n){
    info = i;
    next = n;
  }
}

一个节点只包含两个域:info域 和 next域,前者用来存储信息,后者用来把节点连接在一起形成链表。

有了节点的定义,要创建一个新的节点只需要给他 new 一下就好:

IntNode p = new IntNode(10); //创建了一个数据域为10的节点
p.next = new IntNode(9); //创建p的后继节点,数据域为9
p.next.next = new IntNode(8); //创建第三个节点;

现在出现一个问题,如果我想创建10000个节点,那岂不是得写9999个next,这也太复杂了吧

一个简单的解决方案:在链表中同时保存两个指针: 一个指向链表的第一个节点;另一个指向链表的最后一个节点。

单向链表封装如下:

//*****************************IntSLList.java******************************
//      singly linked list class to store integers

public class IntSLList {
    private IntNode head, tail;
    public IntSLList() {
        head = tail = null;
    }
    public boolean isEmpty() {
        return head == null;
    }
    //在链表头添加节点
    public void addToHead(int e1) {
        head = new IntNode(e1,head);
        if(tail == null) {
            tail = head;
        }
    }
    //在链表尾添加节点
    public void addToTail(int e1) {
        if(!isEmpty()) {
            tail.next = new IntNode(e1);
            tail = tail.next;
        }
        else {
            head = tail = new IntNode(e1);
        }
    }
    //删除链表头节点并返回数据
    public int deleteFromHead() {
        int e1 = head.info;
        if(head == tail) {
            head = tail = null;
        }else {
            head = head.next;
        }
        return e1;
        
    }
    //从链表尾删除节点并返回数据
    public int deleteFromTail() {
        int e1 = tail.info;
        if(head == tail) {
            head = tail = null;
        }else {
            IntNode temp;
            for(temp = head;temp.next != tail;temp = temp.next) {
                tail = temp;
                tail.next = null;
            }
        }
        return e1;
    }
    
    //打印链表节点
    public void printAll() {
        for(IntNode t = head; t!=null;t = t.next) {
            System.out.println(t.info + " ");
        }
    }
    //查找节点
    public boolean isInList(int e1) {
        IntNode t;
        for(t = head; t != null && t.info != e1; t = t.next) {
            
        }
        return t != null;
    }
    //删除节点,该节点在链表任何位置
    public void delete(int e1) {
        if(!isEmpty()) {
            if(head == tail && e1 == head.info) {
                head = tail = null;
            }else if(e1 == head.info){
                head = head.next;
            }else {
                IntNode pred,tmp;
                for(pred = head, tmp = head.next; tmp != null && tmp.info != e1; pred = pred.next,tmp = tmp.next) {
                    
                }
                if(tmp != null) {
                    pred.next = tmp.next;
                    if(tmp == tail) {
                        tail = pred;
                    }
                }
            }
        }
    }
    
}

上面的封装给出了整型链表的一般操作,包括插入,删除,查找,判断非空等,示例存储的数据类型是整型的,推广的一般的链表可以直接把info域声明为Object。

单项链表的一个主要缺陷:由于节点只包含指向后继节点的指针,不能访问一个节点的前驱,使得在删除队尾数据时不得不遍历整个链表,对于一个包含很多数据的长链表来说,无疑会降低链表的操作速度。

 2.双向链表

  双向链表每个节点包含两个指针域,一个指向前驱节点,另一个指向后继节点。双向链表对于删除链表尾节点是十分方便的

下面给出简单的整型双向链表的封装:

//+++++++++++++++++++++++++++IntDLLNode.java++++++++++++++++++++++++++++++

 public class IntDLLNode{
    public int info;
    public IntDLLNode next, prev;
    public IntDLLNode(int e1) {
        this(e1,null,null);
    }
    public IntDLLNode(int e1,IntDLLNode n,IntDLLNode p) {
        info = e1;
        next = n;
        prev = p;
    }
}
 
//+++++++++++++++++++++++++++IntDLList.java++++++++++++++++++++++++++++++
public class IntDLList {
    private IntDLLNode head, tail;
    
    public IntDLList() {
        head = tail = null;
    }
    //判断非空与否?
    public boolean isEmpty() {
        return head != null;
    }
    //在表尾添加节点
    public void addToTail(int e1) {
        if(!isEmpty()) {
            tail = new IntDLLNode(e1,null,tail);
            tail.prev.next = tail;
        }else {
            head = tail = new IntDLLNode(e1);
        }
    }
    //删除表尾的节点并返回该节点值
    public int removeFromTail() {
        int e1 = tail.info;
        if(head == tail) {
            head = tail = null; //表中只有一个节点
        }else {
            tail = tail.prev;
            tail.next = null;
        }
        return e1;
    }
}

3.循环链表

将节点连接成环形成循环链表:表的节点必须是有限的,而且每个节点都必须有一个后继。

  3.1循环单向链表

  3.2循环双向链表

原文地址:https://www.cnblogs.com/lc1475373861/p/10687974.html