顺序表及链表的理解以及基本操作的实现

线性表:线性表是具有相同特性的数据元素的序列。在逻辑上是一条线,但在物理上不一定连续,线性表通常有两种存储方法  

通常以数组或者链式结构形式存储。

顺序表:物理地址连续的一段存储单元 依次存储数据的线性结构,一般情况下采用数组存储,在数组上进行增删查改操作。

顺序表分为静态顺序表动态顺序表。

顺序表的基本操作

顺序表的存储静态存储动态存储,静态存储使用定长数组存储,动态存储使用动态申请内存的方式进行存储。

//静态存储
#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;
	}
}

 

 

 

 

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