【考研】分清带头结点和不带头结点的单链表

34 篇文章 52 订阅
21 篇文章 1 订阅

CSDN话题挑战赛第2期
参赛话题:学习笔记

 前言

为分清带结点与不带头结点的单链表操作,本文以图文和表格形式描述了两者之间的区别。考研中,数据结构的单链表操作是重要考点,其中,比较常考带头结点的链表操作。

所以,本文只描述了带头结点的插入、删除、查找、用前插法和后插法创建单链表等基本操作。

可结合以下链接一起学习:

【考研】数据结构考点——直接插入排序_住在阳光的心里的博客-CSDN博客

【考研】单链表相关算法(从基础到真题)_住在阳光的心里的博客-CSDN博客

一、区别

// 单链表的存储结构
typedef struct LNode{
    ElemType data;   //结点的数据域
    struct LNode *next;   //结点的指针域
}LNode, *LinkList;

// LinkList 为指向结构体 LNode 的指针类型

 

 针对上图,在分清带头结点与不带头结点的单链表之前,需弄清头指针与首元结点。

(一)针对头指针

LNode * 与 LinkList ,两者本质上是等价的。

通常习惯上用 LinkList 定义单链表,强调定义的是某个单链表的头指针;用 LNode * 定义指向单链表中任意结点的指针变量。例如:

(1)若定义 LinkList L,则 L 为单链表的头指针。(简称该链表为表L)

(2)若定义 LNode *p,则 p 为指向单链表中某个结点的指针,表示该结点的地址;用 *p 表示该结点。

LinkList p 的定义形式完全等价于 LNode *p。

所以,头指针是指向链表中第一个结点的指针

(3)若链表设有头结点,则头指针所指结点为该线性表的头结点;

(4)若链表不带头结点,则头指针所指结点为该线性表的首元结点。

(二) 针对首元结点

(1)一般,在单链表的第一个结点之前附设一个结点,称为头结点。而链表中存储第一个数据元素的结点,则称为首元结点

(2)头结点与首元结点的关系:头结点的指针域指向首元结点

(三) 头结点的数据域

可以不存储任何信息,也可存储与数据元素类型相同的其他附加信息,例如:当数据元素为整数型时,头结点的数据域可存放该线性表的长度。

二、链表增加头结点的作用

(一) 便于首元结点的处理

增加了头结点后,首元结点的地址保存在头结点(即其 “ 前驱 ” 结点)的指针域中,则对链表的第一个数据元素的操作与其他数据元素相同,无需进行特殊处理。

(二) 便于空表和非空表的统一处理

当链表不设头结点时,假设 L 为单链表的头指针,它应该指向首元结点,则当单链表为长度 n 为 0的空表时,L 指针为空(判定空表的条件可记为: L == NULL)。

增加头结点后,无论链表是否为空,头指针都是指向头结点的非空指针。如图2.10(a) 所示的非空单链表,头指针指向头结点。若为空表,则头结点的指针域为空(判定空表的条件可记为:
L->next == NULL),如图2.10 (b)所示。

