鏈表 ----單鏈表?

鏈表是一種遞歸類型的數據結構,也是比較簡單的數據結構。 

今天實現一個單鏈表結構,主要實現其 從鏈表頭添加節點 / 尾部添加 /任意點添加 /頭部刪除節點 /尾部刪除節點 / 任意點刪除節點。

  假設我們節點的類如下。

package com.fd.javabasic;

public class Node {
    public Node next;
    private String val;
    public Node(String val){
        this.val =val;
    }
    public String getVal(){
        return this.val;
    }
}

  約定 next 的指針爲null 表示當前的節點是最後一個節點,往後是沒有數據的。

 private Node head;
    private int size;

    public SingleLink(){
        this.size =0;
        this.head = null;
    }
    
  ...
    //
    public void printNode(){
        Node temp = this.head;
        do{
            System.out.println(temp.getVal());
            temp = temp.next;
        }while(this.head.next!=null);
    }

    public int size(){
        return this.size;
    }
}

一 : 頭部添加

   思路:鏈表其實就是移動指針,所以需要一個臨時節點(oldHead)保持當前的頭部對象,使新加入的節點的next對象執行oldHead。

 /**
     * 從頭部插入
     * @param node
     * @return
     */
    public void addHead(Node node){
        if(node == null){
            new Exception("node is not null point");
        }
        if(this.head == null ){
            this.head = node;
            this.head.next = null;
        }else{
            Node oldHead = this.head;
            this.head = node;
            this.head.next = oldHead;
        }
        this.size++;
    }

測試:發現和預期的結果一樣。

  SingleLink singleLink = new SingleLink();
        //**倒序打印
        Node node = new Node("0");
        singleLink.addHead(node);
        singleLink.addHead(new Node("1"));
        singleLink.addHead(new Node("2"));
        singleLink.printNode();


   //result
    2
    1
    0

  二:頭部刪除

  如果存在節點即刪除。不存在返回null值。

 /***
     * 從頭刪除一個節點
     * @return
     */
    public Node deleteHead(){
        if(this.size== 0 ){
            return  null;
        }
        Node deleteNode = this.head;
        this.head = this.head.next;
        this.size--;
        return deleteNode;
    }

測試:

 測試預期一樣,最後一個因爲不存在節點,所以報錯。

 Node deleteNode = singleLink.deleteHead();
        System.out.println("delete node :"+ deleteNode.getVal());
        deleteNode = singleLink.deleteHead();
        System.out.println("delete node :"+ deleteNode.getVal());
         deleteNode = singleLink.deleteHead();
        System.out.println("delete node :"+ deleteNode.getVal());
        deleteNode = singleLink.deleteHead();
        System.out.println("delete node :"+ deleteNode.getVal());


//result
delete node :2
delete node :1
delete node :0
Exception in thread "main" java.lang.NullPointerException
	at com.fd.javabasic.SingleLinkTest.main(SingleLinkTest.java:21)

三、尾部添加節點

  對於單鏈表來說,在尾部添加節點,就需要進行遍歷鏈表找到尾節點。所以鏈表複雜度O(n)。

   首先報存一個節點,通過遍歷節點,不斷的改變temp 節點的指針值,找到尾節點,遍歷勢必會消耗性能。有沒有替代的方案呢。

/**
     * 從鏈表尾部插入數據
     * @param node
     */
    public void addTail(Node node){
        if(node == null){
            new Exception("node is not null point");
        }
        if(this.head == null){
            this.head = node;
        }else{
            Node temp = this.head;
            while(temp.next!= null){
                temp = temp.next;
            }
            temp.next=node;
        }

        this.size++;
    }

 方法二: 我們首先須保存尾節點指針。

   這個方法中,我添加了tail 節點用於表示尾節點。這樣從尾部添加只需要改變tail節點的指針就可以了,顯然此方法比只使用一個head 節點,性能要好很多。付出的代價只是多保存了一個tail 節點。

  public void addTail2(Node node){
        if(node == null){
            new Exception("node is not null point");
        }
        if(this.head ==  null ){
            this.head = node;
            this.tail = node;
        }else{
            Node temp =this.tail;
            temp.next=node;
            this.tail = node;
        }
        this.size ++;
    }

