鏈表-Java實現鏈表數據結構

鏈表通常由一連串節點組成,每個節點包含任意的實例數據(data fields)和一或兩個用來指向上 一個/或下一個節點的位置的鏈接(“links”)

鏈表(Linked list):是一種常見的基礎數據結構,是一種線性表,但是並不會按線性的順序存儲數 據,而是在每一個節點裏存到下一個節點的指針(Pointer)。
使用鏈表結構可以克服數組需要預先知道數據大小的缺點,鏈表結構可以充分利用計算機內存空間,實現靈活的內存動態管理。但是鏈表失去了數組隨機讀取的優點,同時鏈表由於增加了結點的指針
域,空間開銷比較大。

單向鏈表

單鏈表是鏈表中結構最簡單的。一個單鏈表的節點(Node)分爲兩個部分,第一個部分(data)保存或者顯示關於節點的信息,另一個部分存儲下一個節點的地址。最後一個節點存儲地址的部分指向空值。
單向鏈表只可向一個方向遍歷,一般查找一個節點的時候需要從第一個節點開始每次訪問下一個節 點,一直訪問到需要的位置。而插入一個節點,對於單向鏈表,我們只提供在鏈表頭插入,只需要將當 前插入的節點設置爲頭節點,next指向原頭節點即可。刪除一個節點,我們將該節點的上一個節點的 next指向該節點的下一個節點
在這裏插入圖片描述
在表頭增加節點:
在這裏插入圖片描述
在表頭刪除節點:
在這裏插入圖片描述

/**
 * @author shihaowei
 * @date 2020-06-03 16:28
 */
public class SingleLinkList {

    private int size;
    private Node head;


    public SingleLinkList() {

        this.size = 0;
        this.head = null;
    }

    /** 內部類節點 */
    public class Node {

        private Object data;
        private Node next;

        public Node(Object data) {
            this.data = data;
        }

        @Override
        public String toString() {
            return "Node{" +
                    "data=" + data +
                    ", next=" + next +
                    '}';
        }
    }

    /** 添加節點 */
    public Object addHead(Object obj){

        Node newNode = new Node(obj);
        if (size == 0){
            head = newNode;

        }else {
            newNode.next = head;
            head = newNode;
        }
        size++;
        return obj;
    }

    /** 刪除頭節點 */
    public Object delHead(){
        if (size > 0){
            Object data = head.data;
            head = head.next;
            size--;
            return data;
        }
        return false;
    }


    /** 獲取指定數據節點*/
    public Node find(Object value){

        Node current = head;
        int tempsize = size;
         while (tempsize>0){

             if (current.data.equals(value)){
                 return current;
             }else {
                 current = current.next;
                 tempsize--;
             }
         }
         return null;
    }

    /** 刪除數據節點*/
    public boolean delValue(Object value){
        if (size == 0){
            return false;
        }
        Node current = head;
        Node prenode = head;

        while (!current.data.equals(value)){

            if (current.next == null){
                return false;
            }else {
                prenode = current;
                current = current.next;
            }
        }

        //如果第一個節點就是要刪除的
        if (current == head){
            head = current.next;
            size--;
        }else {
            prenode.next = current.next;
            size--;
        }

        return true;
    }


    @Override
    public String toString() {
        return "SingleLinkList{" +
                "size=" + size +
                ", head=" + head +
                '}';
    }

    public static void main(String[] args) {

        SingleLinkList linkList = new SingleLinkList();
        linkList.addHead("a");
        linkList.addHead("b");
        linkList.addHead("c");

        //linkList.delHead();
        System.out.println(linkList);
    }


}

雙端鏈表

對於單項鍊表,我們如果想在尾部添加一個節點,那麼必須從頭部一直遍歷到尾部,找到尾節點,然後在尾節點後面插入一個節點。這樣操作很麻煩,如果我們在設計鏈表的時候多個對尾節點的引用,那麼會簡單很多。
在這裏插入圖片描述

public class DoublePointLinklist {

    private Node head;
    private Node tail;
    private int size;

    // 鏈表的節點類
    private class Node{

        private Object data;
        private Node next;

        public Node(Object data) {
            this.data = data;
        }
    }

    public DoublePointLinklist() {
        this.head = null;
        this.tail = null;
        this.size = 0;
    }

    /**
     * 頭節點插入
     * @param data
     * @return
     */
    public Object addHead(Object data){

        Node node = new Node(data);
        if (head.next == null){
            head = node;
            tail = node;
        }else {
            head.next = head;
            head = node;
        }
        size++;
        return data;

    }

    /**
     * 尾節點插入
     * @param data
     * @return
     */
    public Object addTail(Object data){

        Node node = new Node(data);
        if (head.next == null) {
            head = node;
            tail = node;
        }else {
            tail.next = tail;
            tail = node;
        }
        size++;
        return data;
    }


