1. 程式人生 > >線性表之連結串列的實現(二)-靜態連結串列實現

線性表之連結串列的實現(二)-靜態連結串列實現

靜態連結串列基本概念

這一部分的內容主要參照了這篇帖子[靜態連結串列 C實現]的內容。並且貼上的說明圖也是來自於這篇帖子,再次特做宣告。

什麼是靜態連結串列

用全域性資料實現的連結串列叫做靜態連結串列。由於全域性陣列是儲存在靜態區,又叫做靜態連結串列。

優缺點

優點:

  • 其一,保持了傳統連結串列的優點。對於插入和刪除操作效率比較高,只需要修改指標的指向即可,不需要大量的移動元素。
  • 其二,由於預先分配了較大空間,當進行插入或者刪除節點時,沒有開闢和回收資源的時間消耗,還是隻需要修改指標的指向即可。

缺點:

  • 需要預先開闢較大空間,如果系統中不存在這樣一片連續的空間,則無法實現靜態連結串列,但是傳統連結串列可以利用作業系統中的記憶體碎片,並且記憶體利用率高於靜態連結串列。
  • 需要模擬作業系統在陣列空間上實現節點記憶體的開闢和回收。增加程式碼的複雜度。

如何實現

一個數組邏輯分成兩部分,空閒連結串列部分和非空閒連結串列部分。
他們都是通過指標來連線的,空閒連結串列由空閒頭結點來連線起來,非空閒連結串列由非空閒頭結點連線起來。

空閒連結串列本質上被是做需要進行管理的記憶體,當插入節點時,向管理的記憶體部分申請空間,當刪除節點時,向管理的記憶體部分釋放空間

個人感覺對於靜態連結串列的實現最有價值的部分,就是模擬對於記憶體的管理,怎麼樣去實現邏輯到物理的對應,這讓我有機會從系統程式設計師的角度寫程式碼!!!

如下圖所示,下標為0的節點是空閒連結串列的頭結點。
下標為1的節點是非空閒連結串列的頭結點。由他們兩個非洲維護這兩塊區域。
圖片摘自於上文連結,並非我原創


(ps:上面這張圖並非我原創,來自於本文一開始的連結當中)

靜態連結串列實現

常量宣告

  • common.h
#ifndef common_H
#define common_H

/* 函式結果狀態碼 */
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define INFEASIBLE -1
//#define OVERFLOW -2

/* 型別定義 */
typedef int Status;     // Status是函式的型別,其值是函式結果狀態碼
typedef int ElemType;   // ElemType是資料型別
#endif

靜態連結串列節點結構及常用操作介面宣告

  • SLinkedList.h
#ifndef SLinkedList_H
#define SLinkedList_H
#include "common.h"

#define MAXSIZE 1024 //連結串列的最大長度

/* 靜態連結串列節點宣告 */
struct SListNode{
    ElemType data;
    int next;

    SListNode() : data(0), next(-1) {}
    SListNode( ElemType x ) : data(x), next(-1) {}
};

typedef SListNode* SLinkedList;  

/* 靜態連結串列記憶體管理操作 */
Status init_sl( SLinkedList slist ); // 初始化為靜態連結串列空間
int malloc_sl( SLinkedList slist );  // 開闢節點空間
Status free_sl( SLinkedList slist, int k ); // 回收節點空間


/* 靜態連結串列常用操作 */
Status create_slinkedlist( SLinkedList slist, const ElemType* arr, int n ); // 根據陣列建立靜態連結串列-頭插法
Status print_slinkedlist( const SLinkedList slist ); // 列印靜態連結串列
Status insert_slinkedlist( SLinkedList slist, int i, int target ); // 在第i位置插入節點
Status delete_slinkedlist( SLinkedList slist, int i); //在第i個位置刪除節點

#endif

