数据结构的单链表

单链表的定义

定义

用一组任意的存储单元存储线性表的数据元素(这组存储单元可以是连续的,也可以是不连续的)。因此,为了表示每个数据元素a,除了存储其本身的信息之外,还需存储一个指示其直接后继的信息(直接后继的存储位置)。这两部分信息组成数据元素a的存储映像,称为节点(node)。它包括两个域:其中存储数据元素信息的域称为数据域;存储直接后继存储位置的域称为指针域。指针域中存储的信息称作指针。n个节点[a(1<i<n)的存储映像]链接成一个链表,即为线性表:

                           (aj, a2,…,an)

的链式存储结构。又由于此链表的每个节点中只包含一个指针域,故又称线性链表单链表

typedef struct LNode {
    int data;
    struct LNode* next;
}LNode, * LinkList;

单链表的存储结构

下图所示为线性表的单链表存储结构,整个链表的存取必须从头指针开始进行,头指针指示链表中第一个节点(第一个数据元素的存储映像,也称首元节 第点)的存储位置。同时,由于最后一个数据元素没有直接后继,则单链表中最后一个节点的指针为空(NULL)。
请添加图片描述

单链表的逻辑状态

通常将链表画成用箭头相链接的节点的序列,节点之间的箭头表示链域中的指针。上图所示的单链表可画成下图所示的形式,这是因为在使用链表时,关心的只是它所表示的线性表中数据元素之间的逻辑顺序,而不是每个数据元素在存储器中的实际位置。请添加图片描述

单链表操作

头节点

一般情况下,为了处理方便,在单链表的第一个节点之前附设一个节点,称之为头节点。上图所示的单链表增加头节点后如下图所示。
请添加图片描述

说明

  1. 首元节点是指链表中存储第一个数据元素a,的节点。如图2.8或图2.9所示的节点“ZHAO”
  2. 头节点是在首元节点之前附设的一个节点,其指针域指向首元节点。头节点的数据域可以不存储任何信息,也可存储与数据元素类型相同的其他附加信息。例如,当数据元素为整型时,头节点的数据域中可存放该线性表的长度。
  3. 头指针是指向链表中第一个节点的指针。若链表设有头节点,则头指针所指节点为线性表的头节点;若链表不设头节点,则头指针所指节点为该线性表的首元节点。

初始化

单链表的初始化操作就是构造一个只有头节点的空表。

void InitList(LinkList& L)
{
    L = (LNode*)malloc(sizeof(LinkList));
    L->next = NULL;
}

取值

和顺序表不同,链表中逻辑相邻的节点并没有存储在物理相邻的单元中,这样,根据给定的节点位置序号i,在链表中获取该节点的值不能像顺序表那样随机访问,而只能从链表的首元节点出发,顺着链域next逐个节点向下访问。

void GetElem(LinkList* L, int i, ElemType& e)
{//在带头节点的单链表L中根据序号1获取元素的值,用e返回L中第1个数据元素的值
    LinkList* p;
    p = L->next; int j = 1;       //初始化P指向首元节点,计数器初值赋为1
    while (p && j < i)        //顺链域向后查找,直到P为空或p指向第1个元素
    {
        p = p->next;           //p指向下一个节点
        j++;                //计数器j相应加1
    }
    if (!p || j > i)
    {
        return 0;      //i值不合法i>n或i<=0
    }
    e - p->data;         //取第i个节点的数据域
    return 1;
}

查找

链表中按值查找的过程和顺序表类似,从链表的首元节点出发,依次将节点值和给定值e进行比较,返回查找结果。

按值查找

//按值查找:查找x在L中的位置
LNode* LocateElem(LinkList L, int x)
{
    LNode* p = L->next;
    while (p && p->data != x) {
        p = p->next;
    }
    return p;
}

按位查找

//按位查找:查找在单链表L中第i个位置的结点
LNode* GetElem(LinkList L, int i) {
    int j = 1;
    LNode* p = L->next;
    if (i == 0)return L;
    if (i < 1)return NULL;
    while (p && j < i) {
        p = p->next;
        j++;
    }
    return p; //如果i大于表长,p=NULL,直接返回p即可
}

插入

假设要在单链表的两个数据元素a和b之间插入一个数据元素x,已知p为其单链表存储结构中指向节点a的指针,如图(a)所示。
请添加图片描述
为插入数据元素x,首先要生成一个数据域为x的节点,然后将之插入单链表中。根据插入操作的逻辑定义,还需要修改节点a中的指针域,令其指向节点x,而节点x中的指针域应指向节点b,从而实现3个元素a、b和x之间逻辑关系的变化。插入后的单链表如图(b)所示。

void Insert(LinkList& L, int i, int x)
{
    LNode* p = GetElem(L, i - 1);
    LNode* s = (LNode*)malloc(sizeof(LNode));
    s->data = x;
    s->next = p->next;
    p->next = s;
}

删除

要删除单链表中指定位置的元素,同插入元素一样,首先应该找到该位置的前驱节点。如下图所示,在单链表中删除元素b时,应该首先找到其前驱节点a。为了在单链表中实现元素a、b和c之间逻辑关系的变化,仅需修改节点a中的指针域即可。
但在删除节点b时,除了修改节点a的指针域外,还要释放节点b所占的空间,所以在修改指针前,应该引入另一指针q,临时保存节点b的地址以备释放。

