tips:
老师代码:
目录
#include<stdio.h>
#include<stdlib.h>
#define OK 1
#define Error 0
typedef int Status
链表是一种链式储存结构,在C语言中有着重要的作用。
分享给大家的同时也自我巩固。
一.链表的概念和定义
相对于线性表,链表有许多优点,例如在线性表中,删除或插入元素的操作很复杂,需要将插入或删除位置的前/后元素不停地移动,倘若涉及到频繁地插入或者删除的操作,链表的优点就凸显出来了。
链表作为一种链式存储结构,其的存储内容除了要存的数据元素信息之外,还储存着后继元素的存储地址。为了表示链表中每一个存储元素a(i)与其直接后继元素a(i+1)的关系,需要存储一个代表该后继元素信息的存储内容,即该元素的地址。
我们把存储当前元素信息的域称为数据域,把存储后继元素信息的域称为指针域。这两部分组合而成的一个存储单元称为节点(Node)。
上图即为一个节点的图示,图示中,data是数据域,next就是指针域。顾名思义,next作为指针域,其存储的就是指针类型的数据,即是一个地址。
对于链表来说,需要有头有尾,因此我们通常定义一个头节点head(自行命名),head的数据域一般不存储信息,其指针与存储着第一个节点的地址。有了头节点,尾巴也不例外。作为链表的尾端,尾节点的指针域当然为空,一般用“NULL”表示。
上图为链表的结构图解,转载自其他博主的博客。
二.链表的建立
要完成一个链表的建立,与线性表相同,链表的存储形式也是结构体。
typedef struct LinkNode {
char data;
struct Node* next;
}LNode,*LinkList;
下面进行完整链表的建立的代码进程
创建单链表的过程就是一个动态生成链表的过程,即从头开始,依次建立结点,并插入链表。
思路如下:
1.声明结点p,以及计数所用数字i。
2.将数字赋值给p->data。
3.将p插入链表。
另外有必要说明的是,链表的创建有头插法和尾插法两种方式,下面我会分别提出。
(1)头插法
顾名思义头插法就是将新结点插入到下一结点的前方。代码如下:
void creat(Linklist head,int n)
{
Linklist p;
int i;
//这里假设head已经被建立且其指针域为NULL
for (i; i < n; i++)
{
p = (Linklist)malloc(sizeof(Node));//生成新结点
scanf("%c", p->data);
p->next = head->next;
head->next = p;//插入到下一个头节点的后面一个结点
}
}
(2)尾插法
与头插法相反,尾插法的插入方式是将新节点插入到目前链表末尾结点的后方。代码如下:
void creat(Linklist head,int n)
{
Linklist p;
int i;
//这里假设head已经被建立且其指针域为NULL
for (i; i < n; i++)
{
p = (Linklist)malloc(sizeof(Node));//生成新结点
scanf("%c", p->data);
head->next = p;
head = p;//插入到尾部
}
}
三.链表元素的读取
思路如下:
1.给出一个结点的位置
2.由于链表的链式存储结构,需要遍历链表至该位置
3.输出该位置的数据信息
代码如下:
void printList(Linklist head,int n)//n代表要读取的结点的位置
{
Linklist t;
t = head;//定义一个移动结点t指向head
for (int i = 1; i <= n&&t!=NULL; i++)
{
t = t->next;
}
if (t != NULL)
{
printf("%c", t->data);
}
else {
printf("结点不存在!");
}
}
四.链表结点的删除
在实际应用内,有建立则必然少不了删除的操作。
与链表的读取相似,删除之前需要将链表先进行遍历。
但是值得思考的是,链表每一个结点都存储着数据域和指针域,那么如何才能完成链表的删除操作呢?
我用图解来讲述这个问题:
一目了然,我们直接将p的指针域指向p->next->next,即可完成一半的删除操作,这时候链表依然是完整的。那我们不禁会想到,q的指针域仍然存在,其指向仍然是q->next,只是结点q已经被排除在链表之外了。这时候我们就需要用到free(q)这个操作来完成对q所占内存空间的释放了。
代码如下:
void deleteElement(Linklist head,int n)//n代表要删除的结点的位置
{
Linklist t;
t = head;//定义一个移动结点t指向head
for (int i = 1; i < n&&t!=NULL; i++)
{
t = t->next;
}
if (t != NULL&&t->next!=NULL)
{
Linklist q = t->next;
t = t->next->next;
free(q);
}
else {
printf("结点不存在!");
}
}
五.链表结点的插入
与删除类似,删除的操作是使链表跳过要删除的结点,而插入是将一个新节点插入到该插入的位置
但是在插入操作时要注意操作顺序。
即先将新节点s的指针域指向a1,再将原本a1之前的结点的指针域指向s。
void insertElement(LinkList head, char data, int n){
LinkList p, q;
// Step 1. Search to the position.
p = head;
for (int i = 0; i < n; i ++) {
p = p->next;
if (p == NULL) {
printf("节点不存在", n);
return;
}
}
q = (LinkList)malloc(sizeof(LNode));
q->data = data;
// Step 3. Now link.
printf("linking\r\n");
q->next = p->next;
p->next = q;
}
整合后的运行结果