线性表的链式存储结构
将线性表中各个元素分布在存储器的不同存储块,称为结点,通过地址或指针建立元素之间的联系:
data | next |
结点的data域存放数据元素,而next域是一个指针,指向的直接后继的结点。
线性表的链式存储结点描述
结点类型描述:
typedef struct node
{
data_t data; //结点的数据域//
struct node *next; //结点的后继指针域//
}listnode, *linklist;
设p指向链表中结点:
获取,写作:p->data;
而取 ,写作:p->next->data;
若指针p的值为NULL,则它不指向任何结点,此时获取p->data或者p->next->data会报错,怎么解决这个问题?
有两个解决办法,其中之一:
listnode A; //A在栈上
linklist p = &A;
在这个情况下,如果要给结点赋值,有两种写法,第一种是:
A.data = value;
第二种方法是:
p->data = value;
这种方法定义的A的地址在栈上,随着程序的运行跳出函数或者循环,这个值会被销毁,所占用的内存会被释放,无法做到自己控制,因此,我们要考虑将A放在堆上,通过动态内存的方式管理。
可调用C语言中malloc()函数向系统申请结点的存储空间:
linklist p;
p = (linklist)malloc(sizeof(listnode));
创建一个类型为linklist的结点,且该结点的地址已存入指针变量p中:
此时,给结点赋值的方法是:
p->data = value;
建立单链表
依次读入表中的每个元素(假设为整型),若(-1为结束符),则为创建一个结点,然后插入表尾,最后返回链表的头结点指针H。
设,则建表过程如下:
链表的结构是动态形成的,在算法运行之前,表结构是不存在的。
单链表创建的实现
一、创建一个.h头文件linklist.h
typedef int data_t;
typedef struct node {
data_t data;
struct node *next;
}linknode, *linklist;
//创建头结点
linklist list_create();
//从链表尾部插入新增的数据
int list_tail_insert(linklist P, data_t value);
//输出链表
int list_show(linklist P);
在头文件中,用typedef关键字为数据类型int(可根据需要调整)定义一个新的名字data_t。typedef struct node linknode; //将结构体类型struct node重命名为linknode
typedef struct node *linklist; //将结构体类型struct node *重命名为linklist
声明三个函数:分别实现链表的创建,数据的尾部插入和输出。
二、创建一个.c文件linklist.c
在这个文件中实现链表的创建、尾部插入和输出三个功能。
1、链表创建的实现函数
#include <stdio.h>
#include <stdlib.h>
#include "linklist.h"
//实现list的创建
linklist list_create() {
//声明一个linklist指针
linklist P;
//给指针分配内存空间,如果分配失败,直接返回
if ((P = (linklist)malloc(sizeof(linknode))) == NULL) {
printf("malloc failed.\n");
return P;
}
//赋值,data=0,头结点的next指针为NULL
P->data = 0;
P->next = NULL;
//返回指针P
return P;
}
定义一个结构体指针P,并用malloc为其分配内存,再对数据和指针域赋值,分别是0和NULL,最后返回这个指针P。
2、尾部插入的实现函数
//实现尾部插入
int list_tail_insert(linklist P, data_t value) {
//判断传入的P是否为NULL,如为NULL直接返回
if (P == NULL) {
printf("P is NULL.\n");
return -1;
}
//第一步:创建新结点,并完成赋值
//声明两个指针p,q
linklist q;
linklist p;
//给node指针q分配内存空间
if ((q = (linklist)malloc(sizeof(linknode))) == NULL) {
printf("malloc failed.\n");
return -1;
}
//将传入的value赋值给q指向的data,并将指针P指向的next指向q
q->data = value;
q->next = NULL;
//第二步:移动指针p,直到指向链表P的最后一个结点
//将P的头指针赋值给p,并将p不断后移至所指向的next为NULL
p = P;
while (p->next != NULL) {
p = p->next;
}
//第三步:将指针q赋值给p指向的next,完成插入
p->next = q;
return 0;
}
首先对传入的链表指针P进行判断,如果是NULL,则直接返回。
声明两个linklist指针,分别为p和q,并对q指针用malloc分配空间,然后将传入的value付给q指针所指向的data,并给q指针指向的next赋值为NULL。
因为我们要实现的功能是从尾部插入,所以首先要找到尾部结点,尾部结点的next值为NULL,所以只需要用一个指针p,从P的头指针位置,一直往后移动,移动到p->next为NULL即可。
最后将指针q赋值给p->next,实现尾部插入。
3、链表输出的实现函数
//实现链表的输出
int list_show(linklist P) {
linklist p;
if (P == NULL) {
printf("P is NULL.\n");
return -1;
}
p = P;
while (p->next != NULL) {
printf("%d ", p->next->data);
p = p->next;
}
puts("");
return 0;
}
单链表的输出,其实就是从头指针开始,通过next存储的地址,一直往后遍历,知道next的值为NULL,表明链表结束,依次输出p->next->data即可。
三、创建一个.c文件test.c
这个文件主要作用是调用linklist.c中的各个函数,测试其功能是否满足需求。
#include <stdio.h>
#include <stdlib.h>
#include "linklist.h"
int main(int argc, const char *argv[])
{
linklist P;
int value;
//调用链表头结点创建函数
P = list_create();
//调用scanf函数获取用户输入的值
//调用list_tail_insert函数将用户输入的值插入
printf("input value(input '-1' quit):");
while (1) {
scanf("%d", &value);
if (value == -1)
break;
list_tail_insert(P, value);
printf("input value:");
}
//调用list_show函数显示链表
list_show(P);
return 0;
}
首先调用list_create()函数,创建一个结点,这个结点在链表中被当为头结点。
通过一个while循环,在循环中调用scanf函数实现用户的多次输入,当用户输入-1时跳出循环,通过调用list_tail_insert()函数,将用户输入的数值value插入到链表尾部。
最后,调用list_show()函数输出最终的链表。