1. 单链表基本概念
链式存储是最常用的动态存储方法,通常将采用链式存储结构的线性表称为线性链表。从链接方式看,链表可分为单链表、循环链表和双链表。从实现角度看,链表可分为动态链表和静态链表。
链表用一组任意的存储单元来存放线性表的结点,链表中结点的逻辑顺序和物理顺序不一定相同,为了正确表示结点间的逻辑关系,必须在存储线性表的每个数据元素值的同时,存储指示其后继结点的地址(或位置)。这两部分信息组成的存储映像称为结点(Node)。
结点包括两个域:数据域用来存储结点的值,指针域用来存储数据元素的直接后继的地址(或位置)。线性表正是通过每个结点的指针域将线性表的n个结点按其逻辑顺序链接在一起的。由于此线性链表的每个结点只有一个next指针域,故将这种链表称为单链表。
单链表中每个结点的存储地址存放在其前驱结点的指针域中,由于线性表中的第一个结点无前驱,所以应设一个头指针H指向第一个结点。由于线性表的最后一个结点没有直接后继,则指定单链表的最后一个结点的指针域为“空”(NULL)。
单链表的基本运算包括初始化、建表(头插法/尾插法)、输出、结点查找(按值/按序号)、求表长、逆置、插入、删除、升序排序和升序合并等。
2. 完整代码
/*
线性表的链式存储结构(单链表)及其基本运算
包括单链表初始化,建表(头插法、尾插法),输出,查找结点(按值、按序号),求表长,逆置,插入,删除,升序排序,升序合并两个单链表等
*/
# include<stdio.h>
# include<malloc.h>
# define ERROR 0
# define OK 1
typedef int ElemType;
/*单链表的存储结构*/
/*L是单链表的头指针,它指向表中第一个结点(对于带头结点的单链表,则指向单链表的头结点),若L==NULL(对于带头结点的单链表为L->next==NULL)表达式为真,则表示单链表为一个空表,其长度为0。
若是非空表,则可以通过头指针L访问表中结点,从而找到要访问的所有结点的数据信息。
例如,对于带头结点的单链表L,令p=L->next,则p指向表中的第一个元素结点(也称首元结点),通过p->data就可以访问到表中第一个元素的数据值。*/
typedef struct Node {
ElemType data;
struct Node* next;
}Node, * LinkList;
LinkList L, LB;
/*初始化单链表*/
InitList(LinkList* L) {
*L = (LinkList)malloc(sizeof(Node)); //建立头结点
(*L)->next = NULL; //建立空的单链表L
}
/*头插法建表*/
/*从一个空表开始,每次读入数据,生成新结点,将读入数据存放到新结点的数据域中,然后将新结点插入到当前链表的表头结点之后,直到读入结束标志为止。
采用头插法得到的单链表的逻辑顺序与输入元素顺序相反,亦称头插法建表为逆序建表法*/
void CreateFromHead(LinkList L) {
Node* s;
int c, flag = 1;
while (flag) {
scanf("%d", &c);
if (c != 0) {
s = (Node*)malloc(sizeof(Node));//建立新结点
s->data = c;
s->next = L->next; //将s结点插入表头
L->next = s;
}
else
flag = 0;
}
}
/*尾插法建表(建立单链表与输入顺序相同)
将新结点插入到当前单链表的表尾上,为此需增加一个尾指针r,使之指向当前单链表的表尾*/
void CreateFromTail(LinkList L) {
Node* r, * s;
r = L; //r指针动态指向链表的当前表尾,以便于做尾插入,其初值指向头结点
int c, flag = 1;
while (flag) {
scanf("%d", &c);
if (c != 0) {
s = (Node*)malloc(sizeof(Node));//建立新结点
s->data = c;
r->next = s;
r = s;
}
else {
flag = 0;
r->next = NULL; //将最后一个结点的next链域置为空,表示链表的结束
}
}
}
/*单链表输出*/
void OutputList(LinkList L) {
if (L->next == NULL) {
printf("空表!");
}
else {
Node* p;
p = L->next;
while (p) {
printf("%d ", p->data);
p = p->next;
}
}
}
/*按序号查找第i个结点值*/
/*在带头结点的单链表L中查找第i个结点,若找到(1<=i<=n),则返回该结点的存储位置;否则返回NULL*/
int Get(LinkList L, int n) {
int i = 0;
Node* p;
if (n <= 0)
return NULL;
p = L;
while (p->next != NULL && i < n) {
p = p->next;
i++;
}
if (i == n)
return (p->data);
else
return NULL;
}
/*按值查找结点序号*/
/*在带头结点的单链表L中查找其结点值等于n的第一个结点,若找到则返回该结点的位置p,否则返回NULL*/
int Locate(LinkList L, int n) {
int i = 0;
Node* p;
p = L->next;
while (p != NULL) {
if (p->data != n) {
p = p->next;
i++;
}
else
break;
}
if (p != NULL)
return i + 1;
else
return NULL;
}
/*求表长*/
int GetLength(LinkList L) {
int i = 0;
Node* p;
p = L->next;
while (p != NULL) {
p = p->next;
i++;
}
return i;
}
/*逆置*/
void ReverseList(LinkList L) {
Node* p, * temp;
if (L->next == NULL || L->next->next == NULL)
return NULL;
p = L->next->next;
L->next->next = NULL; //原本第一个结点逆置后变为最后一个结点,next链域置为空,表示链表的结束
while (p != NULL) {
temp = p->next; //从第二个结点起依次移至头结点之后实现逆置
p->next = L->next;
L->next = p;
p = temp;
}
}
/*插入*/
/*插入的过程分为三步:
(1)查找:在单链表中找到第i-1个结点并由指针pre指示
(2)申请:申请新结点s,将其数据域的值置为e
(3)插入挂链:通过修改指针域将新结点s挂入单链表L */
void InsList(LinkList L, int i, int e) {
/*在带头结点的单链表L中第i个位置插入值为e的新结点*/
Node* pre, * s;
pre = L;
if (i < 1) {
printf("插入位置不合法!\n");
return ERROR;
}
int k = 0;
while (pre != NULL && k < i - 1) { //查找第i-1个结点
pre = pre->next;
k++;
}
if (pre == NULL) {
printf("插入位置不合法!\n");
return ERROR;
}
s = (Node*)malloc(sizeof(Node)); //申请一个新结点
s->data = e;
s->next = pre->next; //修改指针完成插入操作
pre->next = s;
printf("插入成功!\n");
return OK;
}
/*删除*/
/*删除过程分为两步:
(1)查找:通过计数方式找到第i-1个结点并由指针pre指示
(2)删除第i个结点并释放结点空间 */
int DelList(LinkList L, int i) {
/*在带头结点的单链表L中删除第i个元素*/
Node* pre, * r;
pre = L;
if (i < 1) {
printf("删除位置不合法!\n");
return ERROR;
}
int k = 0, e;
while (pre->next != NULL && k < i - 1) {//查找第i-1个结点
pre = pre->next;
k++;
}
if (pre->next == NULL) {
printf("删除位置不合法!\n");
return ERROR;
}
r = pre->next;
pre->next = r->next; //修改指针,删除结点r
e = r->data;
free(r);
printf("删除成功!删除的结点值为%d\n", e);
return OK;
}
/*升序排序*/
void AscSort(LinkList L) {
Node* s, * pre, * p, * temp;
if (L->next == NULL)
return ERROR;
s = L->next->next;
L->next->next = NULL;
while (s != NULL) {
pre = L;
p = L->next;
while (p && s->data > p->data) {
pre = p;
p = p->next;
}
temp = s->next;
pre->next = s;
s->next = p;
s = temp;
}
}
/*升序合并*/
void MergeList(LinkList L, LinkList LB) {
Node* pa, * pb, * pre;
pre = L;
pa = L->next;
pb = LB->next;
while (pa && pb) {
if (pa->data < pb->data)
{
pre->next = pa; pre = pa; pa = pa->next;
}
else
{
pre->next = pb; pre = pb; pb = pb->next;
}
}
pre->next = pa ? pa : pb;
free(LB);
}
/*选择菜单函数用于实现功能的选择*/
void Menu(int select) {
int n, e;
switch (select) {
case 1:InitList(&L); //初始化
printf("初始化成功!\n"); break;
case 2:printf("输出结果为:"); //输出
OutputList(L);
printf("\n"); break;
case 3:InitList(&L); //头插法建表
printf("请输入单链表元素(以空格隔开,以0结束):");
CreateFromHead(L);
printf("头插法建表成功!\n");
printf("输出结果为:");
OutputList(L);
printf("\n"); break;
case 4:InitList(&L); //尾插法建表
printf("请输入单链表元素(以空格隔开,以0结束):");
CreateFromTail(L);
printf("尾插法建表成功!\n");
printf("输出结果为:");
OutputList(L);
printf("\n"); break;
case 5:printf("请输入要查找的结点序号:"); //按序号查找结点值
scanf("%d", &n);
if (Get(L, n) == NULL)
printf("输入错误,未查到该结点!\n");
else
printf("结点%d的值为:%d\n", n, Get(L, n)); break;
case 6:printf("请输入要查找的结点序号:"); //按值查找结点序号
scanf("%d", &n);
if (Locate(L, n) == NULL)
printf("未查到该结点!\n");
else
printf("值为%d的结点序号为:%d\n", n, Locate(L, n)); break;
case 7:printf("长度为:%d\n", GetLength(L)); break;//求表长
case 8:ReverseList(L); //逆置
printf("逆置成功!输出结果为:");
OutputList(L);
printf("\n"); break;
case 9:printf("请输入插入位置及结点值:"); //插入
scanf("%d%d", &n, &e);
InsList(L, n, e);
printf("输出结果为:");
OutputList(L);
printf("\n"); break;
case 10:printf("请输入删除位置:"); //删除
scanf("%d", &n);
DelList(L, n);
printf("输出结果为:");
OutputList(L);
printf("\n"); break;
case 11:AscSort(L); //升序排序
printf("升序排序结果为:");
OutputList(L);
printf("\n"); break;
case 12:InitList(&LB); //升序合并
printf("请输入要合并的新单链表元素(以空格隔开,以0结束):");
CreateFromTail(LB);
printf("尾插法建表成功!\n");
printf("输出结果为:");
OutputList(LB);
AscSort(L);
AscSort(LB);
MergeList(L, LB);
printf("\n合并成功!输出结果为:");
OutputList(L);
printf("\n"); break;
default:printf("输入错误!\n"); break;
}
}
int main() {
InitList(&L);
printf("----------单链表基本运算----------\n");
printf("1.初始化\t2.输出\n");
printf("3.头插法建表\t4.尾插法建表\n");
printf("5.按序号查找\t6.按值查找\n");
printf("7.求表长\t8.逆置\n");
printf("9.插入\t\t10.删除\n");
printf("11.升序排序\t12.升序合并\n");
printf("0.退出程序\n");
printf("----------------------------------\n");
int select;
printf("请选择操作:");
scanf("%d", &select);
while (select) {
Menu(select);
printf("----------------------------------\n");
printf("请选择下一操作:");
scanf("%d", &select);
}
printf("退出成功!\n");
return 0;
}
3. 运行结果
参考:耿国华《数据结构——用C语言描述(第二版)》