单链表与双链表-线性表的链式表示


前言

链表有单链表双链表两种实现形式。

它通指针将表内各个节点相连,不要求逻辑上的相邻元素在内存上也相邻。因此,链表可以灵活地在内存中“穿梭”,不需要一整片连续的内存空间。

同时,这也决定了链表不能随机存取表内元素。

一、单链表

1.单链表的结构体定义

单链表的结构包含数据域指针域

其中,数据域存放数据元素;指针域存放指向下一个结点地址的指针。

【注】本贴只记录带头结点的链表。

图示如下:
在这里插入图片描述

结构体定义如下,写入 LinkList.h 文件:

#include<iostream>
using namespace std;
typedef int ElemType;
typedef struct LNode{
    ElemType data;       // 存储数据
    struct LNode *next;  // 指向下一个结点
}LNode, *LinkList;

2.单链表的创建

1)头插法创建单链表

#include "LinkList.h"
void CreateList_HeadInsert(LinkList &L) {
    LNode *t;                               // 临时结点 t,存储要插入的数据及其指针域
    ElemType x;                             // 要插入的数据
    L = (LNode*)malloc(sizeof(LNode));      // 分配头节点的内存空间
    L->next = NULL;                         // 头节点(头插创建过程中相当于链表尾)初始化
    cin>>x;
    while (x!=9999) {
        t = (LNode*)malloc(sizeof(LNode));  // 分配临时结点内存空间
        t->data = x;                        // 存入数据
        t->next = L->next;
        L->next = t;                        // 设置指针   
        cin>>x;
    }
}

图解如下:

在这里插入图片描述

2)尾插法创建单链表

#include "LinkList.h"
void CreateList_TailInsert(LinkList &L) {
    L = (LNode*)malloc(sizeof(LNode));
    LNode *t, *r = L;                       // 指针 r 一直指向链表 L 的尾部
    ElemType x;
    cin>>x;
    while (x!=9999) {
        t = (LNode*)malloc(sizeof(LNode));
        t->data = x;
        r->next = t;
        r = t;
        cin>>x;
    }
    r->next = NULL;                         // 链表尾部之后为 NULL
}

图解如下:

在这里插入图片描述

3.单链表的基本操作

3.1 输出操作 - PrintList

函数名:PrintList(LinkList L)

输出格式:

output: 1 2 3 4 5 6

----------------

#include "LinkList.h"
void PrintList(LinkList L) {
    cout<<"output: ";
    while (L->next!=NULL) {
        L = L->next;
        cout<<L->data<<" ";
    }
    cout<<'\n'<<"----------------"<<'\n';
}

3.2 按位查找 - GetElem

函数名:GetElem(LinkList L, int i)

#include "LinkList.h"
LNode* GetElem(LinkList L, int i) {
    int j = 1;               // 位序,从 1 开始
    LNode *p = L->next;      // p 指向第一个存有数据的结点
    if (i==0) return L;      // i 是位序,从 1 开始;0 表示头结点
    if (i<1) return NULL;    // 位序不合法
    while (p&&j<i) {
        p = p->next;
        j++;                 // 遍历,直到位置 i
    }
    return p;                // 返回指向位置 i 处结点的指针;无匹配返回 NULL
}

3.3 按值查找 - LocateElem

函数名:LocateElem(LinkList L, ElemType e)

#include "LinkList.h"
LNode* LocateElem(LinkList L, ElemType e) {
    LNode *p = L->next;
    while (p&&p->data!=e)    // 从第一个存储数据的结点开始往后遍历
        p = p->next;  
    return p;                // 若表内无匹配元素,返回表尾的 next: NULL
}

3.4 插入操作 - ListInsert

函数名:ListInsert(LinkList &L, int i, ElemType e)

#include "LinkList.h"
bool ListInsert(LinkList &L, int i, ElemType e) {
    LNode *p = GetElem(L, i-1);                 // 前驱结点
    if (p==NULL) return false;                  // 位序不合法,插入失败
    LNode *t = (LNode*)malloc(sizeof(LNode));
    t->data = e;
    t->next = p->next;
    p->next = t;
    return true;
}

示例如下:

在这里插入图片描述

3.5 删除操作 - ListDelete

函数名:ListDelete(LinkList &L, int i, ElemType &e)

#include "LinkList.h"
bool ListDelete(LinkList &L, int i, ElemType &e) {
    LNode *p = GetElem(L, i-1);     // 前驱结点
    if (p==NULL) return false;
    LNode *s = p->next;
    e = s->data;                    // 带出数值
    p->next = s->next;
    free(s);                        // 释放删除的结点
    return true;
}

示例如下:

在这里插入图片描述

二、双链表

1.双链表的结构体

单链表结点只有一个 next 指针指向它的后继,因此单链表在搜索查询、插入删除时,只能从前往后依次遍历。若要访问某处结点的前驱,则需要从头开始重新遍历。

因此双链表结点采用两个指针:prior 指向前驱;next 指向后继。

示意图如下:

在这里插入图片描述

结构体定义如下,写入 DLinkList.h 文件:

#include<iostream>
using namespace std;
typedef int ElemType;
typedef struct DNode {
    ElemType data;
    struct DNode *prior, *next;  // 分别指向前驱、后继
}DNode, *DLinkList;

由于双链表结点有两个指针,其删除、插入操作与单链表稍有区别。

2.双链表的创建

只记录尾插法:(头插可根据前文讲解自己练习)

#include "DLinkList.h"
void CreateDLinkList_TailInsert(DLinkList &DL) {
    DL = (DNode*)malloc(sizeof(DNode));
    DNode *t, *r = DL;
    DL->prior = NULL;
    ElemType x;
    cin>>x;
    while (x!=9999) {
        t = (DNode*)malloc(sizeof(DNode));
        t->data = x;
        t->prior = r;      // 设置插入结点前驱
        r->next = t;       // 设置前驱的后继
        r = t;             // 更新表尾指针
        cin>>x;
    }
    r->next = NULL;        // 表尾之后为 NULL
}

图解如下:

在这里插入图片描述

三、总结

链表不同于顺序表,不能随机存取

链表的指针域会占用存储资源,存储效率不及顺序表(只存数据元素)。

但同时链表能够更加灵活的进行存储,不受限于正片完整的内存空间,因此链表的长度可以远远大于顺序表。

本次记录了单链表定义结构体基本操作;以及双链表的定义和创建。
还有双链表的基本操作,以及循环链表静态链表没有更新。
其实可以类比推出代码的实现方法。
——2023/2/28

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

斡铎

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值