1. 程式人生 > >靜態連結串列基本操作(C語言實現)詳解

靜態連結串列基本操作(C語言實現)詳解

上節,我們初步建立了一個靜態連結串列,本節學習有關靜態連結串列的一些基本操作,包括對錶中資料元素的新增、刪除、查詢和更改。

本節是建立在已能成功建立靜態連結串列的基礎上,因此我們繼續使用上節中已建立好的靜態連結串列學習本節內容,建立好的靜態連結串列如圖 1 所示:
建立好的靜態連結串列
圖 1 建立好的靜態連結串列

靜態連結串列新增元素

例如,在圖 1 的基礎,將元素 4 新增到靜態連結串列中的第 3 個位置上,實現過程如下:
  1. 從備用連結串列中摘除一個節點,用於儲存元素 4;
  2. 找到表中第 2 個節點(新增位置的前一個節點,這裡是資料元素 2),將元素 2 的遊標賦值給新元素 4;
  3. 將元素 4 所在陣列中的下標賦值給元素 2 的遊標;

經過以上幾步操作,資料元素 4 就成功地新增到了靜態連結串列中,此時新的靜態連結串列如圖 2 所示:

圖 2 新增元素 4 的靜態連結串列
由此,我們通過嘗試編寫 C 語言程式實現以上操作。讀者可參考一下程式:
//向連結串列中插入資料,body表示連結串列的頭結點在陣列中的位置,add表示插入元素的位置,a表示要插入的資料
void insertArr(component * array,int body,int add,char a){
    int tempBody=body;//tempBody做遍歷結構體陣列使用
    //找到要插入位置的上一個結點在陣列中的位置
    for (int i=1; i<add; i++) {
        tempBody=array[tempBody].cur;
    }
    int insert=mallocArr(array);//申請空間,準備插入
    array[insert].data=a;

    array[insert].cur=array[tempBody].cur;//新插入結點的遊標等於其直接前驅結點的遊標
    array[tempBody].cur=insert;//直接前驅結點的遊標等於新插入結點所在陣列中的下標
}

靜態連結串列刪除元素

靜態連結串列中刪除指定元素,只需實現以下 2 步操作:
  1. 將存有目標元素的節點從資料鏈表中摘除;
  2. 將摘除節點新增到備用連結串列,以便下次再用;

提示:若問題中涉及大量刪除元素的操作,建議讀者在建立靜態連結串列之初建立一個帶有頭節點的靜態連結串列,方便實現刪除連結串列中第一個資料元素的操作。

實現該操作的 C 語言程式碼為:
//備用連結串列回收空間的函式,其中array為儲存資料的陣列,k表示未使用節點所在陣列的下標
void freeArr(component * array,int k){
    array[k].cur=array[0].cur;
    array[0].cur=k;
}
//刪除結點函式,a 表示被刪除結點中資料域存放的資料
void deletArr(component * array,int body,char a){
    int tempBody=body;
    //找到被刪除結點的位置
    while (array[tempBody].data!=a) {
        tempBody=array[tempBody].cur;
        //當tempBody為0時,表示連結串列遍歷結束,說明連結串列中沒有儲存該資料的結點
        if (tempBody==0) {
            printf("連結串列中沒有此資料");
            return;
        }
    }
    //執行到此,證明有該結點
    int del=tempBody;
    tempBody=body;
    //找到該結點的上一個結點,做刪除操作
    while (array[tempBody].cur!=del) {
        tempBody=array[tempBody].cur;
    }
    //將被刪除結點的遊標直接給被刪除結點的上一個結點
    array[tempBody].cur=array[del].cur;
    //回收被摘除節點的空間
    freeArr(array, del);
}

靜態連結串列查詢元素

靜態連結串列查詢指定元素,由於我們只知道靜態連結串列第一個元素所在陣列中的位置,因此只能通過逐個遍歷靜態連結串列的方式,查詢存有指定資料元素的節點。

靜態連結串列查詢指定資料元素的 C 語言實現程式碼如下:
//在以body作為頭結點的連結串列中查詢資料域為elem的結點在陣列中的位置
int selectElem(component * array,int body,char elem){
    int tempBody=body;
    //當遊標值為0時,表示連結串列結束
    while (array[tempBody].cur!=0) {
        if (array[tempBody].data==elem) {
            return tempBody;
        }
        tempBody=array[tempBody].cur;
    }
    return -1;//返回-1,表示在連結串列中沒有找到該元素
}

靜態連結串列中更改資料

更改靜態連結串列中的資料,只需找到目標元素所在的節點,直接更改節點中的資料域即可。

實現此操作的 C 語言程式碼如下:
//在以body作為頭結點的連結串列中將資料域為oldElem的結點,資料域改為newElem
void amendElem(component * array,int body,char oldElem,char newElem){
    int add=selectElem(array, body, oldElem);
    if (add==-1) {
        printf("無更改元素");
        return;
    }
    array[add].data=newElem;
}

總結

