小猪的数据结构辅助教程——2.2 线性表中的单链表

小猪的数据结构辅助教程——2.2 线性表中的单链表

标签(空格分隔): 数据结构


本节学习路线图与学习要点

学习要点

  • 1.理解顺序表以及单链表各自的有点以及缺点!
  • 2.熟悉单链表的形式,对于头指针,头结点,尾结点,数据域和指针域这些名词要知道是什么!
  • 3.熟悉单链表的结点结构
  • 4.区分头指针头结点
  • 5.熟悉创建单链表的两种方式:头插法和尾插法
  • 6.了解单链表12个基本操作的逻辑
  • 7.有趣的算法题:查找单链表的中间结点~

1.单链表的引入(顺序表与单链表的PK)


2.单链表的结构图以及一些名词


3.单链表结点的存储结构

从上面的结构图我们就知道单链表的结点的结构分为数据域和指针域,对应的存储结构的代码如下:

typedef struct LNode
{
    ElemType data;         //数据域
    struct LNode *next;   //指针域
}LNode,*LinkList;         //定义两个只是方便使用而已,Linklist是一个结点指针哦~ 

4.头指针与头结点的比较

2中我们看到了头指针和头结点,大家可能对这两个名词有点疑惑,下面我们来解释下~


5.头插法与尾插法创建表的比较

两种方法的区别无非是插入的位置:

头插法:新插入结点始终未当前的第一个结点

尾插法:新插入结点始终为当前的最后一个结点

1)头插法建表

实现代码:

//头插法建链表
void HeadCreateList(LinkList L,int n)
{
    LinkList p;
    int i;
    srand(time(0));    //初始化随机数种子
    L = (LinkList)malloc(sizeof(LNode));
    L ->next = NULL;
    //利用循环生成结点并添加到单链表中
    for(i = 0;i < n;i++)
    {
        p = (LinkList)malloc(sizeof(LNode));    //生产新结点
        p ->data = rand()%100 + 1;              //生产两位随机数100,三位就是1000
        p ->next = L ->next;
        L ->next = p;                      //插到表头
    }
}

2)尾插法建表

实现代码:

void TailCraeteList(LinkList L,int n)
{
    LinkList p,r;
    int i;
    srand(time(0));    //初始化随机数种子
    L = (LinkList)malloc(sizeof(LNode));
    r = L;
    for(i = 0;i < n;i++)
    {
        p = (LinkList)malloc(sizeof(LNode));    //生产新结点
        p ->data = rand()%100 + 1;              //生产两位随机数100,三位就是1000
        r ->next = p;        //将表尾终端结点的指针指向新结点
        r = p;      //将当前的新结点定义为表尾的尾结点
    }
    r->next = NULL;      //当前链表结束
} 

6.有趣的算法:查找单链表的中间结点

就是给你一个单链表,要你获得单链表中位置中间的结点?你会怎么做?

一般我们可能用一个指针,从头到尾撸一遍,同时记录单链表的长度,然后再除以2得出第几

项为中间结点,然后再撸length / 2获得中间节点,重复遍历很繁琐,有没有其他的方法呢?

有,肯定有,这里提供一个简单的方法:

用两个不同的指针,按照不同的移动顺序来移动,这里我们暂且把他们成为快慢指针

每次循环,

**快指针向后移动两个结点: p = p -> next -> next;

慢指针向后移动一个结点: q = q -> next;**

当快指针到达尾部的时候,慢指针不就指向中间结点了,你说是吧~

原理非常简单,下面我们写下代码实现:

Status GetMidLNode(LinkList *L,ElemType *e)
{
    LinkList p,q;
    p = q = *L;
    while(p ->next ->next != NULL)
    {
        if(p ->next ->next != NULL)
        {
            p = p ->next ->next;
            q = q ->next;
        }else{
            p = p ->next;
        }
    }
    e = q ->data;
    return OK;
} 

7.12种基础基本操作代码实现

从本节开始就不像上一节一样一步步地讲解了,直接上代码,难点部分会写下注释!

1)构造空表

void InitList(LinkList L)
{
    L = (LinkList)malloc(sizeof(LNode));
    if(!L)exit(ERROR);
    L ->next = NULL;
}

2)将链表置为空表

void ClearList(LinkList L)
{
    LinkList p = L ->next;
    L ->next = NULL;
    //接着就是释放头结点以外的结点了
    while(p)
    {
        p = L->next;
        free(L);  //释放首元结点
        L = p;    //移动到当前的首元结点
    }
} 

3)判断是否为空表

这里要区分两种情况:

**有头结点:L -> next = NULL;此时表为空表!

无头结点:L = NULL;此时为空表!**

Status ListEmpty(LinkList L)
{
    //有头节点的情况,只需判断头结点的指针域是否为空即可
    if(L ->next)return FALSE;
    else return TRUE;
}

