原來你是這樣的數據結構之鏈表結構

順序表結構的存儲方式非常容易理解,操作也十分方便,但是順序結構有如下缺點:

1.在插入或刪除時,往往需要移動大量數據
2.如果表比較大,有時難以分配比較足夠連續的存儲空間,往往導致內存分配失敗.

為了克服上述順序表的缺點,可以采用鏈表結構,鏈表結構是一種動態存儲分配的結構形式,可以根據需要動態申請所需的內存單元.

什么是鏈表結構

典型的鏈表結構,鏈表中每一個結點都應包括兩部分

  • 數據部分,保存當前結點的實際數據
  • 地址部分,保存的是下一個結點的引用

鏈表結構就是由許多這種結點構成的,在進行鏈表操作時,首先需要定義一個"頭引用"變量,一般以(head)表示,該引用變量指向鏈表結構的第一個結點,第一個結點的地址部分又之向第二個結點,第二個結點的地址部分又指向第三個結點,一直到最后一個結點,這時最后一個結點的地址部分為空,稱為"表尾",一般在表層的地址部分放一個空地址null,鏈表到此結束,如下圖


image

由于采用了引用來指示下一個數據的地址,因此在鏈表結構中,邏輯上相鄰的結點在內存中并不一定相鄰,邏輯相鄰關系通過地址部分的引用變量來實現.

鏈表結構帶來的最大好處就是結點之間不要求連續存放,因此在保存大量數據時,不需要分配一塊連續的存儲空間,用戶可以用new函數來動態分配結點的存儲空間,當刪除某個結點時,給該結點賦值null,釋放其占用的內存空間.

當然鏈表結構也有缺點,就是浪費存儲空間,因此,對于每個結點數據,都要額外保存一個引用變量,但是,在某些場合,鏈表結構所帶來的好處還是大于其缺點的.

對于鏈表結構的訪問只能從表頭按個查找,即從head的頭結點,到第二個結點,然后第三個結點,直到找到合適的結點.

鏈式存儲是最常用的存儲方式之一,它不僅可以用來表示線性表,而且還可以用來表示各種非線性的數據結構,鏈表結構有如下幾種

  • 單鏈表:同上面的鏈式結構一樣,每個結點中只包含一個引用.
  • 雙向鏈表:若每個結點包含兩個引用,一個是頭引用,一個是尾引用,一個指向上一個結點,一個指向下一個結點.
  • 單循環鏈表:在單鏈表中,將終端結點的飲用域null改為指向表頭結點或開始結點即可構成單循環鏈表.
  • 多重鏈的循環鏈表:如果將表中結點鏈在多個環上,將構成多重鏈的循環語法.
    接下來,將分析如何在java語言中建立鏈表,并完成鏈表結構的基本運算.

準備數據

下面我們鏈表結構的程序設計,首先需要準備數據,也就是準備在鏈表操作中需要用到的變量及類等

class DATA2                              //結點數據
{
    String key;
    String name;
    int age;
}
class CLType
{
    DATA2 nodeData = new DATA2();      //保存當前結點的數據
    CLType nextNode;
}

上面的代碼定義了結點的數據DATA2,以及鏈表結構的類CLType,結點的具體數據保存在DATA2中,下一個結點的引用保存在nextNode中

追加結點

追加結點即在鏈表末尾增加一個結點,表尾結點的部分保存原來是null,此時需要將其設置為新增結點的地址(即原表尾結點指向新增結點),然后將新增結點的地址部分設置為null,即新增結點成為表尾.

由于一般情況下,鏈表只有一個頭引用head,要在末尾增加結點就需要從頭引用head 開始按個檢查,直到找到最后一個結點.

典型的追加結點步驟如下:

  • 追加一個結點
  • 1.首先分配內存空間,保存新增的結點
  • 2.從頭引用開始檢查,直到找到最后一個結點,
  • 3.將結尾結點的內存地址設為新的結點
  • 4.將新的結點的地址部分設置為空地址,null,即新結點成為表尾.