測試:

   Node node = new Node("0");
        singleLink.addHead(node);
        singleLink.addHead(new Node("1"));
        singleLink.addTail2(new Node("2"));
        singleLink.addTail2(new Node("3"));
        singleLink.addTail2(new Node("4"));
        singleLink.printNode();


//result
1
0
2
3
4

四: 從尾部刪除一個節點

  從尾部刪除一個節點,發現我做遍歷,找到最後一個元素的倒數第二個元素,進行刪除,很顯然這個方法的性能很差。

  有沒有替代的辦法呢?

 public Node deleteTail(){
        if(size == 0){
            return null;
        }
        Node temp = this.head;
        for(int i=1;i<this.size-1;i++){
            temp = this.head.next;
        }
        Node tail = temp.next;
        temp.next = null;
        this.size --;
        return tail;
    }

如果有的話,應該需要改變node 的數據結構了,需要添加一個prev 指向前一個的指針。這樣我們就可以實現了,目前在這裏不打算這樣做。

從上面的可以看出單鏈表在插入節點和刪除節點時性能非常高,時間複雜度都是O(1) ,但是任意位置插入和刪除的時候,就需要進行遍歷了,這裏涉及到了查找。

下面我給出任意節點查找和刪除的例子

/**
     * 通過索引插入數據
     * @param node
     * @param index
     * @return
     */
    public void addIndex(Node node,int index){
       if(node  == null || index <0 || index>=this.size){
           new Exception("node is not null or index is <0 >=size");
       }
       if(index == 0){
           addHead(node);
           return;
       }
       if(index == this.size-1){
           addTail(node);
           return;
       }
       int tempIndex=0;

       //查找前一個
       Node temp =this.head;
       for(int i=0;i<index-1;i++){
           temp = this.head.next;
       }

       Node insertNodeAfterTemp = temp.next;
       temp.next = node;
       node.next = insertNodeAfterTemp;
       this.size ++;
    }

測試:

 SingleLink singleLink = new SingleLink();
        //**倒序打印
        Node node = new Node("0");
        singleLink.addHead(node);
        singleLink.addHead(new Node("1"));
        singleLink.addTail2(new Node("2"));
        singleLink.addTail2(new Node("3"));
        singleLink.addTail2(new Node("4"));
        // 5 應該排在1的後面
        singleLink.addIndex(new Node("5"),1);
        singleLink.printNode();


//result
1
0
5
2
3
4

刪除任意節點:

/**
     * 刪除任意一個節點
     */
    public Node deleteTail(int index){
        if(index < 0 || index > size-1){
            new Exception("index > size or index < 0");
        }
        if (index == 0) {return deleteHead();}
        if(index == size -1) {return deleteTail();}
        //查找前一個
        Node temp =this.head;
        for(int i=0;i<index-1;i++){
            temp = this.head.next;
        }

        Node deleteNode = temp.next;
        Node deleteAfterNode = deleteNode.next;
        temp.next = deleteAfterNode;
        this.size --;
        return deleteNode;
    }

測試:

 //把添加的5 刪除
        Node deleteNode = singleLink.deleteTail(1);
        singleLink.printNode();

//result

1
0
2
3
4

總結:

     單鏈表優點: 1、存儲的空間可以連續或者不連續,形成鏈表只需要next 指向下一個對象的引用即可。

                             2、長度大小初始可以不受限制,大小不受限制(不是絕對的,受物理內存,虛擬機分配限制)

                             3、在頭部和尾部插入元素或者刪除元素時間複雜度O(1),性能比較好。可以作爲棧,或者隊列的結構。

    單鏈表缺點: 查找元素時間複雜度O(n),不是進行用於查找的數據結構。

                             

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