C語言:順序表(上)
1.順序表的介紹
2.順序表的實現
1.順序表的介紹
線性表是n個具有相同特性的數據元素的有限序列。
線性表是一種在實際中廣泛使用的數據結構,常見的線性表:順序表、鏈表、棧、隊列、字符串…
線性表在邏輯上是線性結構,也就說是連續的一條直線。但是在物理結構上并不一定是連續的,線性表在物理上存儲時,通常以 數組 和 鏈式結構 的形式存儲。
順序表的底層結構是數組,對數組的封裝,實現了常用的增刪改查等接口。
順序表分為兩類:
-
靜態順序表:使用定長數組存儲元素。容易出現空間不夠用、空間浪費的問題。
-
動態順序表:空間可以按需申請。
接下來以動態順序表為例,編程實現順序表。
2.順序表的實現
首先,我們打開vs2022,創建一個頭文件SeqList.h,用來定義結構體和函數聲明,再創建SeqList.c來編寫函數,創建test.c文件來進行測試。
實現順序表的過程中,我們需要realloc函數來進行開辟和調整空間,用assert進行檢查,所以我們在頭文件SeqList.h中應包含stdio.h、stdlib.h、assert.h。
//SeqList.h
#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
定義順序表SeqList,再用typedef重命名為SL。
typedef struct SeqList
{int* arr;int size; //有效數據個數int capacity;//空間大小(個數)
}SL;
指針arr可以指向數組、結構體、字符串等等數據,所以要把int重命名,以便后續更改arr指向的數據。
//SeqList.h
#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int SLDataType;//把int重命名,方便后續更改
typedef struct SeqList
{SLDataType* arr;int size; //有效數據個數int capacity;//空間大小(個數)
}SL;
接下來進行函數聲明,我們要編寫函數實現順序表的初始化、銷毀、打印、尾插/刪、頭插/刪、定位插入/刪除、查找。
//SeqList.h
#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int SLDataType;//把int重命名,方便后續更改
typedef struct SeqList
{SLDataType* arr;int size; //有效數據個數int capacity;//空間大小(個數)
}SL;
void SLInit(SL* ps);//順序表初始化void SLDestroy(SL* ps);//順序表銷毀void SLPrint(SL s);//順序表打印void SLPushBack(SL* ps, SLDataType x);//尾插void SLPushFront(SL* ps, SLDataType x);//頭插void SLPopBack(SL* ps);//尾刪void SLPopFront(SL* ps);//頭刪void SLInsert(SL* ps, int pos, SLDataType x);//定位插入void SLErase(SL* ps, int pos);//定位刪除int SLFind(SL* ps, SLDataType x);//查找
順序表初始化:指針ps指向順序表,把arr先置為NULL,有效數據的個數size為0,空間大小capacity為0。
void SLInit(SL* ps)//順序表初始化
{ps->arr = NULL;ps->size = ps->capacity = 0;
}
順序表銷毀:需要判斷順序表是否為空,若為空則不需要銷毀,若不為空則先用free釋放arr指向的空間,再把size、capacity置為0。
void SLDestroy(SL* ps)//順序表銷毀
{if (ps->arr)//若ps->arr為NULL,不再free,若不為NULL,則執行freefree(ps->arr);ps->arr = NULL;ps->size = ps->capacity = 0;
}
在創建順序表之后,我們通過頭插、尾插、定位插入的方式往順序表中插入數據,但在插入之前,我們需要檢查arr指向的空間是否足夠,不夠則用realloc函數增加空間。
我們先判斷size與capacity是否相等,若相等,則說明空間不足,如圖所示:
size與capacity不相等,則以原空間大小的2倍增加空間。
void SLCheckCapacity(SL* ps)//檢查插入前空間是否足夠
{if (ps->capacity == ps->size)//空間大小和有效數據個數一致,則空間不足{int newCapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;SLDataType* t = (SLDataType*)realloc(ps->arr, newCapacity * sizeof(SLDataType));//要申請多大空間if (t == NULL)//若空間申請失敗{perror("realloc fail !");exit(1);//退出程序}ps->arr = t;//空間申請成功ps->capacity = newCapacity;//記錄新的空間大小}
}
尾插:尾插函數需要參數指向順序表的指針ps和插入的數據x,先用assert檢查ps是否為NULL,再通過SLCheckCapacity函數檢查空間是否足夠,若不足則增加空間,在插入數據之后,還要讓size加1,記錄新的有效數據個數。
void SLPushBack(SL* ps, SLDataType x)//尾插
{assert(ps);SLCheckCapacity(ps);//檢查插入前空間是否足夠ps->arr[ps->size] = x;ps->size++;
}
頭插:頭插與尾插類似,頭插還需要循環把數據整體向后挪動一位。
void SLPushFront(SL* ps, SLDataType x)//頭插
{assert(ps);SLCheckCapacity(ps);//檢查插入前空間是否足夠for (int i = ps->size;i > 0;i--)//讓順序表中已有的數據整體往后挪一位{ //從最后一位開始挪,避免覆蓋 ps->arr[i] = ps->arr[i - 1];}ps->arr[0] = x;ps->size++;
}
順序表打印函數:
void SLPrint(SL s)//打印
{for (int i = 0;i < s.size;i++)printf("%d ", s.arr[i]);printf("\n");
}
尾刪:只要size減1,讓原本最后一個數據無法被訪問,就實現了尾刪。
void SLPopBack(SL* ps)//尾刪
{assert(ps);assert(ps->size);//順序表不為空--ps->size;
}
頭刪:與尾刪類似,頭刪還要數據整體向前挪一位,且從第二位數據開始挪,通過覆蓋第一位數據實現頭刪。
void SLPopFront(SL* ps)//頭刪
{assert(ps);assert(ps->size);for (int i = 0;i < ps->size - 1;i++)//數據整體往前挪一位{ //從第二位往前挪,覆蓋第一位實現刪除ps->arr[i] = ps->arr[i + 1];}ps->size--;
}
定位插入:類似頭插、尾插,但多了一個參數pos,pos為數據插入后的下標,所以0<=pos<=size,在插入前,下標為pos及大于pos的數據往后挪一位。
void SLInsert(SL* ps, int pos, SLDataType x)//定位插入,插入后下標為pos
{assert(ps);assert(pos >= 0 && pos <= ps->size);SLCheckCapacity(ps);for (int i = ps->size;i > pos;i--)//pos及之后的數據整體往后挪一位{ps->arr[i] = ps->arr[i - 1];}ps->arr[pos] = x;ps->size++;
}
定位刪除:pos為下標,顯然0<=pos<size,下標大于pos的數據往前挪一位,通過arr[pos+1]把arr[pos]覆蓋掉來實現定位刪除。
void SLErase(SL* ps, int pos)//定位刪除
{assert(ps);assert(pos >= 0 && pos < ps->size);for (int i = pos;i < ps->size-1;i++)//pos之后數據往前挪一位{ //arr[pos+1]覆蓋掉arr[pos]ps->arr[i] = ps->arr[i + 1];}ps->size--;
}
查找:SLFind函數的參數x為要查找的數據,遍歷整個順序表,找到返回下標,找不到返回-1。
int SLFind(SL* ps, SLDataType x)//查找
{assert(ps);for (int i = 0;i < ps->size;i++){if (ps->arr[i] == x)return i;//找到了,返回下標}return -1;//未找到
}
在SeqList.c文件中的代碼如下:
//SeqList.c
#include"SeqList.h"
void SLInit(SL* ps)//順序表初始化
{ps->arr = NULL;ps->size = ps->capacity = 0;
}
void SLDestroy(SL* ps)//順序表銷毀
{if (ps->arr)//若ps->arr為NULL,不再free,若不為NULL,則執行freefree(ps->arr);ps->arr = NULL;ps->size = ps->capacity = 0;
}
void SLCheckCapacity(SL* ps)//檢查插入前空間是否足夠
{if (ps->capacity == ps->size)//空間大小和有效數據個數一致,則空間不足{int newCapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;SLDataType* t = (SLDataType*)realloc(ps->arr, newCapacity * sizeof(SLDataType));//要申請多大空間if (t == NULL)//若空間申請失敗{perror("realloc fail !");exit(1);//退出程序}ps->arr = t;//空間申請成功ps->capacity = newCapacity;}
}
void SLPushBack(SL* ps, SLDataType x)//尾插
{assert(ps);SLCheckCapacity(ps);//檢查插入前空間是否足夠ps->arr[ps->size] = x;ps->size++;
}
void SLPushFront(SL* ps, SLDataType x)//頭插
{assert(ps);SLCheckCapacity(ps);//檢查插入前空間是否足夠for (int i = ps->size;i > 0;i--)//讓順序表中已有的數據整體往后挪一位{ //從最后一位開始挪,避免覆蓋 ps->arr[i] = ps->arr[i - 1];}ps->arr[0] = x;ps->size++;
}
void SLPrint(SL s)//打印
{for (int i = 0;i < s.size;i++)printf("%d ", s.arr[i]);printf("\n");
}
void SLPopBack(SL* ps)//尾刪
{assert(ps);assert(ps->size);//順序表不為空--ps->size;
}
void SLPopFront(SL* ps)//頭刪
{assert(ps);assert(ps->size);for (int i = 0;i < ps->size - 1;i++)//數據整體往前挪一位{ //從第二位往前挪,覆蓋第一位實現刪除ps->arr[i] = ps->arr[i + 1];}ps->size--;
}
void SLInsert(SL* ps, int pos, SLDataType x)//定位插入,插入后下標為pos
{assert(ps);assert(pos >= 0 && pos <= ps->size);SLCheckCapacity(ps);for (int i = ps->size;i > pos;i--)//pos及之后的數據整體往后挪一位{ps->arr[i] = ps->arr[i - 1];}ps->arr[pos] = x;ps->size++;
}
void SLErase(SL* ps, int pos)//定位刪除
{assert(ps);assert(pos >= 0 && pos < ps->size);for (int i = pos;i < ps->size-1;i++)//pos之后數據往前挪一位{ //arr[pos+1]覆蓋掉arr[pos]ps->arr[i] = ps->arr[i + 1];}ps->size--;
}
int SLFind(SL* ps, SLDataType x)//查找
{assert(ps);for (int i = 0;i < ps->size;i++){if (ps->arr[i] == x)return i;//找到了,返回下標}return -1;//未找到
}
最后,我們就可以在test.c文件中做測試了,例如測試頭插和尾插:
#include"SeqList.h"
void test1()
{SL s;SL* ps = &s;SLInit(ps);SLPushFront(ps, 1);SLPushBack(ps,2);SLPushBack(ps, 3);SLPushBack(ps, 4);SLPrint(s);SLDestroy(ps);
}
int main()
{test1();return 0;
}
拙作一篇,望諸位同道不吝斧正。