C语言与数据结构(2)-单链表的基本操作

上一节我们学习了线性表,线性表的查询效率是最高的,时间复杂度O(1)。但他有一些缺点,比如如果我们要存入1000万个int型的数字,那么就需要一块可以存入1000万int型的连续的空间。而链表则不需要连续的空间也可以存储。

我写代码喜欢把不同功能封装到不同函数中,和王道408书里写的链表代码不一样,但是原理肯定都是一样的。而且关于链表的写法,我看几个人就有几种写法,仁者见仁,智者见智吧。我的下面的代码不一定优雅(可以跑)。

1.构建链表

一个链表的结点有数据域,指向下一个结点的指针域构成。

我们用结构体来构造一个链表结点

typedef struct Node {
	int data;
	struct Node* next;
}Node;

2.初始化链表

我们需要构建一个头结点,头结点是我们找到这个链表的唯一标识。就像数组一样,数组在函数间的传递其实是在传递数组的第一个元素的地址。所以,我们如果要传递一个链表,那就只需要传递链表头结点的指针就可以了。并且,由于单链表的指向单一性,如果头结点丢了,那一定就不能再遍历整个链表了。

//初始化
Node* initInsert() {
	Node* list = (Node*)malloc(sizeof(Node));
	list->data = 0;
	list->next = NULL;
	return list;
}
int main(void) {
	Node *list=initInsert();
	return 0;
}

这段代码就可以构建一个头结点。

3.插入数据

插入数据有三种情况,头插法,尾插法,中间插入。插入一个数据我们就需要构造一个新的结点插入。

头插法

需要注意的是,头插法并不是每插入一个新的结点,就把新结点变成头结点,而是插在头结点和头结点的下一结点中间,就像这样。

因为我刚刚说过头结点是唯一标识,它不能随意变动

//头插法
void headInsert(Node*list,int data) {
	Node* node = (Node*)malloc(sizeof(Node));
	node->data = data;
	node->next = list->next;
	list->next = node;
	list->data++;
}

尾插法

使用头插法会使得插入的数据顺序和输入的顺序刚好相反,例如,你按1,2,3的顺序插入,1反而是离头结点最远的。头->3->2->1 。

如果想要  头->1->2->3,就要使用尾插法。

//尾插法
void BackInsert(Node* list, int data) {
	Node* node = (Node*)malloc(sizeof(Node));
	list->data++;
	node->data = data;
	node->data = NULL;
	Node* p = list->next;
	int i = 0;
	while (1) {
		if (p->next == NULL){
			p->next = node;
			break;
		}
		p = p->next;
	}
}

其实上面这段代码并不是一个好代码,因为它要找到最后一个结点,每次都需要把整个链表遍历一遍,非常憨。这是我第一次写的尾插法代码,并不完美。后来我发现,可以使用尾结点来记录最后一个结点的指针,这样就非常优雅了。我们不能把头结点拿来当尾结点用,因为这样头结点就丢了。所以我们要多传一个结点来当尾结点。

#include<stdio.h>
#include<malloc.h>

typedef struct Node {
	int data;
	struct Node* next;
}Node;

//尾插法
void BackInsert(Node *list) {
	Node* Back = list;
	int n;
	scanf_s("%d", &n);
	for (int i = 0; i <n; i++) {
		Node* node = (Node*)malloc(sizeof(Node));
		Back->next = node;
		node->data = i;
		list->data++;
		Back = node;
	}
}
//初始化
Node* initInsert() {
	Node* list = (Node*)malloc(sizeof(Node));
	list -> data = 0;
	list->next = NULL;
	return list;
}
//遍历链表
void input(Node* list) {
	Node* p = list->next;
	while (p->next != NULL) {
		printf("%d\n", p->data);
		p = p->next;
	}
}
//demo
int main(void) {
	Node* list = initInsert();
	BackInsert(list);
	input(list);
	return 0;
}

中间插入

就比如说,题目要求把2这个数插到链表中里某一个数后面。

原理很简单,就是先把链表遍历,到那个数遍历停下,再把2插进去。和上面一样,就不写代码了。

4.遍历整个链表

//遍历整个链表
void input(Node* list) {
	Node* p = list->next;
	for (int i = 0; i < list->data; i++) {
		printf("%d\n", p->data);
		p = p->next;
	}
}

5.按值删除一个元素

//按值删除一个结点
void delect(Node* list, int data) {
	Node* p = list;
	Node* q = p->next;
	while (1) {
		if (q->data==data) {
			p->next = q->next;
			q = NULL;
			list->data--;
			break;
		}
		p = p->next;
		q = p->next;
	}
}

全部代码

#include<stdio.h>
#include<malloc.h>

typedef struct Node {
	int data;
	struct Node* next;
}Node;

//初始化
Node* initInsert() {
	Node* list = (Node*)malloc(sizeof(Node));
	list->data = 0;
	list->next = NULL;
	return list;
}
Node* initBackInsert() {
	Node* list = (Node*)malloc(sizeof(Node));
	list->data = 0;
	list->next = NULL;
	return list;
}
//头插法
void headInsert(Node*list,int data) {
	Node* node = (Node*)malloc(sizeof(Node));
	node->data = data;
	node->next = list->next;
	list->next = node;
	list->data++;
}
//按值删除一个结点
void delect(Node* list, int data) {
	Node* p = list;
	Node* q = p->next;
	while (1) {
		if (q->data==data) {
			p->next = q->next;
			q = NULL;
			list->data--;
			break;
		}
		p = p->next;
		q = p->next;
	}
}
//遍历整个链表
void input(Node* list) {
	Node* p = list->next;
	for (int i = 0; i < list->data; i++) {
		printf("%d\n", p->data);
		p = p->next;
	}
}
//尾插法
void BackInsert(Node* list, int data) {
	Node* node = (Node*)malloc(sizeof(Node));
	list->data++;
	node->data = data;
	node->data = NULL;
	Node* p = list->next;
	int i = 0;
	while (1) {
		if (p->next == NULL){
			p->next = node;
			break;
		}
		p = p->next;
	}
}

int main(void) {
	Node *list=initInsert();
	for (int i = 1; i <=4; i++)
	{
		headInsert(list, i);
	}
	input(list);
	delect(list, 2);
	input(list);
	return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱吃巧乐兹的源

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值