順序表

小懿同学發表於2024-04-22

順序表

(順序儲存結構)及初始化詳解

順序表,全名順序儲存結構,是線性表的一種。通過《什麼是線性表》一節的學習我們知道,線性表用於儲存邏輯關係為“一對一”的資料,順序表自然也不例外。

不僅如此,順序表對資料的物理儲存結構也有要求。順序表儲存資料時,會提前申請一整塊足夠大小的物理空間,然後將資料依次儲存起來,儲存時做到資料元素之間不留一絲縫隙。

例如,使用順序表儲存集合 {1,2,3,4,5},資料最終的儲存狀態如圖 1 所示:

img
圖 1 順序儲存結構示意圖

由此我們可以得出,將“具有 ‘一對一’ 邏輯關係的資料按照次序連續儲存到一整塊物理空間上”的儲存結構就是順序儲存結構。

通過觀察圖 1 中資料的儲存狀態,我們可以發現,順序表儲存資料同陣列非常接近。其實,順序表儲存資料使用的就是陣列。

順序表的初始化

使用順序表儲存資料之前,除了要申請足夠大小的物理空間之外,為了方便後期使用表中的資料,順序表還需要實時記錄以下 2 項資料:

  1. 順序表申請的儲存容量;
  2. 順序表的長度,也就是表中儲存資料元素的個數;

提示:正常狀態下,順序表申請的儲存容量要大於順序表的長度。

因此,我們需要自定義順序表,C 語言實現程式碼如下:

typedef struct Table{
    int * head;//宣告瞭一個名為head的長度不確定的陣列,也叫“動態陣列”
    int length;//記錄當前順序表的長度
    int size;//記錄順序表分配的儲存容量
}table;

注意,head 是我們宣告的一個未初始化的動態陣列,不要只把它看做是普通的指標。

接下來開始學習順序表的初始化,也就是初步建立一個順序表。建立順序表需要做如下工作:

  • 給 head 動態資料申請足夠大小的物理空間;
  • 給 size 和 length 賦初值;

因此,C 語言實現程式碼如下:

#define Size 5 //對Size進行巨集定義,表示順序表申請空間的大小
table initTable(){
    table t;
    t.head=(int*)malloc(Size*sizeof(int));//構造一個空的順序表,動態申請儲存空間
    if (!t.head) //如果申請失敗,作出提示並直接退出程式
    {
        printf("初始化失敗");
        exit(0);
    }
    t.length=0;//空表的長度初始化為0
    t.size=Size;//空表的初始儲存空間為Size
    return t;
}

我們看到,整個順序表初始化的過程被封裝到了一個函式中,此函式返回值是一個已經初始化完成的順序表。這樣做的好處是增加了程式碼的可用性,也更加美觀。與此同時,順序表初始化過程中,要注意對物理空間的申請進行判斷,對申請失敗的情況進行處理,這裡只進行了“輸出提示資訊和強制退出”的操作,可以根據你自己的需要對程式碼中的 if 語句進行改進。

通過在主函式中呼叫 initTable 語句,就可以成功建立一個空的順序表,與此同時我們還可以試著向順序表中新增一些元素,C 語言實現程式碼如下:

#include <stdio.h>
#include <stdlib.h>
#define Size 5
typedef struct Table{
    int * head;
    int length;
    int size;
}table;
table initTable(){
    table t;
    t.head=(int*)malloc(Size*sizeof(int));
    if (!t.head)
    {
        printf("初始化失敗");
        exit(0);
    }
    t.length=0;
    t.size=Size;
    return t;
}
//輸出順序表中元素的函式
void displayTable(table t){
    for (int i=0;i<t.length;i++) {
        printf("%d ",t.head[i]);
    }
    printf("\n");
}
int main(){
    table t=initTable();
    //向順序表中新增元素
    for (int i=1; i<=Size; i++) {
        t.head[i-1]=i;
        t.length++;
    }
    printf("順序表中儲存的元素分別是:\n");
    displayTable(t);
    return 0;
}

程式執行結果如下:

順序表中儲存的元素分別是:

1 2 3 4 5

可以看到,順序表初始化成功。

順序表的基本操作

我們學習了順序表及初始化的過程,本節學習有關順序表的一些基本操作,以及如何使用 C 語言實現它們。

順序表插入元素

向已有順序表中插入資料元素,根據插入位置的不同,可分為以下 3 種情況:

  1. 插入到順序表的表頭;
  2. 在表的中間位置插入元素;
  3. 尾隨順序表中已有元素,作為順序表中的最後一個元素;

雖然資料元素插入順序表中的位置有所不同,但是都使用的是同一種方式去解決,即:通過遍歷,找到資料元素要插入的位置,然後做如下兩步工作:

  • 將要插入位置元素以及後續的元素整體向後移動一個位置;
  • 將元素放到騰出來的位置上;

例如,在 {1,2,3,4,5} 的第 3 個位置上插入元素 6,實現過程如下:

  • 遍歷至順序表儲存第 3 個資料元素的位置,如圖 1 所示:

    找到目標元素位置
    圖 1 找到目標元素位置

  • 將元素 3 以及後續元素 4 和 5 整體向後移動一個位置,如圖 2 所示:

    將插入位置騰出
    圖 2 將插入位置騰出

  • 將新元素 6 放入騰出的位置,如圖 3 所示:

    插入目標元素
    圖 3 插入目標元素

因此,順序表插入資料元素的 C 語言實現程式碼如下:

