C語言實現順序表(順序存儲結構)


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

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

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

例如,使用順序表存儲集合{1,2,3,4,5}數據最終的存儲狀態如下圖所示:
在這里插入圖片描述
由此我們可以得出,將“具有 ‘一對一’ 邏輯關系的數據按照次序連續存儲到一整塊物理空間上”的存儲結構就是順序存儲結構。

通過觀察圖 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

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

順序表的基本操作

順序表插入元素

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

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

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

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

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

  • 遍歷至順序表存儲第 3 個數據元素的位置,如下圖 所示:
    在這里插入圖片描述
    將元素 3 以及后續元素 4 和 5 整體向后移動一個位置,如圖下 所示:
    在這里插入圖片描述
  • 將新元素 6 放入騰出的位置,如下 所示:
    在這里插入圖片描述
    因此,順序表插入數據元素的 C 語言實現代碼如下:
//插入函數,其中,elem為插入的元素,add為插入到順序表的位置
table addTable(table t,int elem,int add)
{
    //判斷插入本身是否存在問題(如果插入元素位置比整張表的長度+1還大(如果相等,是尾隨的情況),或者插入的位置本身不存在,程序作為提示並自動退出)
    if (add>t.length+1||add<1) {
        printf("插入位置有問題");
        return t;
    }
    //做插入操作時,首先需要看順序表是否有多余的存儲空間提供給插入的元素,如果沒有,需要申請
    if (t.length==t.size) {
        t.head=(int *)realloc(t.head, (t.size+1)*sizeof(int));
        if (!t.head) {
            printf("存儲分配失敗");
            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 的過程如下圖 所示:
在這里插入圖片描述
因此,順序表刪除元素的 C 語言實現代碼為:

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

順序表查找元素

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

這里,我們選擇順序查找算法,具體實現代碼為:

//查找函數,其中,elem表示要查找的數據元素的值
int searchable(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 replaceTable(table t,int elem,int newElem)
{
	int add=searchable(t, elem);
	t.head[add-1]=newElem;//由於返回的是元素在順序表中的位置,所以-1就是該元素在數組中的下標
	return t;
}
void displayTable(table t){
	for (int i=0;i<t.length;i++) {
		printf("%d ",t.head[i]);
	}
	printf("\n");
}

返回順序表的長度和最大容納表項個數

這兩個元素屬於結構體,直接返回即可

//返回順序表的長度
int returnLength(table t)
{
	return t.length;//返回結構體的長度元素即可
}
//返回順序表最大容納表項個數
int returnSize(table t)
{
	return t.size;
}

以上是順序表使用過程中最常用的基本操作,相關完整代碼已經push到GitHub,需要的小伙伴自行clone,如果覺得還不錯的話,歡迎Star,這里是傳送門順序表(順序存儲結構),除此之外,想要了解更多的C,C++,Java,Python等相關知識的童鞋,歡迎來我的博客(相逢的博客),我們一起討論!接下來我會持續更新其他語言實現順序表,其中算法分析我會在下一次,C++實現時與大家分享,敬請期待!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM