在单链表中的一个节点,其结构由两部分构成,分别为数据域和指针域,数据域的数据类型可以为任意类型,用来存放当前节点需要存储的数据元素,可以是任意类型,指针域的数据类型是指针,用来存储下一个节点的地址,如果没有下一个节点,则指针域赋值NULL,节点的示意图如下:
链表具有如下优点:
克服数组链表需要预先知道数据大小的缺点;
链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理;
链表允许在任意位置上插入和删除节点;
但是链表也存在一定的缺陷:
链表失去了数组随机存取的优点;
链表增加了指针域,使得其空间开销较大;
无法随机存取,存取元素时需要遍历链表,比较耗费时间;
本次将使用代码演示如何创建链表,以及对链表上节点的插入、删除、打印。
代码展示:
List.c
#include <stdio.h>
#include <stdlib.h>
#include "List.h"
//创建一个带头结点的空单链表
//只有一个头结点,没有数据结点
List* CreateList(void)
{
List *l = (List*) malloc(sizeof(List)) ;
l->first = NULL;
l->last = NULL;
}
/*
Insert: 往带头结点的单链表l(升序),插入一个数据x
要求插入后 l仍然升序
@l: 头结点
@x: 待插入的元素x
返回值:
无。
*/
void Insert(List* l, DataType x)
{
if (l == NULL)
{
return ;
}
//创建一个“数据结点”,来保存x
Node *p = (Node*) malloc(sizeof(Node));
p->data = x;
p->next = NULL;
//把数据结点 p,插入到 l指向的链表中去。
if (l->first == NULL)
{
l->first = p;
l->last = p;
}
else
{
//1. 先找到插入位置
// 从第一个数据结点开始,遍历,找到第一个值比p值大的结点 pk(及pk前面的那个结点pr),
// pk的前面就是 插入位置
Node *pk = NULL;//指向第一个值比p大的结点,“pk前面就是插入位置”
Node *pr = NULL; //指向pk前面的那个结点
pk = l->first;
while (pk)
{
if (pk->data > p->data)
{
break;
}
pr = pk;
pk = pk->next;
}
//2. 分情况完成插入操作
if (l->first->data > p->data ) // pr == NULL //(第一个结点就比p要大)
{
p->next = l->first;
l->first = p;
}
else if (l->last->data < p->data) // pk == NULL //(链表上所有结点的值都比p要小)
{
l->last->next = p;
l->last = p;
}
else //剩下的情况
{
// “中间插入”
p->next = pk;
pr->next = p;
}
}
}
//打印链表上每一个数据结点的值
void PrintList(List *l)
{
if (l == NULL)
{
return ;
}
Node *p = l->first;
while (p)
{
printf("%d ", p->data);
p = p->next;
}
}
/*
DeleteX: 在一个链表l上,删除值为x的结点
@l: 指向链表的头结点
@x:要删除的结点的值
返回值:
无。
*/
void DeleteX(List*l, DataType x)
{
if (l == NULL || l->first == NULL)
{
return ;
}
// 1. 找要到删除的结点px
Node *px ; //指向要删除的结点
Node *pr; //指向 px前面的那个结点
Node *ps = l->first; //每次查找的起始结点!!!
while (1)
{
px = ps;
while (px)
{
if (px->data == x)
{
break;
}
pr = px;
px = px->next;
}
if (px == NULL) //没找到 要删除的结点
{
return;
}
ps = px->next; //下一次的查找起始结点应该是 px的下一个!!!
// 2. 分情况完成删除操作
if (px == l->first) //(删除的是第一个数据结点)
{
if (l->first == l->last)// (只有一个数据结点)
{
l->first = NULL;
l->last = NULL;
free(px);
}
else
{
l->first = px->next;
px->next = NULL;
free(px);
}
}
else if (px == l->last) //(删除的是最后一个数据结点)
{
pr->next = NULL;
l->last = pr;
free(px);
}
else //删除的是中间结点
{
pr->next = px->next;
px->next = NULL;
free(px);
}
}
}
/*
DeleteList: 删除整个链表
要删除每一个数据结点,并且头结点也要删除
@l: 头结点
返回值:
无。
*/
void DeleteList(List *l)
{
if (l == NULL)
{
return ;
}
Node *px;
//1. 删除所有的数据结点
while (px = l->first)
{
l->first = px->next;
px->next = NULL;
free(px);
}
//2. 删除头结点
free(l);
}
List.h
#ifndef __LIST_H__
#define __LIST_H__
typedef int DataType;
typedef struct Node
{
DataType data; // "数据域":保存数据元素的
struct Node* next; // "指针域":保存下一个数据元素的地址
}Node;
typedef struct List
{
Node *first; //指向链表上的第一个“数据结点”
Node *last ; //指向链表上的最后一个“数据结点”
// ...
}List;
//创建一个带头结点的空单链表
//只有一个头结点,没有数据结点
List* CreateList(void);
/*
Insert: 往带头结点的单链表l(升序),插入一个数据x
要求插入后 l仍然升序
@l: 头结点
@x: 待插入的元素x
返回值:
无。
*/
void Insert(List* l, DataType x);
//打印链表上每一个数据结点的值
void PrintList(List *l);
/*
DeleteX: 在一个链表l上,删除值为x的结点
@l: 指向链表的头结点
@x:要删除的结点的值
返回值:
无。
*/
void DeleteX(List*l, DataType x);
/*
DeleteList: 删除整个链表
要删除每一个数据结点,并且头结点也要删除
@l: 头结点
返回值:
无。
*/
void DeleteList(List *l);
#endif
测试范例:
main.c
#include <stdio.h>
#include "List.h"
int main()
{
int d;
List *l = CreateList();
while (1)
{
scanf("%d", &d);
if (d == 0)
{
break;
}
Insert(l, d);
}
PrintList(l);
}