//插入函式,其中,elem為插入的元素,add為插入到順序表的位置
table addTable(table t,int elem,int add)
{
    //判斷插入本身是否存在問題(如果插入元素位置比整張表的長度+1還大(如果相等,是尾隨的情況),或者插入的位置本身不存在,程式作為提示並自動退出)
    if (add>t.length+1||add<1) {
        printf("插入位置有問題\n");
        return t;
    }
    //做插入操作時,首先需要看順序表是否有多餘的儲存空間提供給插入的元素,如果沒有,需要申請
    if (t.length==t.size) {
        t.head=(int *)realloc(t.head, (t.size+1)*sizeof(int));
        if (!t.head) {
            printf("儲存分配失敗\n");
            return t;
        }
        t.size+=1;
    } 
    //插入操作,需要將從插入位置開始的後續元素,逐個後移
    for (int i=t.length-1; i>=add-1; i--) {
        t.head[i+1]=t.head[i];
    }
    //後移完成後,直接將所需插入元素,新增到順序表的相應位置
    t.head[add-1]=elem;
    //由於新增了元素,所以長度+1
    t.length++;
    return t;
}

注意,動態陣列額外申請更多物理空間使用的是 realloc 函式。並且,在實現後續元素整體後移的過程,目標位置其實是有資料的,還是 3,只是下一步新插入元素時會把舊元素直接覆蓋。

順序表刪除元素

從順序表中刪除指定元素,實現起來非常簡單,只需找到目標元素,並將其後續所有元素整體前移 1 個位置即可。

後續元素整體前移一個位置,會直接將目標元素刪除,可間接實現刪除元素的目的。

例如,從 {1,2,3,4,5} 中刪除元素 3 的過程如圖 4 所示:

img
圖 4 順序表刪除元素的過程示意圖

因此,順序表刪除元素的 C 語言實現程式碼為:

table delTable(table t,int add){
    if (add>t.length || add<1) {
        printf("被刪除元素的位置有誤\n");
        return t;
    }
    //刪除操作
    for (int i=add; i<t.length; i++) {
        t.head[i-1]=t.head[i];
    }
    t.length--;
    return t;
}

順序表查詢元素

順序表中查詢目標元素,可以使用多種查詢演算法實現,比如說二分查詢演算法、插值查詢演算法等。

這裡,我們選擇順序查詢演算法,具體實現程式碼為:

//查詢函式,其中,elem表示要查詢的資料元素的值
int selectTable(table t,int elem){
    for (int i=0; i<t.length; i++) {
        if (t.head[i]==elem) {
            return i+1;
        }
    }
    return -1;//如果查詢失敗,返回-1
}

順序表更改元素

順序表更改元素的實現過程是:

  1. 找到目標元素;
  2. 直接修改該元素的值;

順序表更改元素的 C 語言實現程式碼為:

//更改函式,其中,elem為要更改的元素,newElem為新的資料元素
table amendTable(table t,int elem,int newElem){
    int add=selectTable(t, elem);
    t.head[add-1]=newElem;//由於返回的是元素在順序表中的位置,所以-1就是該元素在陣列中的下標
    return t;C
}

以上是順序表使用過程中最常用的基本操作,這裡給出本節完整的實現程式碼:

#include <stdio.h>
#include <stdlib.h>
#define Size 5
typedef struct Table{
    int * head;
    int length;
    int size;
}table;
table initTable(){
    table t;
    t.head=(int*)malloc(Size*sizeof(int));
    if (!t.head)
    {
        printf("初始化失敗\n");
        exit(0);
    }
    t.length=0;
    t.size=Size;
    return t;
}
table addTable(table t,int elem,int add)
{
    if (add>t.length+1||add<1) {
        printf("插入位置有問題\n");
        return t;
    }
    if (t.length>=t.size) {
        t.head=(int *)realloc(t.head, (t.size+1)*sizeof(int));
        if (!t.head) {
            printf("儲存分配失敗\n");
        }
        t.size+=1;
    }
    for (int i=t.length-1; i>=add-1; i--) {
        t.head[i+1]=t.head[i];
    }
    t.head[add-1]=elem;
    t.length++;
    return t;
}
table delTable(table t,int add){
    if (add>t.length || add<1) {
        printf("被刪除元素的位置有誤\n");
        return t;
    }
    for (int i=add; i<t.length; i++) {
        t.head[i-1]=t.head[i];
    }
    t.length--;
    return t;
}
int selectTable(table t,int elem){
    for (int i=0; i<t.length; i++) {
        if (t.head[i]==elem) {
            return i+1;
        }
    }
    return -1;
}
table amendTable(table t,int elem,int newElem){
    int add=selectTable(t, elem);
    t.head[add-1]=newElem;
    return t;
}
void displayTable(table t){
    for (int i=0;i<t.length;i++) {
        printf("%d ",t.head[i]);
    }
    printf("\n");
}
int main(){
    table t1=initTable();
    for (int i=1; i<=Size; i++) {
        t1.head[i-1]=i;
        t1.length++;
    }
    printf("原順序表:\n");
    displayTable(t1);
  
    printf("刪除元素1:\n");
    t1=delTable(t1, 1);
    displayTable(t1);
  
    printf("在第2的位置插入元素5:\n");
    t1=addTable(t1, 5, 2);
    displayTable(t1);
  
    printf("查詢元素3的位置:\n");
    int add=selectTable(t1, 3);
    printf("%d\n",add);
  
    printf("將元素3改為6:\n");
    t1=amendTable(t1, 3, 6);
    displayTable(t1);
    return 0;
}

程式執行結果為:

原順序表:
1 2 3 4 5
刪除元素1:
2 3 4 5
在第2的位置插入元素5:
2 5 3 4 5
查詢元素3的位置:
3
將元素3改為6:
2 5 6 4 5

相關文章