链表结构构建:
在我们构建的链表中,我们采用头插法,我们采用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++;
}