实现对链表的增删查改

实现对链表的增删查改

链表
链表:逻辑上连续 物理上不连续 不是一条线 而是一环扣一环
每一个数据单元中不光有数据 还有指针 指向下一个元素的起始位置
一个数据单元包括一个数据和指针, 数据单元不是挨着的, 通过指针连接下一个数据, 都相当于是一个结构体, 所以是逻辑连续, 物理不一定连续的线性表, 一个数据单元是结构体(单向)
有8种链表结构
常用的有2种:

  1. 单链表不带头非循环链表( 做底层结构的实现(栈的底层实现) )
  2. 双链表带头循环结构
    Node为结点(一个数据单元) sList表示整个单链表(本次实现的为无头非循环单链表)
#include <stdio.h>
#include <windows.h>

typedef struct Node{// 定义一个结点
	int _data;// 数据
	struct Node* _next;// 指向下一个指针
}Node;// 数据单元用结构体表示
// 单链表不需要记录每个节点 记录一个头(第一个节点的起始位置)

// 实现无头非循环单链表(所以一开始是一个空链表)
// (成员为头结点)
typedef struct sList{
	Node* _head;// 单链表只需要记录第一个结点的起始位置
}sList;

// 单链表初始化, 放的是结构体(sList本身也需要给一个结构体)
void sListInit(sList* sl){
	// 空链表
	// 新的头指向新的节点 指向newNode 不指向原来的节点 指向新的
	// 先创建新节点
	sl->_head = NULL;// 让头指向NULL(无效地址)
}

// 打印
void printsList(sList* sl){
	Node* cur = sl->_head;
	while (cur){
		printf("%d ", cur->_data);
		cur = cur->_next;
	}
	printf("\n");
}

//创建新的结点(进行封装)
//没必要传指针, 新结点指向空就行了
Node* createNode(int data){
	// 先申请空间
	Node* node = (Node*)malloc(sizeof(Node));
	node->_data = data;
	node->_next = NULL;
	return node;
}

// 头插
// 让单链表的头(sL->head)不在指向原来的第一个结点, 而指向newNode ,让newNoded->next指向之前的第一个结点
void sListPushFront(sList* sl, int data){
	// 每创建一个node(新结点) next都指向NULL
	Node* node = createNode(data);

	// 判断原始链表是否为空 由于链表进行了初始化, 所以第一次会进if(也只有第一次进if)
	// 如果原始链表为空, 就让新结点node作为我们的头(变成只有一个节点的链表)
	if (sl->_head == NULL){
		sl->_head = node;
	}
	else{
		// 头插
		// sl->head原来指向的就是链表的第一个结点 赋给新结点的next后再指向新的结点
		node->_next = sl->_head;
		sl->_head = node;
	}
}

// 头删(删除头结点)修改头指针的指向
// 让 sl->head 不再指向原始的第一个结点 指向第一个结点的下一个结点, 然后释放原始头结点
// 首先把原来的头保存下来,         node = sl->_head
// 再让sl->_head 指向下一个结点    sl->head = sl->_head->_next,
// 再释放原始头结点(保存在node里)  free(node)
void sListPopFront(sList* sl){
	Node* node = (Node*)malloc(sizeof(Node));// head的类型为Node* 所以用Node*定义node
	// 空表不需要删除
	if (sl->_head == NULL)
		return;
	node = sl->_head;
	sl->_head = sl->_head->_next;
	free(node);

}

/*尾插
给定一个新结点(newNode),让最后一个结点的next指向newNode, 而newNode的next指向NULL
首先应该找到最后一个非空的结点,
定义一个指针cur 将链表遍历一遍, 只要cur->_next不为NULL, 就继续向后走
当cur->next为NULL时, 在cur后面插入新结点,*/
void sListPushBack(sList* sl, int data){

	// 找到最后一个结点
	Node* cur = sl->_head;
	// 判断是否为空链表
	if (cur == NULL){
		sl->_head = createNode(data);
		return;
	}
	// 若不为空链表 就让cur往后走, 直到指向NULL.
	while (cur->_next){
		cur = cur->_next;// 向后走一步
	}
	// 到这里cur指向了最后一个非空结点
	cur->_next = createNode(data);

}

// 尾删
// 删除最后一个节点后, 要让最后一个结点的前一个结点(前驱)->next = NULL,
// 设置3个指针: sl->head, cur(当前), prev(前驱, 一开始可以使一个NULL的位置, )
// 当cur指向第二个时, prev指向第一个,cur往前移动一位,prev跟着往前一位
// 如果cur的next不为NULL就一直走,当cur->next = NULL时,要删掉cur
// 让prev->next不再指向cur,指向NULL
// 如果链表中只有一个结点的话 只需要让sl->head = NULL 就可以了,
void sListPopBack(sList* sl){
	Node* cur, *prev;
	// 空链表
	if (sl->_head == NULL)
		return;
	// 不是空链表 但是只有一个头结点
	if (sl->_head->_next == NULL){
		// 直接把头free后就相当于删除头结点, 之后变成空链表
		sl->_head = NULL;
		free(sl->_head);
	}
	// 有多个结点
	else{
		prev = NULL;
		cur = sl->_head;
		while (cur->_next){
			prev = cur;
			cur = cur->_next;
		}
		if (prev == NULL){
			sl->_head = NULL;
		}
		// cur指向最后一个结点, 让前驱指向NULL
		prev->_next = NULL;
		free(cur);
	}
}

