順序表及鏈表的理解以及基本操作的實現

線性表:線性表是具有相同特性的數據元素的序列。在邏輯上是一條線,但在物理上不一定連續,線性表通常有兩種存儲方法  

通常以數組或者鏈式結構形式存儲。

順序表:物理地址連續的一段存儲單元 依次存儲數據的線性結構,一般情況下采用數組存儲,在數組上進行增刪查改操作。

順序表分爲靜態順序表動態順序表。

順序表的基本操作

順序表的存儲靜態存儲動態存儲,靜態存儲使用定長數組存儲,動態存儲使用動態申請內存的方式進行存儲。

//靜態存儲
#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;
	}
}

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章