zoukankan      html  css  js  c++  java
  • 链表及数组模拟链表

    转载请注明出处,部分内容引自百度百科、谭浩强《C程序设计》、蜗牛君的奋斗史大神的博客



    前置知识:    C语言入门



    数组党的福音(本蒟蒻学链表时不会指针,然而好像所有人都拿指针写)
    首先,我们需要知道什么是链表
    百度百科
    看不懂勿喷(毕竟百度百科也不是用来让人看懂的)
    我们可以从中得出链表的特性:
    链表是一种物理存储单元上非连续、非顺序的存储结构
    提取主谓宾:链表是存储结构。我认为这就是链表的本质——一种数据结构。
    那么非连续、非线性有什么含义呢?这表明链表的内存是不连续的,前一个元素存储地址的下一个地址中存储的不一定是下一个元素。链表通过一个指向下一个元素地址的引用将链表中的元素串起来。
    链表分为三类:单向链表、双向链表、循环链表
    1,单向链表
    单向链表是最简单的链表形式。我们将链表中最基本的数据称为节点(node),每一个节点包含了数据块和指向下一个节点的指针,链表有一个头指针变量,图中以head表示。可以看出,head指向第一个元素,第一个元素又指向第二个元素……直到最后一个元素,该元素不再指向其他元素,它称为表尾,它的地址部分为空,链表到此结束。

    可以看到,要找链表中的某一元素,必须先找到上一个元素,根据它提供的下一个元素的地址才能找到下一个元素。如果不提供头指针,则整个链表都无法访问。链表如同一条铁链一样,一环扣一环,中间是不能断开的。
    为了理解什么是链表,打一个通俗的比方:幼儿园的老师带领孩子们出来散步,老师牵着第一个小孩的手,第一个小孩的另一只手牵着第二个孩子……这就是一个链,最后一个孩子有一只手空着,他是链尾。要找到这个队伍,必须先找到老师,然后顺序找到每一个孩子。
    变量声明:

    const int maxn=1010;
    struct node{    //point即指针,data就是需要维护的数据
        int point,data;
    }a[maxn];
    int head,cnt;    //head即头指针,cnt即内存池计数器

    建立:

    head=++cnt;        //把head设为没有实际意义的哨兵
    a[head].data=0;

    插入(插入数据now到第k个元素之后):

    add(++k,now);    //进入,因为计算时考虑了哨兵,所以进入时++k
    void add(int k,int now)
    {
        for(int i=head;i;i=a[i].point)    //从头指针开始遍历链表
            if(!(--k))        //到达插入位置
            {
                a[++cnt].point=a[i].point;    //将新插入节点的指针指向插入位置的后继
                a[i].point=cnt;        //将前驱节点的指针指向新插入节点
                a[cnt].data=now;
                break;
            }
    }


    删除(删除第k个元素):

    del(++k);    //进入
    void del(int k)
    {
        for(int i=head;i;i=a[i].point)
            if((--k)==1)    //找到前驱
            {
                a[i].point=a[a[i].point].point;        //将前驱的指针指向后继
                break;
            }
    }


    遍历链表:

    for(int i=a[head].point;i;i=a[i].point)
        cout<<a[i].data<<endl;

    2,双向链表
    顾名思义,双向链表就是有两个方向的链表。同单向链表不同,在双向链表中每一个节点不仅存储指向下一个节点的指针,而且存储指向前一个节点的指针。它的优点是访问、插入、删除更方便。但“是以空间换时间”。

    变量声明:

    struct node{
        int pre,nxt,data;    //前驱和后继
    }a[maxn];
    int head,cnt;

    插入:

    void add(int k,int now)
    {
        for(int i=head;i;i=a[i].nxt)
            if(!(--k))
            {
                a[++cnt].nxt=a[i].nxt;    //这两个和单链表一样
                a[i].nxt=cnt;
                a[a[cnt].nxt].pre=cnt;    //将后继的前驱更新为新插入节点
                a[cnt].pre=i;    //将新插入节点的前驱设为其前驱
                a[cnt].data=now;
                break;
            }
    }


    删除:

    void del(int k)
    {
        for(int i=head;i;i=a[i].nxt)
            if(!(--k))
            {
                a[a[i].pre].nxt=a[i].nxt;
                a[a[i].nxt].pre=a[i].pre;
                break;
            }
    }

    3,循环链表
    (1),单向循环链表
    最后一个节点的指针指向头结点
    (2),双向循环链表
    最后一个节点的指针指向头结点,且头结点的前驱指向最后一个结点
    代码实现就不再给出,相信大家已经能够实现。

  • 相关阅读:
    mysql5.7安装
    win10 安装docker
    快速去水印(win10换图3D工具)
    爬虫---国家食品药品监督管理总局
    食品伙伴网爬虫
    驴妈妈旅游爬虫
    天气预测(CNN)
    ConcurrentDictionary线程不安全么,你难道没疑惑,你难道弄懂了么?
    C#线程篇---线程池如何管理线程(6完结篇)
    C#线程篇---Task(任务)和线程池不得不说的秘密(5)
  • 原文地址:https://www.cnblogs.com/ivanovcraft/p/9037475.html
Copyright © 2011-2022 走看看