public CLType CLAddEnd(CLType head,DATA2 nodeData){
                                        //1.分配內存,保存新增的結點數據
        CLType node,htemp;
        if((node=new CLType())==null){
            System.out.println("申請內存失敗! \n");
            return null;
        }else{
            
            node.nextData = nodeData;   //2.保存數據
            node.nextNode = null;       //3.設置下一個結點的索引為null,因為追加的這個是鏈尾
            if(head ==null){            //4.判斷鏈頭是否為空,如果為空,則直接賦值并返回
                head = node;
                return head;
            }
            htemp = head;
            while(htemp.nextNode !=null){//5.循環判斷是否是鏈尾,如果不是鏈尾則繼續判斷
                htemp = htemp.nextNode;
            }
            htemp.nextNode = node;       //6.判斷完成后這個肯定是鏈尾了,直接賦值
            return head;
        }
    }

上述代碼中,輸入參數head為鏈表頭引用,輸入參數nodeData為結點保存的數據,程序中,使用new關鍵字申請保存結點數據的內存空間,如果分配內存成功,node中將保存指向內存區域的引用,然后,將傳入的nodeData保存到申請的內存區域,并設置該結點指向下一個結點的引用值為null.

插入頭結點

插入頭結點也就是在鏈表首部添加結點的過程,有如下幾個步驟:

  • 分配內存空間,保存新增的結點.
  • 使新增結點指向頭引用head所指向的結點
  • 使頭引用head指向新增結點.
public CLType CLAddFirst(CLType head,DATA2 nodeData){
        CLType node;
        if((node =new CLType())==null){
            System.out.println("申請內存失敗! \n");
            return null;
        }else{
            node.nextData = nodeData;
            node.nextNode = head;
            head = node;
            return head;
        }
    }

查找結點

查找結點就是在鏈表結構中查找需要的元素.

public CLType CLFindNode(CLType head,String findkey){
        CLType htemp;
        htemp = head;
        while(htemp.nextNode!=null){
            if(htemp.nextData.key.compareTo(findkey)==0){
                return htemp; 
            }
            htemp = htemp.nextNode;
        }
        return null;
    }

插入結點

插入結點就是在鏈表中間部分的指定位置增加一個結點,插入結點的過程如下:

  • 分配內存空間,保存新增的結點
  • 找到需要插入的邏輯位置,也就是位于哪兩個結點之間
  • 修改插入結點位置的引用,將新增結點的引用指向找到插入結點位置的飲用
  • 將新增結點的位置引用指向插入結點原來的地址
public CLType CLInsertNode(CLType head,String findkey,DATA2 nodeData){
        CLType node,nodetemp;
        if((node=new CLType())==null){
            System.out.println("申請內存失敗! \n");
            return null;
        }
        
        node.nextData = nodeData;
        nodetemp = head.CLFindNode(head, findkey);
        if(nodetemp!=null){
            node.nextNode = nodetemp.nextNode;
            nodetemp.nextNode = node;
        }else{
            System.out.println("插入失敗 \n");
        }
        return head;
    }

刪除結點

刪除結點就是刪除鏈表結構中的結點引用,步驟如下:

  • 查找需要刪除的結點
  • 使前一結點指向當前結點的下一個結點
  • 刪除結點
public int CLDeleteNode(CLType head,String key){
        CLType node,htemp;                           //保存上一個結點和當前結點
        htemp = head;                                //保存當前結點
        node = head;
        while(htemp!=null){
            if(htemp.nextData.key.compareTo(key)==0){
                node.nextNode = htemp.nextNode;
                htemp = null;
                return 1;
            }else{
                node = htemp;
                htemp = htemp.nextNode;
            }
        }
        return 0;
    }

計算鏈表長度

計算鏈表長度即統計鏈表結構中結點的數量,在順序表中比較方便,在鏈表結構中需要遍歷整個鏈表結構來計算長度.

public int CLLength(CLType head){
        int length = 0;
        CLType htemp = head;
        while(htemp!=null){
            length++;
            htemp = htemp.nextNode;
        }
        return length;
    }

完整實現代碼如下:

package LinkedList;
/**
 * 數據結點類型
 * 定義鏈表結構
 * @author feiyu
 *
 */