    /**
     * 刪除節點
     * @param data
     * @return
     */
    public boolean delNode(Object data){
        if ( size == 0){
            return false;
        }

        Node prenode = head;
        Node current = head;
        while (!head.data.equals(data)){
            if (current.next == null){
                return false;
            }else {
                prenode = current;
                current = current.next;
            }
        }

        if (current == head){
            head = current.next;
        }else {
            prenode.next = current.next;
        }

        size--;
        return true;
    }
}

雙向鏈表

我們知道單向鏈表只能從一個方向遍歷,那麼雙向鏈表它可以從兩個方向遍歷
在這裏插入圖片描述

package person.shw.datastructure.link;

/**
 * @author shihaowei
 * @date 2020-06-08 16:39
 */
public class TwoWayLinkedList {

    private Node head;
    private Node tail;
    private int size;


    /**
     * 節點類
     */
    private class Node{

        private Object object;
        private Node next;
        private Node pre;

        public Node(Object object) {
            this.object = object;
        }
    }

    public TwoWayLinkedList() {
        size = 0;
        head = null;
        tail = null;
    }


    /**
     * 鏈表頭增加節點
     * @param obj
     */
    public void addHead(Object obj){

        Node node = new Node(obj);

        if (size == 0){
            head = node;
            tail = node;
        }else {
            head.pre = node;
            head.next = head;
            head = node;
        }
        size++;
    }

    /**
     * 鏈表尾添加節點
     * @param obj
     */
    public void addTail(Object obj){

        Node node = new Node(obj);

        if (size == 0) {
            head = node;
            tail = node;
        }else {
            tail.next = node;
            node.pre = tail;
            tail = node;
        }
        size++;
    }


    /**
     * 刪除頭節點
     * @return
     */
    public Object deleteHead(){

        Node temp = head;

        if (size != 0){
            head = head.next;
            head.pre = null;
            size --;
        }

        return temp;

    }

    /**
     * 刪除尾節點
     * @return
     */
    public Object deleteTail(){

        Node temp = tail;

        if (size != 0){
            tail = tail.pre;
            size --;
        }

        return temp;
    }


    /**
     * 獲取節點個數
     * @return
     */
   public int getSize(){
        return size;
   }


    /**
     * 顯示節點信息
     */
    public void display(){
        if(size >0){
            Node node = head;
            int tempSize = size;
            if(tempSize == 1){//當前鏈表只有一個節點
                System.out.println("["+node.object+"]");
                return; }
            while(tempSize>0){
                if(node.equals(head)){
                    System.out.print("["+node.object+"->");
                }else if(node.next == null){

                    System.out.print(node.object+"]");
                }else{
                    System.out.print(node.object+"->");
                }
                node = node.next;
                tempSize--;
            }
            System.out.println(); }else{//如果鏈表一個節點都沒有,直接打印[]
            System.out.println("[]");
        }
    }

}

有序鏈表

前面的鏈表實現插入數據都是無序的,在有些應用中需要鏈表中的數據有序,這稱爲有序鏈表。
在有序鏈表中,數據是按照關鍵值有序排列的。一般在大多數需要使用有序數組的場合也可以使用有序鏈表。有序鏈表優於有序數組的地方是插入的速度(因爲元素不需要移動),另外鏈表可以擴展到全部有效的使用內存,而數組只能侷限於一個固定的大小中。

public class OrderLinkedList {
  private Node head;
  
  private class Node{
      private int data;
      private Node next;
      public Node(int data){
          this.data = data;
  } 
}
  public OrderLinkedList(){
      head = null;
}
//插入節點,並按照從小打到的順序排列 public void insert(int value){
      Node node = new Node(value);
      Node pre = null;
      Node current = head;
      while(current != null && value > current.data){
          pre = current;
          current = current.next;
      }
      if(pre == null){
          head = node;
          head.next = current;
      }else{
          pre.next = node;
          node.next = current;
      }
}
//刪除頭節點
public void deleteHead(){
      head = head.next;
  }
  public void display(){
      Node current = head;
      while(current != null){
          System.out.print(current.data+" ");
          current = current.next;
      }
      System.out.println("");
  }
}

在有序鏈表中插入和刪除某一項最多需要O(N)次比較,平均需要O(N/2)次,因爲必須沿着鏈表上一 步一步走才能找到正確的插入位置,然而可以最快速度刪除最值,因爲只需要刪除表頭即可,如果一個 應用需要頻繁的存取最小值,且不需要快速的插入,那麼有序鏈表是一個比較好的選擇方案。比如優先 級隊列可以使用有序鏈表來實現

總結

上面我們講了各種鏈表,每個鏈表都包括一個LinikedList對象和許多Node對象,LinkedList對象通常 包含頭和尾節點的引用,分別指向鏈表的第一個節點和最後一個節點。而每個節點對象通常包含數據部 分data,以及對上一個節點的引用prev和下一個節點的引用next,只有下一個節點的引用稱爲單向鏈 表,兩個都有的稱爲雙向鏈表。next值爲null則說明是鏈表的結尾,如果想找到某個節點,我們必須從 第一個節點開始遍歷,不斷通過next找到下一個節點,直到找到所需要的。棧和隊列都是ADT,可以用 數組來實現,也可以用鏈表實現

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章