数据结构 单链表(带表头指针)
文章目录
前言
上一节的动态分配内存的顺序表
前一篇文章,动态分配内存的顺序表
一、链表
链表的一种。一本文提供了一种带表头指针的单链表的简单实现。参考了数据结构(严蔚敏C语言版)教材。
有些判断输入是否合法的说明就跳过了,代码中可能没有体现,读者可以自行补充,
不带表头的指针的代码类似,由于带表头指针比较方便,所以就写了这个。
其中仍然是省略了部分判断输入合法性的操作,读者可以自行补全。部分函数为方便起见进行了简化,读者可以自行改写。
代码仅供参考,学习,
运行环境是Visual Studio 2019
二、代码部分
1.头文件
建立是cpp文件,用c文件也可以,只是需要改变一下读写数据的操作。
#include <iostream>
#include <stdlib.h>
#include <stdio.h>
#include <malloc.h>
using namespace std;
2.预定义
预先定义一些内容,方便后续操作
这里追求方便,定义了ElemType为int类型,读者可以自己更改为其他类型。如果更改了,之后的判断是否相等需要重写,C++可以重载==运算符。
#define OK 1 //返回OK,代表操作成功
#define ERROR 0 //返回ERROR,代表操作失败
typedef int ElemType; //之后出现的ElemType为int类型,本代码默认数据类型是int
3.定义存储结构
使用结构体定义了存储结构。
这里相当于
typedef struct Lnode* LinkList;
typedef struct Lnode Lnode;
这样两行代码,读者可以自行理解一下
typedef struct LNode
{
ElemType data;
struct LNode* next;
}LNode, * LinkList;
4.初始化带头结点的单链表
初始化需要给头结点分配内存空间,注意强制类型转换。
将头结点的next指向空即可。
int InitList(LinkList& L)
{
L = (LNode*)malloc(sizeof(LNode));
if (L == NULL)
{
return false;
}
L->next = NULL;
return true;
}
5.判断是否为空
只需要看首元结点的next是不是NULL即可
bool IsEmpty(LinkList L)
{
if (L->next == NULL)
{
cout << "空链表" << endl;
return true;
}
else
{
cout << "不是空链表" << endl;
return false;
}
}
6.打印链表
很简单,判断一下是否是空表就行,不多做说明
int PrintList(LinkList L)
{
cout << "单链表如下:" << endl;
LNode* p;
p = L->next;
if (p != NULL)
{
cout << p->data;
p = p->next;
}
else
{
return ERROR;
}
while (p != NULL)
{
cout << " " << p->data;
p = p->next;
}
cout << endl;
return OK;
}
7.尾插法构建单链表,设置尾指针
步骤1:尾指针r初始化,指向头结点
步骤2:根据链表的元素个数进行循环
1.生成新节点s;
2.输入元素值赋给新节点s的数据域;
3.将新节点s插入到尾结点r之后
4尾指针r指向新的尾结点*s
int ListTailInsert(LinkList& L)
{
int n;
cout << "请输入要插入元素的个数" << endl;
cin >> n;
cout << "请输入要插入的元素" << endl;
LNode* s, *r;
r = L;
for (int i = 0; i < n; i++)
{
s = (LNode*)malloc(sizeof(LNode)); //生成新节点
cin >> s->data; //新节点数据域赋值
r->next = s; //此时上一次的尾结点的下一个结点就是s
r = s; //s就是新的尾结点
}
r->next = NULL;
return OK;
}
8.头插法构建单链表
和尾插法类似,不过不需要设置尾指针
步骤1:根据链表的元素个数进行循环
1.生成新节点s;
2.输入元素值赋给新节点s的数据域;
3.将新节点*s插入到头结点之前
int ListHeadInsert(LinkList& L)
{
LNode* s;
int n;
cout << "请输入要插入元素的个数" << endl;
cin >> n;
cout << "请输入要插入的元素" << endl;
for (int i = 0; i < n; i++)
{
s = (LNode*)malloc(sizeof(LNode)); //生成新节点
cin >> s->data;
s->next = L->next;
L->next = s;
}
return OK;
}
9.单链表长度,返回长度
很简单,相当于遍历一遍链表,但不输出
int ListLength(LinkList L)
{
int count = 0;
LinkList p=L;
while (p->next != NULL)
{
count++;
p = p->next;
}
return count;
}
10.单链表按位查找,返回该节点
利用循环找到位置,返回节点,不是很难。
LNode* GetElem(LinkList L, int index)
{
LNode* p;
p = L->next;
int j = 1;
while (p != NULL && j < index)
{
p = p->next;
j++;
}
cout << "第" << index << "个元素是" << p->data << endl;
return p;
}
11.单链表按值查找,返回该节点
和遍历操作类似,不过需要在while循环里面加一个判断条件,找不到就返回NULL
LNode* LocateElem(LinkList L, ElemType e)
{
LNode* p=L->next;
int j = 1;
while (p != NULL && p->data != e)
{
p = p->next;
j++;
}
if (p == NULL)
{
cout << "未找到" << endl;
}
else
{
cout << "值为" << e << "的结点为第" << j << "个" << endl;
}
return p;
}
12.单链表在某一节点处插入
注意输入的参数是结点的指针
int InsertNextNode(LinkList& L, LNode* p, ElemType e)
{
LNode* s = (LNode*)malloc(sizeof(LNode));
s->data = e;
s->next = p->next;
p->next = s;
return OK;
}
13.单链表在某一位置处插入
复用了之前的代码,方便操作
int ListInsert(LinkList& L, int i, ElemType e)
{
LNode* p = GetElem(L, i - 1);
return InsertNextNode(L, p, e);
}
14.单链表删除某一结点
和插入操作类似,注意释放内存空间
int DeleteNode(LinkList& L, LNode* p)
{
LNode* q = p->next;
p->data = p->next->data;
p->next = q->next;
free(q);
return OK;
}
15.单链表删除某一位置的元素
和插入操作类似,注意释放内存空间
int ListDelete(LinkList& L, int i)
{
LNode* p = GetElem(L, i);
return DeleteNode(L, p);
}
16.单链表销毁
看看就好
int DistoryList(LinkList &L)
{
if (!L)
{
cout << "表不存在" << endl;
return ERROR;
}
LinkList q = L->next;
while (q != NULL) { //当q结点不为空时一直进入循环
free(L); //释放L结点
L = q; //将q结点赋值给L结点
q = L->next; //将q结点赋值给L结点以后使q结点指向L的下一个结点
}
free(L); //此时q的值为NULL,L指向尾结点,将其释放
L = NULL;
}
17.主函数
测试用的
int main()
{
LinkList L;
InitList(L);
IsEmpty(L);
ListHeadInsert(L);
PrintList(L);
cout << ListLength(L) << endl;
LNode* s=GetElem(L, 2);
cout << s->data << endl;
s = LocateElem(L, 3);
cout << s->data << endl;
ListInsert(L, 2, 4);
ListInsert(L, 2, 4);
PrintList(L);
ListDelete(L, 5);
PrintList(L);
return 0;
}
总结
只要理解了各个结点的指针变化就很简单了