数据结构之链表详尽教程

链表结构构建:

在我们构建的链表中,我们采用头插法,我们采用Header作为头结点以便遍历整个链表,采用Node结点作为链表的身体,来进行数据的存储

typedef struct Header{
	int length;
	struct Node * start ;
}H; 
typedef struct Node{
	char data;
	struct Node * next;
}N;

既然现在我们已经熟悉了链表代码结构的初始名称了,那就让我们开始吧!

链表的创建

我们先引入代码

H * createLink(){
	H * h = (H*)malloc(sizeof(H));
	if(!h){
		printf("创建表为空!\n");
		return NULL;
	}
	h->start = NULL;
	h->length = 0;
	return h;
}

 在这段代码中,我们要创建链表的头结点,以便我们以后遍历的时候从头开始

我们开辟了一段内存地址,并将这段内存地址的指针赋给H* h,这样,我们就能操控这段开辟的空间了,然后进行健壮性判断,如果开辟失败的话,提示报错信息并返回NuLL,虽然一般都开辟空间了,下面判断是否为空显得有点多余,但是为1%去排错一直是一个良好的习惯,然后我们对这个头结点进行初始化,将头结点的length设置为0,start设置为空,这两段代码的意思是,我们的头结点什么也没有连接,即此链表只有一个头结点,长度为零,由于我们返回的是H*类型,所以在main函数中我们使用H*类型进行接收头结点返回的地址

对链表进行数据的增添

还是先看代码

void add_data(H* h){
	char data = '0';
	if(!h){
		printf("无法增加值!\n");
		return ;
	}
	printf("请输入你要添加的值,并以#结束输入\n");
	scanf("%c",&data);
	while(data != '#')
	{
		N * n = (N*)malloc(sizeof(N));
		if(!n){
			printf("添加值失败!\n");
			return ;
		}
		n->data = data;
		n->next = h->start;
		h->start = n;
		h->length++;
		scanf("%c",&data);
	}
}

在这段代码中,我们首先要做的第一步就是健壮性判断,一定要确保结构正确的时候再谈数据,如果我们的头结点存在的话,就进行增添数据的操作,否则就结束操作,当然读者可以在此基础上继续进行健壮性判断,例如不存在头结点时是否动态进行头结点的创建,毕竟一个完整的程序就是不断修缮的过程,其次,我们开始进行要存入数据的读取,并且定义以#字符作为输入结束的标志,在这个过程中,

1.我们输入数据

2.创建存储数据的容器,也就是为数据动态开辟一段地址空间,

3.将我们的开辟结点的next指向头结点的next,也就是n->next = h->start,这样我们的新结点的next也就指向null,这样在我们进行结点遍历的时候很方便

4.我们将头结点的start继续指向新结点的头部,通过这样的方式不断地循环,也就是我们的头插法,我们可以不断的进行数据的读入,链表的创建,直到读入#来结束链表的创建,至此,我们的链表就算创建完成了,当然此代码略显拙略,读者可在此代码的启发下进行更加丰富的补充和优化

对链表进行数据的删除

先看代码

void delete_data(H* h,int index){
	if(!h){printf("为空\n");return;}
	if(index < 0 || index > h->length)
	{printf("error\n");return; }
	N * temp = h->start;
	if(index == 1){
		printf("index is %d\n",index);
		printf("temp is %c\t\n",temp->data);
		printf("h is %c\n",h->start->next->data);
		h->start = h->start->next;
		free(temp);
	}else{
	int i = 1;
	N * dummy = NULL;
	while(i<index){
	dummy = temp;//前驱节点 
	temp = temp->next;//目标节点 
	i++;
	}
	dummy->next = temp->next;
	free(temp);
  }
}

还是国际惯例,我们先进行健壮性判断,我们可以把这一步骤抽离出来做一个单独的功能,这里因为代码量太低就不需要那样了,我们上面这个代码的实现过程是这样的,

1.在判断头结点是否为空后我们进行数位判断,我们要确保删除的位置既不能小于零也不能比链表长度长,那样不合法,在进行健壮性判断之后,我们就可以对数据进行处理了

2.N * temp = h->start;通过这一段代码,我们的temp就拿到了头结点下的第一个结点的位置,进行删除操作时,我的逻辑是如果删除的是第一个结点的话,我们就先获取第一个结点的位置,然后将它先存下来,然后将头结点连到第二个结点上面,这样的话,我们就可以将头结点下的第一个结点的位置进行free了

3.若不是1结点,我们采取遍历的规则,遍历到此结点,以及此节点前的一个结点,然后进行如同2般的操作,最后释放index下的指定结点

对链表进行数据的插入

void insert_data(H* h,int index,char data){
	if(!h){
		printf("error\n");
		return;
	}
	if(index<0||index>h->length){
		printf("error\n");
		return;
	}
	N * n = (N*)malloc(sizeof(N));
	memset(n,0,sizeof(*n));
	n->data = data;
	if(index == 1){
		N * temp = h->start;
		h->start = n;
		n->next = temp;
	}else{
		int i = 1,j;
		N * s = h->start;
		N * dummy = NULL;
		while(i<index){
			dummy = s;//前置结点 
			s = s->next;//目标结点 
			//新建结点n 
			i++;
		}
		printf("\n%c %c\n",dummy->data,s->data);
		n->next = s;
		dummy->next = n; 
	}
	h->length++;
}

这段代码是一个C函数,用于在一个链表中的特定位置插入一个新的数据节点。我将逐步解释其功能和逻辑:

1. **函数签名**:
   ```c
   void insert_data(H* h,int index,char data)
   ```
   这意味着该函数没有返回值,它需要三个参数:
   - 一个指向`H`类型的指针`h`,这似乎是链表的头部或包含链表元数据的结构。
   - 一个整数`index`,表示我们要插入新数据的位置。
   - 一个字符`data`,这是我们要插入的数据。

