數據結構:靜態鏈表

什麼叫靜態鏈表?

用數組描述的鏈表叫作靜態鏈表,這種描述方法叫作遊標實現法。數組

如何用數組了描述呢 ?
簡單的說,就是咱們會先建立一個固定的數組,而後數組中的每個元素都使用一個結構體,該結構體包括兩個元素,一個是要儲存的數據,一個是遊標。spa

什麼是遊標?
遊標是用來記錄鏈表的下一個結點的位置(節點所在數組中的下標)。code

這都是一些比較抽象的概念,不太好理解,也很容易搞亂,下面我結合這些圖簡單的說一些。blog

個人理解

靜態鏈表

你們能夠先創建幾個概念,數組和鏈表可不要搞混了。
首先這個數組,其實就是咱們用來儲存用的。那鏈表呢, 我我的的理解,指得是數組已經使用的這一部分(剩餘沒使用的那部分暫且叫作備用鏈表)。咱們使用這個數組下標爲0的這個位置來當作鏈表的頭結點,因此說這個位置不進行實際的儲存。索引

咱們在進行存儲的時候,是按照數組下標的位置把數據逐一存儲進數組的(數組的第0個位置不作實際的存儲功能,下面會說到),也就是說咱們要把‘甲’,‘乙’,‘丙’,‘丁’,‘戊’,‘己’,‘庚’ 這七個字儲存起來,使用的是數組的下標1到6的連續位置,可是在數組中的連續位置,不表明他在鏈表中的位置,打個比方說,咱們要繼續存儲一個‘辛’這個字,可是我要把它存到甲和乙中間,他的數組的位置就是在下標爲7的位置,可是他的鏈表位置是在甲和乙中間,這是經過遊標來體現出來的,下面會說到rem

好的,咱們分析一下
在初始化完這個數組以後,全部的位置上都沒有儲存數據,而後每一個位置的遊標表示數組的下一個元素,也就是第0個元素的遊標爲1,第1個元素的遊標爲2....
進行了存儲以後,如上圖,咱們看幾個比較關鍵的位置,第0個元素的遊標變味了7,第6個元素的遊標爲0,第999個元素的遊標爲1,這表明什麼意思呢?
咱們在進行插入數據操做的時候,第0個元素的遊標老是指向下次儲存要用的那個位置,數組的最後一個元素也就是999這個位置的元素,他的遊標指向鏈表的第一個節點(不包括頭結點的)在數組中的下標。而後鏈表的最後一個節點的遊標要指向0,由於沒有下一個了嘛,因此咱們看到第6個元素的位置的遊標是0。it

代碼實現

理論差很少說完了,下面該說具體的實現了,網上的實現代碼不少,直接找了一份代碼,我們一塊兒看一下代碼吧io

int main(int argc, const char * argv[]) {  
    StaticLinkList list[MAXSIZE];
    // 初始化數組中的數據
    createLinkList(list);
    
    for (int i = 0; i < 2; i ++) {
        ListInsert(list, 1, 100+i);
    }
    return 0;
}

咱們在循環中執行了兩次的插入操做,ListInsert方法中的參數1指的是每次都是把數據插在鏈表的第1個結點以前,咱們先推測一下結果。for循環

咱們先存了一個100這個數字,又存了101這個數字,由於以前初始化以後並無進行別的存儲,因此完成存儲以後,由於按照以前的說法,數組的第1個元素應該放的100,第2個位置纔是101,可是咱們在儲存的時候,是把要插入的數據插入到鏈表第一個元素的前面,因此從鏈表的角度看,101應該是第一個節點,100是第二個。那要怎麼表現出來呢?結合咱們上面說的,999這個位置的遊標應該指向鏈表第一個節點,也就是遊標應該爲2,數組下標2的元素由於他是鏈表的第一個節點,因此他的遊標應該指向鏈表第二個節點,也就是數組下標1。鏈表就兩個節點,因此最後一個節點的遊標要指向0. 而後數組下標0的位置的遊標指向了下一個可用的存儲位置3。class

結果
結果

