網站首頁 編程語言 正文
概念及結構
順序表是用一段物理地址連續的存儲單元依次存儲數據元素的線性結構,一般情況下采用數組存儲。在數組 上完成數據的增刪查改。
順序表一般可以分為:
靜態順序表:使用定長數組存儲元素,元素的數目無法進行修改。
//順序表的靜態存儲 #define N 7 typedef int SLDataType; typedef struct SeqList { SLDataType array[N];//定長數組 size_t size;//有效數據的個數 }SeqList;
動態順序表
//順序表的動態存儲 typedef struct SeqList { SLDataType* array;//指向動態開辟的數組,空間不夠可以增容 size_t size;//有效數據的個數 size_t capacity;//容量空間的大小 };
接口實現
靜態順序表只適用于確定知道需要存多少數據的場景。靜態順序表的定長數組導致N定大了,空間開多了浪 費,開少了不夠用。所以現實中基本都是使用動態順序表,根據需要動態的分配空間大小,所以下面我們實現動態順序表。
1 順序表的動態存儲
typedef int SLDataType;//順序表中存儲的數據,此處假設是int型 typedef struct SeqList { int* a;//指向動態開辟的數組空間,空間可以隨時增容 int size;//存儲數據個數 int capacity;//存儲空間大小 }SL,SeqList;
2 順序表初始化
void SeqListInit(SeqList* psl);//聲明 void SeqListInit(SeqList* psl) { assert(psl);//進行斷言是因為當psl為NULL時,下面的操作將無法進行,因為空指針是無法進行解引用的。 psl->a = NULL; psl->size = 0; psl->capacity = 0; }//函數實現
注意:進行斷言是因為當psl為NULL時,下面的操作將無法進行,因為空指針是無法進行解引用的,后面也是如此。
3 順序表的銷毀
void SeqListDestroy(SeqList* psl); void SeqListDestroy(SeqList* psl) { assert(psl); free(psl->a); psl->a = NULL; psl->capacity = psl->size = 0; }
注意:free后面括號中的指針必須是malloc開辟出來的那塊空間,且不能有任何的偏差(即指針不能發生移動)。
下面進行舉例:
像上面這樣使用是完全沒有問題的,但是像下面這樣進行使用就出現了問題:
tmp進行自增操作后,就指向了下圖所示位置:
free的位置是tmp++后的位置,這不符合C語言的規定,且即使正常的釋放掉了,前面的那一塊int空間也將引起內存泄漏問題,即動態開辟的內存忘記釋放。
4 順序表的尾插
void SeqListPushBack(SeqList* psl,SLDataType x);//聲明 void SeqListPushBack(SeqList* psl, SLDataType x) { assert(psl); //如果滿了,就進行擴容 SeqListCheckCapacity(psl); psl->a[psl->size] = x; psl->size++; }
5 順序表的尾刪
void SeqListPopBack(SeqList* psl); void SeqListPopBack(SeqList* psl) { assert(psl); if(psl->size > 0) { psl->size -= 1; } }
6 順序表的頭插
void SeqListPushFront(SeqList* psl, SLDataType x); void SeqListPushFront(SeqList* psl, SLDataType x) { assert(psl); SeqListCheckCapacity(psl); int end = psl->size - 1; while (end >= 0) { psl->a[end+1] = psl->a[end]; --end; } psl->a[0] = x; psl->size++; }
順序表的頭插會涉及到后續元素的移動,頭插時要將順序表中的元素從后面開始進行移動,因為從前面開始移動的話會出現覆蓋現象。下面是圖示:
7 順序表的頭刪
同理,如果想要元素不被覆蓋,就只能從前向后進行移動。
void SeqListPopFront(SeqList* psl); void SeqListPopFront(SeqList* psl) { //挪出數據,騰出頭部空間 //方法一:從1開始移動 /*assert(psl); if (psl->size > 0) { int begin = 1; while (begin < psl->size) { psl->a[begin - 1] = psl->a[begin]; begin++; } psl->size--; }*/ //方法二:從0開始移動 assert(psl); if (psl->size > 0) { int begin = 0; while (begin < psl->size - 1) { psl->a[begin] = psl->a[begin + 1]; begin++; } psl->size--; } }
下圖是兩種移動方式的區別:
問:為什么不可以直接將指針進行加1操作?
- free釋放空間時的指針和malloc開辟空間的指針必須相同
- mallo開辟的空間存在浪費,即0的那塊空間被浪費,無法進行使用,因為那塊空間是被合法申請的。
8 順序表容量的檢查與擴容
void SeqListCheckCapacity(SeqList* psl); void SeqListCheckCapacity(SeqList* psl) { assert(psl); if (psl->capacity == psl->size) { size_t newCapacity = psl->capacity == 0 ? 4 : psl->capacity * 2; SLDataType* tmp = (SLDataType*)realloc(psl->a, sizeof(SLDataType) * newCapacity); if (tmp == NULL) { printf("realloc fail\n"); exit(-1); } else { psl->a = tmp; psl->capacity *= 2; } } }
注意點1:此處考慮使用的是如果容量不夠,就將容量擴容為原容量的兩倍,但是最開始的容量是0,所以要考慮到最開始為0的情況。
注意點2:要對擴容是否成功進行檢測,即判斷剛申請的空間是否為空。
9 順序表任意位置的插入
void SeqListInsert(SeqList* psl,size_t pos,SLDataType x); void SeqListInsert(SeqList* psl, size_t pos, SLDataType x) { assert(psl); //較為溫和的檢查方式 /*if (pos > psl->size) { exit(-1); }*/ assert(pos <= psl->size);//較為暴力的檢查方式 SeqListCheckCapacity(psl); size_t end = psl->size; while (end > pos) { psl->a[end] = psl->a[end-1]; --end; } psl->a[pos] = x; psl->size++; }
注意:
問:為什么end從psl->size開始?
答:此處需要注意的是pos和end的類型,為什么呢?因為兩者都是無符號類型,所以尤其注意當end到了-1的時候,就會變成一個很大的數字,此時如果以此作為下標進行訪問,一定會出現越界訪問內存的情況,考慮一種極端情況,當pos為0的時候,end最小的時候就是為0,所以此時不會出現越界的情況,但是如果end是從psl->size - 1開始的話,那么while循環體內的語句就變成下面這樣:
while(end > pos) { psl->a[end+1] = psl->a[end]; --end; }
最后end的最小值會變成-1,但是因為end是size_t類型,所以會變成一個很大的數字,在whle()循環條件判定時條件始終是滿足的,同時在進入循環體內之后,會出現越界訪問內存的操作。所以兩種情況的圖如下所示:
10 順序表任意位置的刪除
void SeqListErase(SeqList* psl, size_t pos); void SeqListErase(SeqList* psl, size_t pos) { assert(psl); assert(pos <= psl->size); size_t begin = pos+1; while (begin < psl->size) { psl->a[begin-1] = psl->a[begin]; ++begin; } psl->size--; }
11 順序表的打印
void SeqListPrint(SeqList* psl); void SeqListPrint(SeqList* psl) { assert(psl); for (int i = 0; i < psl->size; i++) { printf("%d ", psl->a[i]); } printf("\n"); }
12 順序表元素的查找
int SeqListFind(SeqList* psl,SLDataType x); int SeqListFind(SeqList* psl, SLDataType x) { assert(psl); for (int i = 0; i < psl->size; i++) { if (psl->a[i] == x) return i;//找到了對應元素,返回相應的下標 } return -1;//說明沒有找到對應的元素 }
13 順序表元素的修改
void SeqListModify(SeqList* psl, size_t pos, SLDataType x); void SeqListModify(SeqList* psl, size_t pos, SLDataType x) { assert(psl); assert(pos < psl->size); psl->a[pos] = x; }
原文鏈接:https://blog.csdn.net/m0_57304511/article/details/123460490
相關推薦
- 2022-06-20 Go語言實現切片增刪改查的示例代碼_Golang
- 2022-11-13 C#實現定義一個通用返回值_C#教程
- 2022-10-07 C++結構體中變長數組的使用問題分解刨析_C 語言
- 2022-07-01 Nginx的gzip指令使用小結_nginx
- 2022-06-21 Android?Studio實現登錄界面功能_Android
- 2022-07-04 聯邦學習FedAvg中模型聚合過程的理解分析_其它綜合
- 2022-01-30 element-ui表格的多選框CheckBox 是否可以勾選
- 2022-10-05 Python實現打印彩色字符串的方法詳解_python
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支