void Delete(LinkList& L, int i)
{
    if (i<1 || i>Length(L)) {
        cout << "delete failed: index is wrong." << endl;
        return;
    }
    LNode* p = GetElem(L, i - 1);
    LNode* q = p->next;
    p->next = q->next;
    free(q);
}

创建单链表

前插法

前插法是通过将新节点逐个插入链表的头部(头节点之后)来创建链表,每次申请一个新点,读入相应的数据元素值,然后将新节点插入到头节点之后。
下图所示为线性表(b,c,d,e)前插法的创建过程,因为每次插入在链表的头部,所以应逆位序输入数据,依次输入e、d、c、b,输入顺序和线性表中的逻辑顺序是相反的。
请添加图片描述

LinkList HeadInsert(LinkList& L)
{
    InitList(L); //初始化
    int x;
    cin >> x;
    while (x != 9999) {
        LNode* s = (LNode*)malloc(sizeof(LNode));
        s->data = x;
        s->next = L->next;
        L->next = s;
        cin >> x;
    }
    return L;
}

后插法

后插法是通过将新节点逐个插入链表的尾部来创建链表。同前插法一样,每次申请一个新节点,读入相应的数据元素值。不同的是,为了使新节点能够插入表尾,需要增加一个尾指针r指向链表的尾节点。
请添加图片描述

LinkList TailInsert(LinkList& L)
{
    InitList(L);
    LNode* s, * r = L;
    int x;
    cin >> x;
    while (x != 9999) {
        s = (LNode*)malloc(sizeof(LNode));
        s->data = x;
        r->next = s;
        r = s;
        cin >> x;
    }
    r->next = NULL;
    return L;
}

完整代码

#include <stdlib.h>
#include <iostream>
using namespace std;

typedef struct LNode
{
    int data;
    struct LNode* next;
}LNode, * LinkList;

//初始化
void InitList(LinkList& L)
{
    L = (LNode*)malloc(sizeof(LinkList));
    L->next = NULL;
}

//遍历操作
void PrintList(LinkList L)
{
    LNode* p = L->next;
    while (p) {
        cout << p->data << " ";
        p = p->next;
    }
    cout << endl;
}

//求单链表的长度
int Length(LinkList L) {
    LNode* p = L->next;
    int len = 0;
    while (p) {
        len++;
        p = p->next;
    }
    return len;
}

//按值查找:查找x在L中的位置
LNode* LocateElem(LinkList L, int x)
{
    LNode* p = L->next;
    while (p && p->data != x) {
        p = p->next;
    }
    return p;
}

//按位查找:查找在单链表L中第i个位置的结点
LNode* GetElem(LinkList L, int i) {
    int j = 1;
    LNode* p = L->next;
    if (i == 0)return L;
    if (i < 1)return NULL;
    while (p && j < i) {
        p = p->next;
        j++;
    }
    return p; //如果i大于表长,p=NULL,直接返回p即可
}

//将x插入到单链表L的第i个位置上
void Insert(LinkList& L, int i, int x)
{
    LNode* p = GetElem(L, i - 1);
    LNode* s = (LNode*)malloc(sizeof(LNode));
    s->data = x;
    s->next = p->next;
    p->next = s;
}

//删除操作:将单链表中的第i个结点删除
void Delete(LinkList& L, int i)
{
    if (i<1 || i>Length(L)) {
        cout << "delete failed: index is wrong." << endl;
        return;
    }
    LNode* p = GetElem(L, i - 1);
    LNode* q = p->next;
    p->next = q->next;
    free(q);
}


//头插法建立单链表
LinkList HeadInsert(LinkList& L)
{
    InitList(L); //初始化
    int x;
    cin >> x;
    while (x != 9999) {
        LNode* s = (LNode*)malloc(sizeof(LNode));
        s->data = x;
        s->next = L->next;
        L->next = s;
        cin >> x;
    }
    return L;
}

//尾插法建立单链表
LinkList TailInsert(LinkList& L)
{
    InitList(L);
    LNode* s, * r = L;
    int x;
    cin >> x;
    while (x != 9999) {
        s = (LNode*)malloc(sizeof(LNode));
        s->data = x;
        r->next = s;
        r = s;
        cin >> x;
    }
    r->next = NULL;
    return L;
}

//按值查找:查找x在L中的位置
LNode* LocateElem(LinkList L, int x)
{
    LNode* p = L->next;
    while (p && p->data != x) {
        p = p->next;
    }
    return p;
}

//按位查找:查找在单链表L中第i个位置的结点
LNode* GetElem(LinkList L, int i) {
    int j = 1;
    LNode* p = L->next;
    if (i == 0)return L;
    if (i < 1)return NULL;
    while (p && j < i) {
        p = p->next;
        j++;
    }
    return p; //如果i大于表长,p=NULL,直接返回p即可
}

//将x插入到单链表L的第i个位置上
void Insert(LinkList& L, int i, int x)
{
    LNode* p = GetElem(L, i - 1);
    LNode* s = (LNode*)malloc(sizeof(LNode));
    s->data = x;
    s->next = p->next;
    p->next = s;
}

//删除操作:将单链表中的第i个结点删除
void Delete(LinkList& L, int i)
{
    if (i<1 || i>Length(L)) {
        cout << "delete failed: index is wrong." << endl;
        return;
    }
    LNode* p = GetElem(L, i - 1);
    LNode* q = p->next;
    p->next = q->next;
    free(q);
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值