4)销毁单链表

void DestoryList(LinkList L)
{
    LinkList q;
    //删除头结点外的结点
    while(L)
    {
        //指向首元结点,而不是头结点
        q = L ->next;
        free(L);
        L = q;      //删除后指向首元
    }
} 

5)获得表长度

int ListLength(LinkList L)
{
    int i = 0;
    LinkList p = L ->next;
    while(p)
    {
        i++;
        p = p ->next;
    }
    return i;
} 

6)获得表中第i个元素的值

Status GetElem(LinkList L,int i,ElemType *e)
{
    int j = 1;
    //指向首元,然后依次后移,假如到了结尾或者j的值大于i
    //还没找个改元素说明i不合法
    LinkList p = L ->next;
    while(p && j < i)
    {
        j++;
        p = p ->next;
    }
    if(!p || j> i)return ERROR;
    e = p ->data;
    return OK;
}

7)查找表中是否存在满足条件的元素

int LocateElem(LinkList L,ElemType e,Status(*compare)(ElemType,ElemType))
{
    int i = 0;
    LinkList p = L -> next;
    while(p)
    {
        i++;
        if(compare(p->data,e))return i;
        p = p -> next;
    }
    return 0;
} 

8)获得某个结点的直接前驱

Status BeforeElem(LinkList L,ElemType choose,ElemType *before)
{
    LinkList q,p = L ->next;
    while(p ->next)
    {
        q = p ->next;
        //判断p的后继是否为choose,是的话返回OK,否则继续后移
        if(q ->data == choose)
        {
            before = p ->data;
            return OK;
        }
        p = q;
    }
    return ERROR;
}

9.获得某个结点的直接后继

Status NextElem(LinkList L,ElemType choose,ElemType *behind)
{
    LinkList p = L ->next;
    while(p ->next)
    {
        if(p ->data == choose)
        {
            behind = p ->next ->data;
            return OK;
        }
        p = p ->next;
    }
    return ERROR;
}

10.往表中第i个位置插入元素

Status ListInsert(LinkList L,int i,ElemType e)
{
    int j = 0;
    LinkList p,q =L;  //让q指向头结点
    while(p && j < i - 1)
    {
        j++;
        p = p ->next;  //p指向下一个节点
    }
    if(!p || j > i - 1)return ERROR;
    p = (LinkList)malloc(sizeof(LNode));
    //要先让插入的结点的指针域指向插入位置的后继结点
    //再让插入节点的前驱的指针域指向插入结点
    //!!!顺序不能乱哦1
    p ->data = e;
    p ->next = q ->next;
    q ->next = p;
    return OK;
} 


11.删除表中第i个元素

Status ListDelete(LinkList L,int i,ElemType *e)
{
    int j = 0;
    LinkList p,q = L;
    while(q ->next && j < i -1)
    {
        j++;
        q = q->next;
    }
    if(!q || j >i -1)return ERROR;
    p = q ->next;   //指向准备删除的结点
    q ->next = p ->next; //删除结点的前驱的指针域指向删除结点的后继
    e = p ->data;
    free(p);    //释放要删除的结点
    return OK;
}


12.遍历单链表中的所有元素

void ListTraverser(LinkList L,void(*visit)(ElemType))
{
    LinkList p = L ->next;
    while(p)
    {
        visit(p ->data);
        p = p ->next;
    }
    printf("\n");
}

8.本节代码下载:

https://github.com/coder-pig/Data-structure-auxiliary-tutorial/blob/master/List/list2.c

时间: 2024-10-07 06:10:02

小猪的数据结构辅助教程——2.2 线性表中的单链表的相关文章

小猪的数据结构辅助教程——2.7 线性表中的双向循环链表

小猪的数据结构辅助教程--2.7 线性表中的双向循环链表 标签(空格分隔): 数据结构 本节学习路线图与学习要点 学习要点: 1.了解引入双向循环链表的原因 2.熟悉双向循环链表的特点以及存储结构 3.掌握双向循环链表的一些基本操作的实现逻辑 4.掌握逆序输出双向循环链表元素逻辑 1.双向循环链表的引入 2.双向循环链表的存储结构 双向循环链表的特点: 上面也说了,空间换时间,比起循环链表只是多了一个指向前驱的指针 特点的话: 判断空表:L ->next = L -> prior = L; 存

小猪的数据结构辅助教程——2.4 线性表中的循环链表

小猪的数据结构辅助教程--2.4 线性表中的循环链表 标签(空格分隔): 数据结构 本节学习路线图与学习要点 学习要点: 1.了解单链表存在怎样的缺点,暴露出来的问题 2.知道什么是循环单链表,掌握单链表的特点以及存储结构 3.掌握循环链表的一些基本操作的实现逻辑,最好能手撕代码 1.循环单链表的引入 2.循环链表的特点以及存储结构 循环链表的特点: 上面也说了,比单链表稍微高比格一点的地方就是: 链表最后一个结点的指针域指向了头结点而已,这样形成所谓的环,就是循环单链表了,呵呵! 特点的话有:

