博主声明:
转载请在开头附加本文链接及作者信息,并标记为转载。本文由博主 威威喵 原创,请多支持与指教。
本文首发于此 博主:威威喵 | 博客主页:https://blog.youkuaiyun.com/smile_running
链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表的数据结构包含两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。如下图所示:
如图中,Data 保存的就是数据域,地址保存的就是指针域,它指向的是下一个结点。用代码的方式表示呢,它的数据结构类型为这样的:
typedef struct Node
{
int data; // 数据域
struct Node *next; // 指针域,指向下一个节点
}LinkList;
使用链式结构的好处就是无需知道其数据的大小,比如顺序结构需要预先知道数据大小,这是它的一个优点。链式结构可以充分利用计算机内存空间,实现灵活的内存动态管理。那么如何初始化一个链表呢?
一、头插法
初始化链表的方式有两种:一种是头插法,另一种是尾插法。顾名思义,头插法就是将新来的数据添加到已存在的结点之前,比如以下的图例:
此时,链表的输出结果与插入时的结果正好是倒序的,以下是头插法的实现代码:
/** 建立链表 - 头插法**/
LinkList *CreateHead(int length)
{
int i=0;
LinkList *head, *node, *end; // 头节点、中间节点、尾节点
head = (LinkList *)malloc(sizeof(LinkList));
head->data = length; // 头节点数据保存链表的长度
head->next = NULL;
end = head;
while(i<length)
{
node = (LinkList *)malloc(sizeof(LinkList));
printf("插入整数: \n");
scanf("%d", &node->data);
node->next = end;
end = node;
i++;
}
return end;
}
头插法是将输入的值插到已经存在的结点之前,所以头插法的输出结果是倒序的,如下:
二、尾插法
对于头插法,做一个简单的了解即可,因为我们在实际中插入的顺序和输出的顺序一般都是一致的,否则看着也非常别扭。下面来看看尾插法的做法吧。尾插法其实就是将新的数据插入到已经存在的结点之后,保证了插入时的顺序,如下图:
尾插法的关键代码如下:
/** 建立链表 - 尾插法**/
LinkList *CreateStern(int length)
{
int i=0;
LinkList *head, *node, *end; // 头节点、中间节点、尾节点
head = (LinkList *)malloc(sizeof(LinkList));
head->data = length; // 头节点数据保存链表的长度
end = head;
while(i<length)
{
node = (LinkList *)malloc(sizeof(LinkList));
printf("插入整数: \n");
scanf("%d", &node->data);
end->next = node;
end = node;
i++;
}
end->next = NULL;
//返回头节点指针
return head;
}
接着,我们来看看如何向一个已经存在的链表中插入新的结点。其做法是:定义一个 node 结点,保存所插入的值,然后 node 的指针域保存的是 L>next 结点。L>next 结点的指针域就不再是之前的了,而是刚刚插入的最新的 node 结点,如下图所示:
其插入结点的代码如下:
/** 向链表中插入元素 **/
Status Insert(LinkList *list, int data, int pos)
{
int index = 1;
LinkList *node;
if(pos<1 || pos>Length(list)+1)
{
printf("下标异常!\n");
return ERROR;
}
node = (LinkList *)malloc(sizeof(LinkList));
list->data++;// 表长 + 1
while(index<pos && list!=NULL)
{
list = list->next;
index++;
}
node->data = data;
node->next = list->next;
list->next = node;
printf("插入元素:%d 成功!\n", data);
return OK;
}
删除就比较简单了,把 L->next 指针域指向 L->next->next 即可,如下图:
其代码如下:
/** 删除链表中的元素 **/
Status Delete(LinkList *list, int pos)
{
int index = 1;
LinkList *node;
if(pos<1 || pos>Length(list)+1)
{
printf("下标异常!\n");
return ERROR;
}
list->data--; // 表长 -1
while(index<pos && list!=NULL)
{
list = list->next;
index++;
}
node = list->next;
list->next = node->next;
printf("删除元素成功!\n");
return OK;
}
下面是尾插法构建一个链表的完整代码,包括插入和删除结点、打印结点、获取链表的长度等操作。代码如下:
#include <stdio.h>
#include <stdlib.h>
#define OK 1
#define ERROR 2
typedef int Status;
typedef struct Node
{
int data; // 数据域
struct Node *next; // 指针域,指向下一个节点
}LinkList;
/** 建立链表 - 尾插法**/
LinkList *CreateStern(int length)
{
int i=0;
LinkList *head, *node, *end; // 头节点、中间节点、尾节点
head = (LinkList *)malloc(sizeof(LinkList));
head->data = length; // 头节点数据保存链表的长度
end = head;
while(i<length)
{
node = (LinkList *)malloc(sizeof(LinkList));
printf("插入整数: \n");
scanf("%d", &node->data);
end->next = node;
end = node;
i++;
}
end->next = NULL;
//返回头节点指针
return head;
}
/** 向链表中插入元素 **/
Status Insert(LinkList *list, int data, int pos)
{
int index = 1;
LinkList *node;
if(pos<1 || pos>Length(list)+1)
{
printf("下标异常!\n");
return ERROR;
}
node = (LinkList *)malloc(sizeof(LinkList));
list->data++;// 表长 + 1
while(index<pos && list!=NULL)
{
list = list->next;
index++;
}
node->data = data;
node->next = list->next;
list->next = node;
printf("插入元素:%d 成功!\n", data);
return OK;
}
/** 删除链表中的元素 **/
Status Delete(LinkList *list, int pos)
{
int index = 1;
LinkList *node;
if(pos<1 || pos>Length(list)+1)
{
printf("下标异常!\n");
return ERROR;
}
list->data--; // 表长 -1
while(index<pos && list!=NULL)
{
list = list->next;
index++;
}
node = list->next;
list->next = node->next;
printf("删除元素成功!\n");
return OK;
}
/** 摧毁链表 **/
void Destory(LinkList *list)
{
LinkList *head;
while(head!=NULL)
{
head = list->next;
free(list);
list = head;
}
}
/** 打印链表 **/
void Print(LinkList *list)
{
printf("链表:");
while(list->next != NULL)
{
list = list->next;
printf("%d ", list->data);
}
printf("\n");
}
/** 获取链表的长度 **/
int Length(LinkList *list)
{
// 返回头节点的数据域
return list->data;
}
void main()
{
LinkList *list;
int len;
printf("请输入链表元素个数:");
scanf("%d", &len);
list = CreateStern(len);// 初始化链表
Print(list);// 打印链表
printf("链表长度:%d \n", Length(list));
Insert(list, 45, 5);// 插入元素
Print(list);// 打印链表
printf("链表长度:%d \n", Length(list));
Delete(list, 1);
Print(list);// 打印链表
printf("链表长度:%d \n", Length(list));
Destory(list);
printf("链表长度:%d \n", Length(list));
}
其运行结果如下: