線性表:線性表是具有相同特性的數據元素的序列。在邏輯上是一條線,但在物理上不一定連續,線性表通常有兩種存儲方法
通常以數組或者鏈式結構形式存儲。
順序表:物理地址連續的一段存儲單元 依次存儲數據的線性結構,一般情況下采用數組存儲,在數組上進行增刪查改操作。
順序表分爲靜態順序表和動態順序表。
順序表的基本操作:
順序表的存儲:靜態存儲和動態存儲,靜態存儲使用定長數組存儲,動態存儲使用動態申請內存的方式進行存儲。
//靜態存儲
#define CAPACITY 100
typedef struct SeqList {
int arr[CAPACITY];
int size
} Seqlist;
//動態存儲
typedef struct SeqList {
int* arr;
int capacity;
int size;
} SeqList;
動態順序表基本操作的接口:
//初始化
void SeqListInit(SeqList* s, int capacity);
//銷燬
void SeqListDestroy(SeqList* s);
//檢測容量
void CheckCapacity(SeqList* s);
//頭插
void FrontPushSeqList(SeqList* s, int v);
//尾插
void BackPushSeqList(SeqList* s, int v);
//中間插(pos(下標)位置)
void InsertPushSeqList(SeqList* s, int v, int pos);
//頭刪
void FrontPopSeqList(SeqList* s);
//尾刪
void BackPopSeeaList(SeqList* s);
//中間刪(pos(下標)位置)
void ErasePosSeqList(SeqList* s, int pos);
//求順序表長度
int Length(SeqList s);
//查找數組中有沒有v
int Find(SeqList* s, int v);
//刪除第一個遇到的v
void RemoveSeqList(SeqList* s, int v);
//刪除所有元素v
void RemoveAllSeqList(SeqList* s, int v);
動態順序表基本操作的實現:
//順序表的基本操作
//初始化
//首先保證s不爲空,申請一段內存,將size置爲0,s->capacity置爲capacity
void SeqListInit(SeqList* s, int capacity) {
assert(s != NULL);
s->arr = malloc(sizeof(int) * capacity);
if (s->arr != NULL) {
s->size = 0;
s->capacity = capacity;
}
}
//銷燬
//判斷s和s->arr不爲空,然後釋放掉申請的內存,將s->arr置爲空,size置爲0,capacity置爲0
void SeqListDestroy(SeqList* s) {
assert(s != NULL);
assert(s->arr != NULL);
free(s->arr);
s->arr = NULL;
s->size = 0;
s->capacity = 0;
}
//插入時檢查空間是否充足,如果不夠,進行擴容
//在做插入元素操作時判斷空間是否夠用
//如果夠用,什麼都不做,如果不夠,進行擴容,重新申請一塊大的內存,進行搬家
void CheckCapacity(SeqList* s) {
if (s->size < s->capacity) {
return;
}
int newcapacity = 2 * s->capacity;
int* newarr = malloc(sizeof(int)* newcapacity);
assert(newarr != NULL);
for (int i = 0; i < s->size; i++) {
newarr[i] = s->arr[i];
}
free(s->arr);
s->arr = newarr;
s->capacity = newcapacity;
}
//增刪查改
//頭插
void FrontPushSeqList(SeqList* s, int v) {
//檢測空間是否足夠
//如果足夠,將數組所有元素往後挪一格,將arr[0] 改爲v
CheckCapacity(s);
for (int i = s->size; i > 0; i--) {
s->arr[i] = s->arr[i - 1];
}
s->arr[0] = v;
s->size++;
}
//尾插
void BackPushSeqList(SeqList* s, int v) {
//檢測容量是否足夠,
//將arr[size] 改爲 v
//size++
CheckCapacity(s);
s->arr[s->size] = v;
s->size++;
}
//中間插(pos(下標)位置)
void InsertPushSeqList(SeqList* s, int v, int pos) {
//檢測容量是否足夠
//判斷pos 是否合法
//將pos 到size-1 的數往後挪一格,將pos位置改爲v size ++
if (pos < 0 || pos >= s->size) {
return;
}
CheckCapacity(s);
for (int i = s->size; i > pos; i--) {
s->arr[i] = s->arr[i - 1];
}
s->arr[pos] = v;
s->size++;
}
//頭刪
//判斷數組不爲空,
//將下標爲[1,size-1]的數往前挪一格,size--
void FrontPopSeqList(SeqList* s) {
assert(s != NULL);
for (int i = 0; i < s->size - 1; ++i) {
s->arr[i] = s->arr[i + 1];
}
s->size--;
}
//尾刪
//判斷數組不爲空,size--
void BackPopSeeaList(SeqList* s) {
assert(s != NULL);
s->size--;
}
//中間刪(pos(下標)位置)
//判斷pos是否合法
//判斷s是否爲空
void ErasePosSeqList(SeqList* s, int pos) {
if (pos < 0 || pos >= s->size) {
return;
}
assert(s != NULL);
for (int i = pos; i < s->size - 1; i++) {
s->arr[i] = s->arr[i + 1];
}
s->size--;
}
//求順序表長度
int Length(SeqList s) {
return s.size;
}
//查找數組中有沒有v
//如果沒有,返回-1,
//如果有,返回下標
int Find(SeqList* s,int v) {
for (int i = 0; i < s->size; i++) {
if (s->arr[i] == v) {
return i;
}
}
return -1;
}
//刪除第一個遇到的v
//如果沒有,返回
//如果有,刪除第一次遇到的結束
void RemoveSeqList(SeqList* s, int v) {
assert(s != NULL);
for (int i = 0; i < s->size; i++) {
if (s->arr[i] == v) {
ErasePosSeqList(s, i);
break;
}
}
}
//刪除所有元素v
void RemoveAllSeqList(SeqList* s, int v) {
assert(s != NULL);
for (int i = 0; i < s->size; i++) {
if (s->arr[i] == v) {
ErasePosSeqList(s, i);
}
}
}
鏈表:是一種邏輯連續,物理存儲不連續的結構,數據元素的邏輯順序是通過鏈表的指針連接次序實現的。
實際中鏈表有多種,一下幾種情況組合起來就有8種:
1,帶頭,不帶頭
2,循環,不循環
3,單鏈表,雙鏈表
雖然鏈表有這麼多結構,實際中常用的有兩種:無頭單向非循環,帶頭雙向循環。
(1)無頭單向非循環鏈表增刪查改的實現
typedef struct Node {
int value;
struct Node* next;
} Node; //鏈表的結點類型
//初始化 ***初始化,一級指針有點問題,
//將第一個結點置爲空
void InitLinkList(Node** first) {
*first = NULL;
}
//銷燬
//循環遍歷所有結點,進行釋放
//可以使用一級指針,可以使用二級指針,本質是在操作結點
void DestroyLinkList(Node* first) {
assert(first != NULL);
Node* next;
for (Node* cur = first; cur != NULL; cur = next) {
next = cur->next;
free(cur);
}
first->next = NULL;
}
//頭插 ***不能使用一級指針,一級指針相當於鏈表的一份拷貝,在函數體內操作,對外部沒影響
//申請一個新的結點,值該爲v 將該結點的下一個next改爲原來的第一個結點
//最後將第一個結點改爲申請的新的結點
void FrontPushLinkList(Node** first, int v) {
Node* node = (Node*)malloc(sizeof(Node));
node->value = v;
node->next = *first;
*first = node;
}
//尾插 ***對非頭結點進行操作,可以用二級指針,也可以用一級指針,
//循環遍歷找到最後一個結點,將最後一個結點的next置爲node
void BackPushLinkList1(Node* first, int v) {
Node* node = (Node*)malloc(sizeof(Node));
node->value = v;
node->next = NULL;
Node* cur = first;
while (cur->next != NULL) {
cur = cur->next;
}
cur->next = node;
}
void BackPushLinkList2(Node** first, int v) {
Node* node = (Node*)malloc(sizeof(Node));
node->value = v;
node->next = NULL;
Node* cur = *first;
while (cur->next != NULL) {
cur = cur->next;
}
cur->next = node;
}
//中間插:在第pos個結點後面位置做插入操作,
//使用if判斷,若爲空就調用頭插,操作完直接return,使用二級指針
//一級指針不能處理參數爲NULL時的情況
void InsertPushLinkList1(Node** pos, int v) {
if (*pos == NULL) {
FrontPushLinkList(pos, v);
return;
}
Node* node = (Node*)malloc(sizeof(Node));
node->value = v;
node->next = (*pos)->next;
(*pos)->next = node;
}
//在pos前面插入一個結點
//pos保證是鏈表中的結點
void InsertPushLinkList2(Node** first, Node** pos, int v) {
if (*pos == NULL) {
FrontPushLinkList(pos, v);
}
Node* node = (Node*)malloc(sizeof(Node));
node->value = v;
Node* cur = *first;
while (cur->next != *pos) {
cur = cur->next;
}
Node* next = cur->next;
cur->next = node;
node->next = next;
}
//頭刪
//頭刪首先判斷鏈表不爲空,
//記錄第二個結點的地址,釋放掉第一個結點,將第二個結點改爲第一個結點
// ***不可以用一級指針,只能用二級指針
void FrontPopLinkList(Node** first) {
assert(*first != NULL);
Node* next = (*first)->next;
free(*first);
*first = next;
}
//需要修改指針的指向,所以不能用一級指針
//void FrontPopLinkList2(Node* first) {
// assert(first != NULL);
// Node* next = first->next;
// free(first);
// first = next;
//}
//尾刪
//判斷鏈表是否有結點,
//判斷鏈表結點是否爲一個,
//循環找到倒數第二個結點,釋放掉最後一個結點的空間,將倒數第二個結點的next改爲NULL
// ***一級指針,二級指針都可以
void BackPopLinkList2(Node** first) {
assert(*first != NULL);
if ((*first)->next == NULL) {
free(*first);
(*first) = NULL;
return;
}
Node* cur = *first;
while (cur->next->next != NULL) {
cur = cur->next;
}
free(cur->next);
cur->next = NULL;
}
void BackPopLinkList1(Node* first) {
assert(first != NULL);
if (first->next == NULL) {
free(first);
first = NULL;
return;
}
Node* cur = first;
while (cur->next->next != NULL) {
cur = cur->next;
}
free(cur->next);
cur->next = NULL;
}
//中間刪
//pos保證是鏈表中的結點
//刪除pos結點後面的結點
void ListErase(Node* pos) {
Node* next = pos->next;
pos->next = pos->next->next;
free(next);
}
//修改
//鏈表中如果有結點的值==v 就將該值改爲num
void ModifyLinkList(Node* first,int v,int num) {
if (first == NULL) {
return;
}
Node* cur = first;
while (cur != NULL) {
if (cur->value == v) {
cur->value = num;
}
cur = cur->next;
}
}
//查找v
int Findv(Node* first, int v) {
if (first == NULL) {
return -1;
}
Node* cur = first;
while(cur != NULL) {
if (cur->value == v) {
return 1;
}
cur = cur->next;
}
return -1;
}
//刪除第一個遇到的v
//先判斷第一個結點的值,如果第一個結點的值是v,直接頭刪,返回
//如果不是,循環遍歷鏈表,遇到v刪除,返回,沒遇到就一直遍歷到結束
void RemoveFirstV(Node** first, int v) {
assert(*first != NULL);
if ((*first)->value == v) {
Node* next = (*first)->next;
free(*first);
*first = NULL;
return;
}
Node* cur = *first;
while (cur->next != NULL) {
if (cur->next->value == v) {
Node* next = cur->next;
cur->next = cur->next->next;
free(next);
return;
}
cur = cur->next;
}
}
//刪除所有的v
//先從第二個開始遍歷,如果遇到v就刪除,刪除完之後使用原值繼續試一次,如果沒有,繼續往下走,
//保證每一個結點都被比較了一次
//否則,容易出問題
//最後,比較第一個結點的值,如果是v,頭刪,
//如果不是,返回,結束
void RemoveFirstVAll(Node** first, int v) {
assert(*first != NULL);
Node* cur = *first;
while (cur->next!= NULL) {
if (cur->next->value == v) {
Node* next = cur->next;
cur->next = cur->next->next;
free(next);
continue;
}
cur = cur->next;
}
if ((*first)->value == v) {
Node* next = (*first)->next;
free(*first);
(*first) = next;
}
}