結果跟咱們以前分析的是一致的。

只要理清楚了這些思路以後,其餘的代碼看起來也就容易不少了。

好比說下面這個計算鏈表長度的方法

int linkLength(StaticLinkList list[]) {
    int j = 0;
    int i = list[MAXSIZE-1].cur;
    while (i) {
        i = list[i].cur;
        j ++;
    }
    return j;
}

先拿到數組的最後一個元素遊標的數值,若是是0,就說明尚未儲存呢,索引鏈表長度就是0,若是已經儲存過了,因此獲得i的值就不是0,經過一個while循環,一個個的遍歷鏈表結點,直到鏈表最後一個結點的遊標又是0,跳出循環,而後經過一個j來標識循環次數,也就是鏈表長度。

關於靜態鏈表就說這麼多吧,更多的內容你們能夠上網搜一下。

下面我把使用的代碼放上,主要是實現了插入操做,刪除暫時沒作,你們能夠看懂了插入而後本身寫一下刪除,代碼上我儘量詳細的作了註釋,你們能夠看看代碼 ~

//  main.c
//  鏈表
//  Created by sunxiaobin on 2018/6/15.

#include <stdio.h>
#define MAXSIZE 1000
typedef int ElemType;
typedef struct {
    ElemType data;
    int cur;
}StaticLinkList;

// 聲明
void createLinkList(StaticLinkList list[]);
int linkLength(StaticLinkList list[]);
int Malloc_SLL(StaticLinkList list[]);
int ListInsert(StaticLinkList L[], int i, ElemType e);

int main(int argc, const char * argv[]) {
    StaticLinkList list[MAXSIZE];
    
    // 初始化數組中的數據
    createLinkList(list);
    
    for (int i = 0; i < 2; i ++) {
        ListInsert(list, 1, 100+i);
    } 
    return 0;
}

void createLinkList(StaticLinkList list[]){
    int i;// i就表明數組下標
    for (i = 0; i < MAXSIZE-1; i ++) {
        list[i].cur = i + 1;  // cur存放的是下一個結點的下標
    }
    list[MAXSIZE-1].cur = 0; //
    
}

// 求鏈表長度
int linkLength(StaticLinkList list[]) {
    int j = 0;
    int i = list[MAXSIZE-1].cur;
    while (i) {
        i = list[i].cur;
        j ++;
    }   
    return j;
}

// 獲得備用鏈表的第一個元素下標
int Malloc_SLL(StaticLinkList list[]) {
    
    int i = list[0].cur;
    
    if (list[0].cur)
        // 更新頭結點的遊標, 原來頭結點的遊標指向i這個位置,
        // 可是這個位置要進行了存儲 , 被使用了,
        // 頭結點應該把遊標更新到新的備用鏈表的第一個元素, 也就是剛剛使用了這個i位置的元素的遊標指向的位置.
        list[0].cur = list[i].cur;
    
    return i;
}

// 在靜態鏈表L中第i個元素以前插入新的數據元素e
int ListInsert(StaticLinkList list[], int i, ElemType e) {
    
    int k = MAXSIZE - 1; // 數組的最後一個元素
    
    //判斷要插入的位置是否是合法範圍
    if (i<1 || i>linkLength(list)+1) return 0;
    
    int j = Malloc_SLL(list); // 獲得備用鏈表的第一個元素下標
    
    if (j) {  // 若是元素存在,(不存在就說明數組已經滿了)
        
        list[j].data = e;// 先把e存進來,而後經過改變某幾個結點的遊標
        
        // 這個for循環內, 若是是第一次存儲的話根本不走
        //
        for (int l = 1; l<=i-1; l++) {
            // 獲得第i-1個結點在數組中的下標
            k = list[k].cur;
        }
        
        list[j].cur = list[k].cur; // 將第i-1個元素的cur設置爲新加的這個結點的下標,將新加的這個結點的下標設置爲以前第i-1個元素存儲的cur值
        list[k].cur = j;
    
        return 1;
    } 
    return 0;    
}
相關文章
相關標籤/搜索