// 在单链表中找一个值
// 应返回在当前链表的位置 所以返回值为Node*
Node* sListFind(sList* sl, int data){
	Node* cur = sl->_head;
	while (cur){
		if (cur->_data == data)
			return cur;
		cur = cur->_next;
	}
	return NULL;
}

// 任意位置后插入
// 要在单链表前面插, 要遍历单链表,O(N)
// 先保存前驱原始的next, 让前驱的next指向新的结点, 新的结点的next指向前驱的原始next, 

// 单链表一般在结点后面插入,时间复杂度为O(1), 
// 假设在node结点后插一个newNode, 
// 则node->next指向newNode,让newNode->next指向原来node的下一个结点
// (先拿到node原始插入前的next 保存起来)    next = node->_next
// 再让node的next指向newNode                node->_next = newNode
// 再让newNode的next指向node原始的next      newNode->_next = next
// 若为尾插, 则node->_next为NULL
// 给定一个任意(有效)结点(Node* node), 后面插入的数据(int data)
void sListInsertAfter(Node* node, int data){
	// 先判断结点是否有效
	if (node != NULL){
		Node* next = node->_next;
		// 创建新结点
		Node* newNode = createNode(data);
		node->_next = newNode;
		newNode->_next = next;
	}
}

// 任意位置删除
// 先把指定结点->_next保存起来, 将该结点的next指向
// 时间复杂度为O(1),
void slisteraseafter(Node* node){
	if (node != NULL){
		if (node->_next){
			 Node* next = node->_next;
			 node->_next = next->_next;
			 // node->_next = node->_next->_next; 只进行这种操作会造成内存泄漏
			 free(next);
		}
	}
}

// 单链表销毁(这些结点都是malloc的, 要还给系统,每个都free掉
// 如果直接删除头结点, 相当于后面的结点都没有释放掉
// 先拿到cur的next, 再把cur free掉, 再让cur指向next,逐个free掉,
void sListDestory(sList* sl){
	if (sl->_head){
		Node* cur = sl->_head;
		while (cur){
			Node* next = cur->_next;
			free(cur);
			cur = next;
		}
		sl->_head = NULL;// 链表置空
	}
	//sl->_head = NULL; 同样只进行这种操作会造成内存泄漏
}

// 测试函数
void testsList(){
	sList sl;// 定义链表
	sListInit(&sl);// 初始化(链表置空)
	sListPushFront(&sl, 1);
	sListPushFront(&sl, 2);
	sListPushFront(&sl, 3);
	sListPushFront(&sl, 4);
	sListPushFront(&sl, 5);
	sListPushFront(&sl, 6);// 尾插
	sListPopFront(&sl);// 头删
	sListPopBack(&sl);// 尾删
	printsList(&sl);// 打印
}
void test(){
	Node* cur;
	sList sl;
	sListInit(&sl);
	sListPushBack(&sl, 1);
	sListPushBack(&sl, 2);
	sListPushBack(&sl, 3);
	sListPushBack(&sl, 4);
	sListPushBack(&sl, 5);
	sListPushBack(&sl, 6);
	printsList(&sl);
	// 在中间某个结点后面插入数据
	cur = sListFind(&sl, 4);
	printf("4 after: 100\n");
	sListInsertAfter(cur, 100);
	printsList(&sl);
	// 在头结点后面插入数据
	cur = sListFind(&sl, 1);
	printf("1 after: 200\n");
	sListInsertAfter(cur, 200);
	printsList(&sl);
	// 在最后一个结点后面插入数据
	cur = sListFind(&sl, 6);
	printf("6 after: 300\n");
	sListInsertAfter(cur, 300);
	printsList(&sl);
}
void test1(){
	Node* cur;
	sList sl;
	sListInit(&sl);
	sListPushBack(&sl, 1);
	sListPushBack(&sl, 2);
	sListPushBack(&sl, 3);
	sListPushBack(&sl, 4);
	sListPushBack(&sl, 5);
	sListPushBack(&sl, 6);
	printsList(&sl);
	cur = sListFind(&sl, 4);
	printf("Erase 4 after: \n");
	slisteraseafter(cur);
	printsList(&sl);

	cur = sListFind(&sl, 1);
	printf("Erase 1 after: \n");
	slisteraseafter(cur);
	printsList(&sl);
}
void test2(){
	Node* cur;
	sList sl;
	sListInit(&sl);
	sListPushBack(&sl, 1);
	sListPushBack(&sl, 2);
	sListPushBack(&sl, 3);
	sListPushBack(&sl, 4);
	sListPushBack(&sl, 5);
	sListPushBack(&sl, 6);
	printsList(&sl);
	sListDestory(&sl);
	printsList(&sl);
	cur = sListFind(&sl, 4);
	printsList(&sl);
}
int main(){
	//testsList();
	//test();
	//test1();
	test2();
	system("pause");
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值