靜態連結串列記憶體管理和常用操作實現

  • SLinkedList.cpp
    下面我分別給出每個函式的實現,並給出相應的說明。所以,將該檔案的程式碼拆分開來。

  • 將資料空間初始化為連結串列

    • 程式碼還是圍繞空閒連結串列部分和非空閒連結串列部分去寫。由於初始化時沒有連結串列- 節點,所以此時全部是空閒連結串列。
    • 注意對兩個頭結點的初始化。
      為指標指向-1,不指向0,這點和嚴蔚敏老師的寫法有區別
Status init_sl( SLinkedList slist ){
    if(!slist){
        std::cerr << "Invalid arguments!";
        return ERROR;
    }

    slist[0].next = 2;  // slist[0]是空閒表的頭結點
    slist[1].next = -1; // slist[1]是非空閒表的頭結點

    for(int i = 2; i < MAXSIZE - 1; ++i){
        slist[i].next = i + 1;
    }
    slist[MAXSIZE - 1].next = -1;

    return OK;
}
  • 申請節點空間
    • 此時是對空閒連結串列的操作,所以注意操作的頭結點。
    • 注意申請不成功時的判斷,最後返回申請節點的下標即可。
int malloc_sl( SLinkedList slist ){
    if(NULL == slist){
        std::cerr << "Invalid arguments!";
        return ERROR;
    }

    int i = slist[0].next;
    if(i != -1){ // 申請節點成功 
        slist[0].next = slist[i].next;
    }
    return i;
}
  • 釋放節點空間
    • 這一塊都是對空閒連結串列的操作,所以注意操作頭結點即可。
    • 頭插法
Status free_sl( SLinkedList slist, int k ){
    if(NULL == slist){
        std::cerr << "Invalid arguments!";
            return ERROR;
    }

    slist[k].next = slist[0].next;
    slist[0].next = k;

    return OK;
}
  • 靜態連結串列常用操作
    • 這一部分的程式碼是操作非空連結串列,就是實際連結串列本身。
    • 插入操作和刪除操作要注意,對於第i個節點,申請空間和歸還空間的操作符是空間的下標。申請空間的表現為申請了一個合法下標,釋放空間的表現為釋放了一個下標。所以對於刪除第i個節點,要找到這個節點的下標,刪除即可。
Status create_slinkedlist( SLinkedList slist, const ElemType* arr, int n ){
    if(!slist || !arr || n <= 0){
        std::cerr << "Invalid arguments!";
        return ERROR;
    }

    for( int i = 0; i < n; ++i ){
        int s = malloc_sl( slist );
        if(-1==s){
            std::cerr << "Overflow!";
            return OVERFLOW;
        }
        slist[s].data = arr[i];
        slist[s].next = slist[1].next;
        slist[1].next = s;
    }

    return OK;
}
Status print_slinkedlist( const SLinkedList slist ){
    if(!slist){
        std::cerr << "Not invalid arguments!";
        return ERROR;
    }
    int cur = slist[1].next;
    while(cur != -1){
        std::cout << slist[cur].data << std::endl;
        cur = slist[cur].next;
    }
    return OK;
}
Status insert_slinkedlist( SLinkedList slist, int i, int target ){
    if(!slist || i < 1 ){
        std::cerr << "Invalid arguments!";
        return ERROR;
    }

    int cur = 1; // 頭結點
    for( int cnt = 0; cnt < i-1 && cur > -1 ; ++cnt ){
        cur = slist[cur].next;
    }
    if(-1 == cur) return ERROR;
    else{
        int s = malloc_sl(slist); // 為第i個位置的節點開闢空間
        if(-1==s) return OVERFLOW;
        slist[s].data = target;
        slist[s].next = slist[cur].next;
        slist[cur].next = s;

        return OK;
    }
}
Status delete_slinkedlist( SLinkedList slist, int i){
    if(!slist){
        std::cerr << "Invalid arguments!";
        return ERROR;
    }
    int cur = 1; // 頭結點
    for( int cnt = 0; cnt < i - 1; ++cnt){
        cur = slist[cur].next;
    }
    if(-1==cur) return ERROR;
    else{
        int s = slist[cur].next; // 要刪除的第i個節點的位置
        slist[cur].next = slist[s].next;

        free_sl(slist, s); // 釋放第i個節點的位置
        return OK;
    } 
}