2. **参数检查**:
   首先,函数检查`h`是否为`NULL`,并且检查`index`是否有效。如果不是,则它打印错误消息并返回。

3. **创建新节点**:
   使用`malloc`分配内存来创建一个新的节点`N`。然后使用`memset`函数将其所有字节设置为0。接下来,它设置新节点的`data`字段为传入的`data`。

4. **插入逻辑**:
   - 如果`index`为1,那么新节点将被插入到链表的开头。新节点的`next`字段指向原始的第一个节点。
   - 如果`index`不是1,那么代码将遍历链表,直到找到应该插入新节点的位置。`dummy`表示要插入位置的前一个节点,而`s`是该位置的当前节点。新节点被插入在这两者之间。

5. **增加长度**:
   一旦新节点被正确地插入,链表的`length`字段增加1。

注意事项:
- 该代码似乎有一些问题。例如,如果`index`等于`h->length`,这意味着我们要将新节点添加到链表的末尾。但当前的代码不处理这种情况。另外,当`index`为链表的长度时,`s`可能会为`NULL`,所以当我们尝试访问`s->data`时,它可能会导致错误。
- 代码中使用的数据结构`H`和`N`没有给出,所以我只能基于你提供的代码片段进行假设。`H`可能是包含链表的头节点和长度的结构,而`N`是链表节点结构,包含数据和指向下一个节点的指针。

建议:
如果你在实际项目中使用此代码,我建议进行更多的错误检查和边界情况处理,确保代码的健壮性。

对链表进行数据的遍历(查找同理)

这段代码是一个C函数,用于遍历并打印链表中的数据。下面是该函数的详细解释:

1. **函数签名**:
   ```c
   void print_data(H* h)
   ```
   这个函数没有返回值,它接受一个参数:
   - 一个指向`H`类型的指针`h`,这似乎是链表的头部或包含链表元数据的结构。

2. **检查链表是否为空**:
   如果传入的链表头指针`h`是`NULL`,函数将打印“为空”并返回,表示链表为空或不存在。

3. **遍历链表并打印**:
   函数首先获取链表的起始节点`h->start`并赋值给`n`。然后,使用`while`循环遍历链表,直到达到链表的末尾(即`n`为`NULL`):
   - 在每次循环迭代中,它都会打印当前节点`n`的数据。
   - 然后,将`n`的值更新为`n->next`,即指向下一个节点。

这个函数的主要目的是提供一个简单的方法来查看链表的内容,这在调试或查看链表数据时很有用。

请注意,我们再次假定`H`和`N`是某种已定义的数据结构,其中`H`可能包含对链表的第一个节点的引用,以及其他可能的元数据,而`N`表示链表中的一个节点,它有一个`data`字段(存储字符数据)和一个`next`字段(指向下一个节点的指针)。

完整代码

//头插法 
#include<stdio.h>
#include<stdlib.h> 
typedef struct Header{
	int length;
	struct Node * start ;
}H; 
typedef struct Node{
	char data;
	struct Node * next;
}N;
H * createLink(); 
void add_data(H* h);
void delete_data(H* h,int index);
void insert_data(H* h,int index,char data);
void print_data(H* h);
int main(){
	H * h = createLink();
	add_data(h);
//	delete_data(h,3);
	insert_data(h,2,'u');
	print_data(h);
	return 0;
} 
H * createLink(){
	H * h = (H*)malloc(sizeof(H));
	if(!h){
		printf("创建表为空!\n");
		return NULL;
	}
	h->start = NULL;
	h->length = 0;
	return h;
}
void add_data(H* h){
	char data = '0';
	if(!h){
		printf("无法增加值!\n");
		return ;
	}
	printf("请输入你要添加的值,并以#结束输入\n");
	scanf("%c",&data);
	while(data != '#')
	{
		N * n = (N*)malloc(sizeof(N));
		if(!n){
			printf("添加值失败!\n");
			return ;
		}
		n->data = data;
		n->next = h->start;
		h->start = n;
		h->length++;
		scanf("%c",&data);
	}
}
void print_data(H* h){
	if(!h){
		printf("为空\n");
		return ;
	}
	N * n = h->start;
	while(n){
		printf("%c\t",n->data);
		n = n->next;
	}
}
void delete_data(H* h,int index){
	if(!h){printf("为空\n");return;}
	if(index < 0 || index > h->length)
	{printf("error\n");return; }
	N * temp = h->start;
	if(index == 1){
		printf("index is %d\n",index);
		printf("temp is %c\t\n",temp->data);
		printf("h is %c\n",h->start->next->data);
		h->start = h->start->next;
		free(temp);
	}else{
	int i = 1;
	N * dummy = NULL;
	while(i<index){
	dummy = temp;//前驱节点 
	temp = temp->next;//目标节点 
	i++;
	}
	dummy->next = temp->next;
	free(temp);
  }
}
void insert_data(H* h,int index,char data){
	if(!h){
		printf("error\n");
		return;
	}
	if(index<0||index>h->length){
		printf("error\n");
		return;
	}
	N * n = (N*)malloc(sizeof(N));
	memset(n,0,sizeof(*n));
	n->data = data;
	if(index == 1){
		N * temp = h->start;
		h->start = n;
		n->next = temp;
	}else{
		int i = 1,j;
		N * s = h->start;
		N * dummy = NULL;
		while(i<index){
			dummy = s;//前置结点 
			s = s->next;//目标结点 
			//新建结点n 
			i++;
		}
		printf("\n%c %c\n",dummy->data,s->data);
		n->next = s;
		dummy->next = n; 
	}
	h->length++;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值