zoukankan      html  css  js  c++  java
  • 链表

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

      (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循环双向链表

  • 相关阅读:
    English,The Da Vinci Code, Chapter 23
    python,meatobject
    English,The Da Vinci Code, Chapter 22
    English,The Da Vinci Code, Chapter 21
    English,The Da Vinci Code, Chapter 20
    English,The Da Vinci Code, Chapter 19
    python,xml,ELement Tree
    English,The Da Vinci Code, Chapter 18
    English,The Da Vinci Code, Chapter 17
    English,The Da Vinci Code, Chapter 16
  • 原文地址:https://www.cnblogs.com/lc1475373861/p/10687974.html
Copyright © 2011-2022 走看看