小猪的数据结构辅助教程——2.1 线性表中的顺序表

小猪的数据结构辅助教程--2.1 线性表中的顺序表 标签(空格分隔): 数据结构 本节学习路线图与学习要点 学习要点: 1.抽象数据类型(ADT)的概念,三要素:数据,数据元素间的关系和数据的操作 2.线性表的特点:按照一条线排列的数据集合,1对1,除了首元和尾元,其他元素都有直接前驱和直接后继 3.牢记线性表的存储结构,要理解并熟悉12个基本操作的逻辑,最好能徒手撕出代码 4.求并集,顺序表的经典例子,必须掌握! 1.抽象的数据类型 简单点说: 抽象:有点像我们面向对象语言中的类的思想,将事物

小猪的数据结构辅助教程——3.1 栈与队列中的顺序栈

小猪的数据结构辅助教程--3.1 栈与队列中的顺序栈 标签(空格分隔): 数据结构 本节学习路线图与学习要点 学习要点 1.栈与队列的介绍,栈顶,栈底,入栈,出栈的概念 2.熟悉顺序栈的特点以及存储结构 3.掌握顺序栈的基本操作的实现逻辑 4.掌握顺序栈的经典例子:进制变换的实现逻辑 1.栈与队列的概念: 嗯,本节要进行讲解的就是栈 + 顺序结构 = 顺序栈! 可能大家对栈的概念还是很模糊,我们找个常见的东西来拟物化~ 不知道大家喜欢吃零食不--"桶装薯片"就可以用来演示栈! 生产的时

小猪的数据结构辅助教程——2.5 经典例子:约瑟夫问题的解决

小猪的数据结构辅助教程--2.5 经典例子:约瑟夫问题的解决 标签(空格分隔): 数据结构 约瑟夫问题的解析 关于问题的故事背景就不提了,我们直接说这个问题的内容吧: 一堆人,围成一个圈,然后规定一个数N,然后依次报数,当报数到N,这个人自杀,其他人鼓掌!啪啪啪, 接着又从1开始报数,报到N又自杀-以此类推,直到死剩最后一个人,那么游戏结束! 这就是问题,而我们用计算机模拟的话,用户输入:N(参与人数),M(第几个人死),结果返回最后一个人! 类似的问题有跳海问题,猴子选王等,下面我们就以N =

小猪的数据结构辅助教程——3.2 栈与队列中的链栈

小猪的数据结构辅助教程--3.2 栈与队列中的链栈 标签(空格分隔): 数据结构 1.本节引言: 嗯,本节没有学习路线图哈,因为栈我们一般都用的是顺序栈,链栈还是顺带提一提吧, 栈因为只是栈顶来做插入和删除操作,所以较好的方法是将栈顶放在单链表的头部,栈顶 指针与单链表的头指针合二为一~所以本节只是讲下链栈的存储结构和基本操作! 2.链栈的存储结构与示意图 存储结构: typedef struct StackNode { SElemType data; //存放的数据 struct StackN

小猪的数据结构辅助教程——1.数据结构与算法绪论

小猪的数据结构辅助教程--1.数据结构与算法绪论 标签(空格分隔): 数据结构 本节学习路线图与学习要点 学习要点: 1.了解数据结构的相关概念 2.了解算法的相关概念 3.熟悉时间复杂度的计算 4.了解空间复杂度的概念,闰年表空间换时间的样例~ 1.什么是数据结构? 2.算法的叙述 3.时间复杂度计算的简单演示样例 数据结构预算法--时间复杂度分析实例

小猪的数据结构辅助教程——2.6 经典例子:魔术师发牌问题和拉丁方阵问题

小猪的数据结构辅助教程--2.6 经典例子:魔术师发牌问题和拉丁方阵问题 标签(空格分隔): 数据结构 本节引言: 本节继续带来的是循环链表的两个经典例子,分别是魔术师发牌问题和拉丁方阵问题! 1.魔术师发牌问题 问题描述: 魔术师利用一副牌中的13张黑桃牌,预先将他们排好后叠放在一起,牌面朝下.对观众说:"我不看牌,只数数就可以猜到每张牌是什么,我大声数数,你们听,不信?现场演示."魔术师将牌堆最上面的哪张排数为1,把他翻过来正好是黑桃A,将黑桃A从牌堆抽出放在桌子上,第二次数1.2

线性表之循环单链表

#include<iostream> using namespace std; struct LinkNode { int value; LinkNode* next; }; LinkNode* createRoundLinkList() { LinkNode* head = (LinkNode*)malloc(sizeof(LinkNode)); head->next=head; head->value=0; return head; } bool insertElem(int