- 这篇最适合刚学习单链表的同学,先从整体入手,搞清楚链表从0到1的实现过程,回头再看代码块会有更清晰的认识。
- 看不懂的同学可多看看代码中的注释!!
- VS中.cpp文件之间相互调用会出现LNK2005、LNK1169错误。也就是说,不借助头文件很有可能函数重定义报错。所以选择创建一个.h头文件(声明单链表结构体以及函数)和两个.cpp源文件(一个写函数的定义,另一个写主函数实现调用)
1.先上.h头文件(sq1.h)声明链表和函数(注意头文件固定格式)
///头文件格式固定
//#ifndef SQLISTI_H
//#define SQLIST_H
//
//正文 //此处写相关结构体、函数等声明
//
//#endif
#ifndef SQLISTI_H
#define SQLIST_H
typedef int ElemType;
typedef struct LNode { //单链表声明
ElemType data;
struct LNode* next;
}LNode;
//各函数声明
void InitList(LNode*& L);
void CreatListHead(LNode*& L, ElemType a[], int n);
void CreatListTail(LNode*& L, ElemType a[], int n);
void DestroyList(LNode*& L);
bool ListEmpty(LNode* L);
int ListLength(LNode* L);
void DisppList(LNode* L);
bool GetElem(LNode* L, int i, ElemType& e);
int LocateElem(LNode* L, ElemType e);
bool ListInsert(LNode*& L, int i, ElemType e);
bool DeleteList(LNode*& L, int i, ElemType& e);
#endif
2.函数定义的.cpp源文件(sq1.cpp)
#include<stdio.h>
#include<stdlib.h>
#include"sq1.h"
//1.初始化链表
void InitList(LNode*& L)
{
L = (LNode*)malloc(sizeof(LNode));
L->next = NULL;
}
//2.头插法创建单链表
void CreatListHead(LNode*& L, ElemType a[], int n)
{
printf("头插法创建演示:\n");
LNode* s; //
int i = 0;
L = (LNode*)malloc(sizeof(LNode)); //为头结点开辟空间,“LNode*”类型的,开“LNode”长度的空间
L->next = NULL;
for (i = 0; i < n; i++)
{
s = (LNode*)malloc(sizeof(LNode));
s->data = a[i]; //新数据保存进s结点的数据域
s->next = L->next; //两步插入操作
L->next = s;
printf("插入第%d个,值为%d\n", i + 1, s->data); //i为下标,从0开始算的,i+1是序数
}
}
//3.尾插法创建单链表
void CreatListTail(LNode*& L, ElemType a[], int n)
{
printf("尾插法创建演示:\n");
LNode* s, * r; //不同于头插法,创建一个始终处于尾结点的r
int i;
L = (LNode*)malloc(sizeof(LNode));
L->next = NULL;
r = L; //结合下面的循环,让r成为每次插入s前的尾结点
for (i = 0; i < n; i++)
{
s = (LNode*)malloc(sizeof(LNode));
s->data = a[i]; //新数据保存进s结点的数据域
r->next = s;
//printf("插入第%d个,值为%d\n", i, s->data);
r = s; //更新r的位置
printf("插入第%d个,值为%d\n", i + 1, s->data);
}
r->next = NULL; //插入完成后使尾结点的指针域为空
}
//4.销毁链表
void DestroyList(LNode*& L)
{
//结点需要用free()函数逐个释放,所以可以看作乘客(每个结点)上车,
//所以这里放出两辆“公交车”,结点们通过“换乘”抵达free()站
LNode* pre = L; //乘坐pre车直接去free()
LNode* p = L->next; //乘坐p车预备去free()
while (p != NULL)
{
free(pre);
//下面开始更新pre和p的位置(两辆车的乘客都更新)
pre = p;
p = pre->next;
}
free(pre); //最后一位乘客需要pre车再跑一趟
}
//5.判断链表是否为空
bool ListEmpty(LNode* L)
{
if (L->next == NULL)
return true;
else
return false;
}
//6.测量链表长度
int ListLength(LNode* L)
{
int i = 0;
LNode* p = L->next;
while (p != NULL)
{
i++;
p = p->next;
}
return(i);
}
//7.输出链表
void DisppList(LNode* L)
{
LNode* p = L->next;
while (p != NULL)
{
printf("%d", p->data);
p = p->next;
}
printf("\n");
}
//8.逻辑位序查找
bool GetElem(LNode* L, int i, ElemType& e) //从头找第i个结点,若存在,将其结果赋值给e
{
int j = 0;
LNode* p = L; //让p成为头结点
while (j < i && p != NULL) //还没找完就继续找
{
j++;
p = p->next;
}
if (p == NULL)
return false;
else
{
e = p->data;
return true;
}
}
//9.按元素值查找其逻辑位序
int LocateElem(LNode* L, ElemType e)
{
int i = 1; //直接从首结点开始查找(头结点->首结点->尾结点)
LNode* p = L->next;
while (p != NULL && p->data != e)
{
p = p->next;
i++;
}
if (p == NULL)
return false;
else
return(i);
}
//10.链表插入
bool ListInsert(LNode*& L, int i, ElemType e)
{
//先查找插入位置是否存在(i-1是否存在)
int j = 0;
LNode* p = L, * s;
while (j < i - 1 && p != NULL)
{
p = p->next;
j++;
}
if (p == NULL) //若位置不存在,返回false
return false;
else
//若插入位置存在,则进行插入操作
{
//首先为s结点开辟空间
s = (LNode*)malloc(sizeof(LNode));
s->data = e;
s->next = p->next;
p->next = s;
return true;
}
}
//11.链表删除
bool DeleteList(LNode*& L, int i, ElemType& e)
{
//先确定i-1位置是否存在结点以及是否存其在后继节点(i)
int j = 0;
LNode* p = L, * q;
while (j < i - 1 && p != NULL)
{
j++;
p = p->next;
}
if (p == NULL)
return false;
else
{
q = p->next;
if (q == NULL) //判断是否存在i-1的后继结点(即i结点)
return false;
e = q->data;
p->next = q->next; //删除q结点
return true;
}
}
3.最后就是在源文件(main.cpp)中使用定义过的各个函数
#include<stdio.h>
#include<stdlib.h>
#include"sq1.h"
void main()
{
LNode* L;
ElemType a[6] = { 1,2,3,4,5,6 }, b[6] = { 6,5,4,3,2,1 };
InitList(L);
printf("初始化后链表长度ListElemtype(L)=%d\n", ListEmpty(L));
CreatListHead(L, b, 6);
CreatListTail(L, a, 6);
DisppList(L);
printf("链表长度为 %d\n", ListLength(L));
int e;
GetElem(L, 3, e);
printf("L中第3个元素为%d\n", e);
printf("L中第4个元素为%d\n", LocateElem(L, 4));
ListInsert(L, 6, 888);//6位置插入888
DisppList(L);
DeleteList(L, 5, e); //删除第5个结点,第三个参数(e)表示将删除结点的值放入指定的存储单元
DisppList(L);
printf("删除的元素为 %d\n", e);
DestroyList(L);
}