public class CLType {
    DATA2 nextData  = new DATA2();     //當前結點的數據類型
    CLType nextNode;                   //儲存下一個結點的位置
    /**
     * 追加一個結點
     * 1.首先分配內存空間,保存新增的結點
     * 2.從頭引用開始檢查,直到找到最后一個結點,
     * 3.將結尾結點的內存地址設為新的結點
     * 4.將新的結點的地址部分設置為空地址,null,即新結點成為表尾.
     * @param head 頭結點
     * @param nodeData 結點數據
     * @return 返回頭結點
     */
    public CLType CLAddEnd(CLType head,DATA2 nodeData){
                                        //1.分配內存,保存新增的結點數據
        CLType node,htemp;
        if((node=new CLType())==null){
            System.out.println("申請內存失敗! \n");
            return null;
        }else{
            
            node.nextData = nodeData;   //2.保存數據
            node.nextNode = null;       //3.設置下一個結點的索引為null,因為追加的這個是鏈尾
            if(head ==null){            //4.判斷鏈頭是否為空,如果為空,則直接賦值并返回
                head = node;
                return head;
            }
            htemp = head;
            while(htemp.nextNode !=null){//5.循環判斷是否是鏈尾,如果不是鏈尾則繼續判斷
                htemp = htemp.nextNode;
            }
            htemp.nextNode = node;       //6.判斷完成后這個肯定是鏈尾了,直接賦值
            return head;
        }
    }
    /**
     * 插入頭結點
     * 1.分配內存空間,保存新增的結點
     * 2.將新增結點指向頭引用的head所指向的結點
     * 3.使頭引用指向新增的結點 ,有點繞,就是交換了一下位置,讓head原來的頭結點指向新的頭結點
     * @param head
     * @param nodeData
     * @return
     */
    public CLType CLAddFirst(CLType head,DATA2 nodeData){
        CLType node;
        if((node =new CLType())==null){
            System.out.println("申請內存失敗! \n");
            return null;
        }else{
            node.nextData = nodeData;
            node.nextNode = head;
            head = node;
            return head;
        }
    }
    /**
     * 查找結點
     * @param head 頭結點
     * @param findkey 
     * @return
     */
    public CLType CLFindNode(CLType head,String findkey){
        CLType htemp;
        htemp = head;
        while(htemp.nextNode!=null){
            if(htemp.nextData.key.compareTo(findkey)==0){
                return htemp; 
            }
            htemp = htemp.nextNode;
        }
        return null;
    }
    
    /**
     * 插入結點
     * 1.分配內存空間,保存新增的結點
     * 2.查找關鍵字,找到需要插入的結點位置并返回
     * 3.把找到的結點位置地址保存到新的結點地址位置
     * 4.把找到的結點位置指向新的結點
     * @param head
     * @param findkey
     * @param nodeData
     * @return
     */
    public CLType CLInsertNode(CLType head,String findkey,DATA2 nodeData){
        CLType node,nodetemp;
        if((node=new CLType())==null){
            System.out.println("申請內存失敗! \n");
            return null;
        }
        
        node.nextData = nodeData;
        nodetemp = head.CLFindNode(head, findkey);
        if(nodetemp!=null){
            node.nextNode = nodetemp.nextNode;
            nodetemp.nextNode = node;
        }else{
            System.out.println("插入失敗 \n");
        }
        return head;
    }
    /**
     * 刪除結點
     * 1.找到要刪除的結點位置
     * 2.把前一個結點指向當前結點的后一個結點
     * 3.刪除結點
     * @param head
     * @param key
     * @return
     */
    public int CLDeleteNode(CLType head,String key){
        CLType node,htemp;                           //保存上一個結點和當前結點
        htemp = head;                                //保存當前結點
        node = head;
        while(htemp!=null){
            if(htemp.nextData.key.compareTo(key)==0){
                node.nextNode = htemp.nextNode;
                htemp = null;
                return 1;
            }else{
                node = htemp;
                htemp = htemp.nextNode;
            }
        }
        return 0;
    }
    /**
     * 獲取鏈表的長度
     * 1.從遍歷到尾,然后進行累加
     * @return
     */
    public int CLLength(CLType head){
        int length = 0;
        CLType htemp = head;
        while(htemp!=null){
            length++;
            htemp = htemp.nextNode;
        }
        return length;
    }
    /**
     * 顯示所有結點
     * @param head
     */
    public void CLAllNode(CLType head){
        CLType htemp;
        htemp = head;
        DATA2 nodeData;
        while(htemp!=null){
            nodeData = htemp.nextData;
            System.out.printf("結點(%s,%s,%d) \n",nodeData.key,nodeData.name,nodeData.age);
            htemp = htemp.nextNode;
        }
    }
}

上面就是鏈表結構的java代碼實現,當然只是單鏈表,還有雙鏈表,單循環鏈表,雙循環鏈表,這些我們會之后一一添加!
原來你是這樣的數據結構之棧結構

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容