数据结构 单链表(带表头指针)

数据结构 单链表(带表头指针)


前言

上一节的动态分配内存的顺序表
前一篇文章,动态分配内存的顺序表


一、链表

链表的一种。一本文提供了一种带表头指针的单链表的简单实现。参考了数据结构(严蔚敏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;
}

总结

只要理解了各个结点的指针变化就很简单了

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值