這裡給出以上對靜態連結串列做 "增刪查改" 操作的完整實現程式碼:
#include <stdio.h>
#define maxSize 7
typedef struct {
    char data;
    int cur;
}component;
//將結構體陣列中所有分量連結到備用連結串列中
void reserveArr(component *array);
//初始化靜態連結串列
int initArr(component *array);
//向連結串列中插入資料,body表示連結串列的頭結點在陣列中的位置,add表示插入元素的位置,a表示要插入的資料
void insertArr(component * array,int body,int add,char a);
//刪除連結串列中含有字元a的結點
void deletArr(component * array,int body,char a);
//查詢儲存有字元elem的結點在陣列的位置
int selectElem(component * array,int body,char elem);
//將連結串列中的字元oldElem改為newElem
void amendElem(component * array,int body,char oldElem,char newElem);
//輸出函式
void displayArr(component * array,int body);
//從備用連結串列中摘除空閒節點的實現函式
int mallocArr(component * array);
//將摘除下來的節點連結到備用連結串列上
void freeArr(component * array,int k);

int main() {
    component array[maxSize];
    int body=initArr(array);
    printf("靜態連結串列為:\n");
    displayArr(array, body);
  
    printf("在第3的位置上插入結點‘e’:\n");
    insertArr(array, body, 3,'e');
    displayArr(array, body);
  
    printf("刪除資料域為‘a’的結點:\n");
    deletArr(array, body, 'a');
    displayArr(array, body);
  
    printf("查詢資料域為‘e’的結點的位置:\n");
    int selectAdd=selectElem(array,body ,'e');
    printf("%d\n",selectAdd);
    printf("將結點資料域為‘e’改為‘h’:\n");
    amendElem(array, body, 'e', 'h');
    displayArr(array, body);
    return 0;
}
//建立備用連結串列
void reserveArr(component *array){
    for (int i=0; i<maxSize; i++) {
        array[i].cur=i+1;//將每個陣列分量連結到一起
    }
    array[maxSize-1].cur=0;//連結串列最後一個結點的遊標值為0
}

//初始化靜態連結串列
int initArr(component *array){
    reserveArr(array);
    int body=mallocArr(array);
    //宣告一個變數,把它當指標使,指向連結串列的最後的一個結點,因為連結串列為空,所以和頭結點重合
    int tempBody=body;
    for (int i=1; i<5; i++) {
        int j=mallocArr(array);//從備用連結串列中拿出空閒的分量
        array[tempBody].cur=j;//將申請的空線分量連結在連結串列的最後一個結點後面
        array[j].data='a'+i-1;//給新申請的分量的資料域初始化
        tempBody=j;//將指向連結串列最後一個結點的指標後移
    }
    array[tempBody].cur=0;//新的連結串列最後一個結點的指標設定為0
    return body;
}

void insertArr(component * array,int body,int add,char a){
    int tempBody=body;
    for (int i=1; i<add; i++) {
        tempBody=array[tempBody].cur;
    }
    int insert=mallocArr(array);
    array[insert].cur=array[tempBody].cur;
    array[insert].data=a;
    array[tempBody].cur=insert;
  
}

void deletArr(component * array,int body,char a){
    int tempBody=body;
    //找到被刪除結點的位置
    while (array[tempBody].data!=a) {
        tempBody=array[tempBody].cur;
        //當tempBody為0時,表示連結串列遍歷結束,說明連結串列中沒有儲存該資料的結點
        if (tempBody==0) {
            printf("連結串列中沒有此資料");
            return;
        }
    }
    //執行到此,證明有該結點
    int del=tempBody;
    tempBody=body;
    //找到該結點的上一個結點,做刪除操作
    while (array[tempBody].cur!=del) {
        tempBody=array[tempBody].cur;
    }
    //將被刪除結點的遊標直接給被刪除結點的上一個結點
    array[tempBody].cur=array[del].cur;
  
    freeArr(array, del);
}

int selectElem(component * array,int body,char elem){
    int tempBody=body;
    //當遊標值為0時,表示連結串列結束
    while (array[tempBody].cur!=0) {
        if (array[tempBody].data==elem) {
            return tempBody;
        }
        tempBody=array[tempBody].cur;
    }
    return -1;//返回-1,表示在連結串列中沒有找到該元素
}

void amendElem(component * array,int body,char oldElem,char newElem){
    int add=selectElem(array, body, oldElem);
    if (add==-1) {
        printf("無更改元素");
        return;
    }
    array[add].data=newElem;
}

void displayArr(component * array,int body){
    int tempBody=body;//tempBody準備做遍歷使用
    while (array[tempBody].cur) {
        printf("%c,%d ",array[tempBody].data,array[tempBody].cur);
        tempBody=array[tempBody].cur;
    }
    printf("%c,%d\n",array[tempBody].data,array[tempBody].cur);

}

//提取分配空間
int mallocArr(component * array){
    //若備用連結串列非空,則返回分配的結點下標,否則返回0(當分配最後一個結點時,該結點的遊標值為0)
    int i=array[0].cur;
    if (array[0].cur) {
        array[0].cur=array[i].cur;
    }
    return i;
}
//將摘除下來的節點連結到備用連結串列上
void freeArr(component * array,int k){
    array[k].cur=array[0].cur;
    array[0].cur=k;
}
程式執行結果為:

靜態連結串列為:
,2 a,3 b,4 c,5 d,0
在第3的位置上插入結點‘e’:
,2 a,3 b,6 e,4 c,5 d,0
刪除資料域為‘a’的結點:
,3 b,6 e,4 c,5 d,0
查詢資料域為‘e’的結點的位置:
6
將結點資料域為‘e’改為‘h’:
,3 b,6 h,4 c,5 d,0