单链表不带头结点带头结点
判定空表的条件L == NULLL->next == NULL
LinkList L;( L 为单链表的头指针L指向首元结点L指向头结点,头结点指向首元结点

三、带头结点的单链表的基本操作

(一)初始化

// (为更好理解清楚,博主会在每个操作前面放上单链表的结构定义)
// 单链表的存储结构
typedef struct LNode{
    ElemType data;   //结点的数据域
    struct LNode *next;   //结点的指针域
}LNode, *LinkList;    // LinkList 为指向结构体 LNode 的指针类型



// 构造一个空的带头结点的单链表L
Status InitList(LinkList &L){
    L = new LNode;    //生成新结点作为头结点,用头指针L指向头结点 (C++)
    L->next == NULL;    //头结点的指针域置空
    return OK;
}


// 也可以用以下方式
// 构造一个空的带头结点的单链表L
Status InitList(LinkList &L){
    L = (LNode *)malloc(sizeof(LNode));    //生成新结点作为头结点,用头指针L指向头结点 (C语言)
    L->next == NULL;    //头结点的指针域置空
    return OK;
}

(二)插入【设单链表表长为 n ,时间复杂度:O(n)】

 【算法步骤】
将值为 e 的新结点插入到表的第 i 个结点的位置上,即插入到结点  与  之间,具体插入过程如上图所示,图中对应的 5 个步骤说明如下。
① 查找结点  并由指针 p 指向该结点。
② 生成一个新结点 *s。
③ 将新结点 *s 的数据域置为 e。
④ 将新结点 *s 的指针域指向结点  。
⑤ 将结点 *p 的指针域指向新结点 *s。

// 单链表的存储结构
typedef struct LNode{
    ElemType data;   //结点的数据域
    struct LNode *next;   //结点的指针域
}LNode, *LinkList;    // LinkList 为指向结构体 LNode 的指针类型


// 在带头结点的单链表 L 中第 i 个位置插入值为 e 的新结点
Status ListInsert (LinkList &L, int i, ElemType e)
{
    p = L;
    j = 0;
    while(p && (j < i - 1)){    //查找第 i-1 个结点,P指向该结点
        p = p->next;
        ++j;
    }
 
    if(!p || j > i - 1)     // i > n+1 或者 i < 1
        return ERROR;
   
    s = new LNode;    //生成新结点 *s
    s->data = e;    //将结点 *s 的数据域置为 e
 
    s->next = p->next;   //将结点 *s 的指针域指向结点ai
 
    p->next = s;   //将结点 *p 的指针域指向结点 *s
    return OK;
}

(三)删除【时间复杂度:O(n)】

// 单链表的存储结构
typedef struct LNode{
    ElemType data;   //结点的数据域
    struct LNode *next;   //结点的指针域
}LNode, *LinkList;    // LinkList 为指向结构体 LNode 的指针类型


//在带头结点的单链表 L 中,删除第 i 个元素
Status ListDelete(LinkList &L, int i){
    p = L;
    j = 0;
    while((p->next) && (j < i-1)){     //查找第 i-1 个个结点,p指向该结点
        p = p->next;
        ++j;
    }
    
    //注意与插入操作的区别,因为合法的插入位置有 n+1 个,而合法的删除位置只有 n 个
    if(!(p->next) || (j > i-1))     //当 i > n 或 i < 1 时,删除位置不合理
        return ERROR;

    q = p->next;    //临时保存被删结点的地址以备释放
    p->next = q->next;   //改变删除结点前驱结点的指针域

    delete q;   //释放删除结点的空间

    return OK;
}

(四)查找【时间复杂度:O(n)】

注意:单链表是非随机存取的存储结构,即不能直接找到表中某个特定的结点。所以在查找某个特定的结点,需要从表头开始遍历,依次查找。

// 单链表的存储结构
typedef struct LNode{
    ElemType data;   //结点的数据域
    struct LNode *next;   //结点的指针域
}LNode, *LinkList;    // LinkList 为指向结构体 LNode 的指针类型


// 在带头结点的单链表 L 中查找值为 e 的元素
LNode *LocateElem(LinkList L, ElemType e){
    p = L->next;    //初始化,p指向首元结点
    while(p && p->data != e){    //顺链域向后扫描,直到 p 为空或 P 所指结点的数据域等于 e
        p = p->next;   //p指向下一个结点
    }
    return p;   //查找成功返回值为e的结点地址p,查找失败p为NULL
}


// 在带头结点的单链表 L 中查找序号为 i 的结点
LNode *LocateElem(LinkList L, int i){
    int j = 1;   //计数,初始为1
    LNode *p = L->next;   //头结点指针赋给p

    if(i == 0) return L;    //若 i 等于 0,则返回头结点
    if(i < 1) return NULL;    //若 i 无效,则返回 NULL

    while(p && j < i){   //从第一个结点开始找,查找第 i 个结点
        p = p->next;
        j++;
    }

    return p;    //返回第 i 个结点的指针,若 i 大于表长,则返回 NULL
}

(五)前插法创建单链表【时间复杂度:O(n)】

前插法:通过将新结点逐个插入链表的头部(头结点之后)来创建链表,每次申请一个新结点,读入相应的数据元素值,然后将新结点插入到头结点之后。

注意:输入顺序与线性表中的逻辑顺序是相反

例如:输入顺序:5,4,3,2,1                   线性表:1,2,3,4,5   

// 单链表的存储结构
typedef struct LNode{
    ElemType data;   //结点的数据域
    struct LNode *next;   //结点的指针域
}LNode, *LinkList;    // LinkList 为指向结构体 LNode 的指针类型


//逆位序输入 n 个元素的值,用前插法创建带头结点的单链表L
void CreatList_H(LinkList &L, int n){
    L = new LNode;    
    L->next = NULL;   // 建立一个带头结点的空链表

    for(int i = 0; i < n; ++i){      
        p = new LNode;    //生成新结点 *p
        cin>>p->data;    //输入元素值赋给新结点 *p 的数据域

        //将新结点 *p 插入到头结点之后
        p->next = L->next;    
        L->next = p;
    }    
}

(六)后插法创建单链表【时间复杂度:O(n)】

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

注意:输入顺序与线性表中的逻辑顺序是相同的。

例如:输入顺序:1,2,3,4,5                      线性表:1,2,3,4,5

// 单链表的存储结构
typedef struct LNode{
    ElemType data;   //结点的数据域
    struct LNode *next;   //结点的指针域
}LNode, *LinkList;    // LinkList 为指向结构体 LNode 的指针类型


//顺序输入 n 个元素的值,用后插法创建带头结点的单链表L
void CreatList_R(LinkList &L, int n){
    L = new LNode;
    L->next = NULL;    //建立一个带头结点的空链表

    r = L;     //尾指针 r 指向头结点

    for(int i = 0; i < n; ++i){
        p = new LNode;     //生成新结点 *p 
        cin>>p->data;     //输入元素值赋给新结点 *p 的数据域

        //将新结点 *p 插入尾结点 *r 之后
        p->next = NULL;    
        r->next = p;

        r = p;    // r 指向新的尾结点 *p
}

  • 32
    点赞
  • 197
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
西南交通大学的考研数据结构和C语言真题主要涵盖了数据结构和C语言的基本概念、常见算法数据结构的应用,是考研复习中的重点和难点。 数据结构部分的真题主要涉及线性表、栈和队列、链表和树、图和排序等知识点。例如,可能会出现关于数组的插入、删除和查找操作以及对其时间复杂度的分析题目,还可能会要求设计和实现单链表、二叉树或图等数据结构,并进行相应的操作和应用。对于这些题目,考生需要熟悉各种数据结构的特点、使用方法和算法,能够分析算法的时间复杂度和空间复杂度,并灵活应用到实际问题中。 C语言部分的真题主要考察C语言的基本语法、指针和内存管理、函数和库等方面的知识。可能会出现关于函数的声明和定义、指针的使用、内存动态分配和释放等方面的题目。考生需要对C语言的语法、特性和常用库函数有一定的掌握,能够理解和分析C语言程序的执行过程和内存管理机制。 对于准备西南交通大学考研的考生来说,要复习数据结构和C语言,首先要掌握基础概念和常用算法数据结构的原理和应用。其次,要多做真题和模拟题,加深对知识的理解和应用。同时,还要关注最新的考研动态和备考资料,及时调整和完善复习计划。通过系统的学习和不断的练习,相信考生一定能够顺利应对西南交通大学考研数据结构和C语言的考试。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

住在阳光的心里

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

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

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

打赏作者

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

抵扣说明:

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

余额充值