精华内容
下载资源
问答
  • 原书这部分内容很多至少相对于循环链表是很多相信当你把单链表的指针域搞清楚后这部分应该难不倒你现在我的问题是能不能从单链表派生出双向链表 你可以有几种做法 一种就是先定义一双链节点但是它的名字必须叫Node...
  • 前面章节中,给读者详细介绍了双向链表及其基本操作。在此基础上,本节教大家:如何利用...1) 我们知道,双向链表中各个节点的标准构成是一个数据域和 2 个指针域,但对于实现贪吃蛇游戏来说,由于各个节点的位置是...

    前面章节中,给读者详细介绍了双向链表及其基本操作。在此基础上,本节教大家:如何利用双向链表实现一个简易的 C 语言版贪吃蛇游戏(如图 1 所示)。

    46c5c4a67a09305380a998fdce059766.png图 1 贪吃蛇小游戏的实现效果

    其中,黄色框代表贪吃蛇,红色  代表食物!

    使用双向链表实现此游戏,有以下几点需要做重点分析。1) 我们知道,双向链表中各个节点的标准构成是一个数据域和 2 个指针域,但对于实现贪吃蛇游戏来说,由于各个节点的位置是随贪吃蛇的移动而变化的,因此链表中的各节点还需要随时进行定位。在一个二维画面中,定义一个节点的位置,至少需要所在的行号和列号这 2 个数据。由此,我们可以得出构成贪吃蛇的双向链表中各节点的构成:

    //创建表示蛇各个节点的结构体typedef struct SnakeNode {    int x, y;//记录节点所在的行和列    struct SnakeNode *pre;//指向前驱节点的指针    struct SnakeNode *next;//指向后续节点的指针}Node, *pNode;

    2) 贪吃蛇的移动,本质上就是对链表中各个节点的重新定位。换句话说,除非贪吃蛇吃到食物,否则无论怎样移动,都不会对双向链表的整个结构(节点数)产生影响,唯一受影响的就只是各个节点中 (x,y) 这对定位数据。由此,我们可以试着设计出实现贪吃蛇移动的功能函数,本节所用的实现思想分为 2 步:

    1. 从蛇尾(双向链表尾节点)开始,移动向前遍历,过程中依次将当前节点的 (x,y) 修改为前驱节点的 (x,y),由此可实现整个蛇身(除首元节点外的其它所有节点)的向前移动;

    2. 接收用户输入的移动指令,根据用户指示贪吃蛇向左、向右、向上还是向下移动,首元节点中的 (x,y) 分别做 x-1、x+1、y-1 和 y+1 运算。

    如下所示,move() 函数就实现了贪吃蛇的移动:

    //贪吃蛇移动的过程,即链表中所有节点从尾结点开始逐个向前移动一个位置bool Move(pNode pHead, char key) {    bool game_over = false;    pNode pt = pTail;    while (pt != pHead) { // 每个节点依次向前完成蛇的移动        pt->x = pt->pre->x;        pt->y = pt->pre->y;        pt = pt->pre;    }    switch (key) {        case'd': {            pHead->x += 1;            if (pHead->x >= ROW)                game_over = true;            break;        }        case'a': {            pHead->x -= 1;            if (pHead->x < 0)                game_over = true;            break;        }        case's': {            pHead->y += 1;            if (pHead->y >= COL)                game_over = true;            break;        }        case'w': {            pHead->y -= 1;            if (pHead->y < 0)                game_over = true;;            break;        }    }    if (SnakeDeath(pHead))        game_over = true;    return game_over;}

    注意,此段代码中还调用了 SnakeDeath() 函数,此函数用于判断贪吃蛇移动时是否撞墙、撞自身,如果是则游戏结束。

    3) 当贪吃蛇吃到食物时,贪吃蛇需要增加一截,其本质也就是双向链表增加一个节点。前面章节中,已经详细介绍了如何在双向链表中增加一个节点,因此实现这个功能唯一的难点在于:如何为该节点初始化 (x,y)?本节所设计的贪吃蛇游戏,针对此问题,提供了最简单的解决方案,就是不对新节点 x 和 y 做初始化。要知道,贪吃蛇是时刻移动的,而在上面的 move() 函数中,会时刻修正贪吃蛇每个节点的位置,因此当为双向链表添加新节点后,只要贪吃蛇移动一步,新节点的位置就会自行更正。

    当然,读者也可发散思维,设计其他的解决方案。

    也就是说,贪吃蛇吃到食物的实现,就仅是给双向链表添加一个新节点。如下即为实现此功能的代码:

    //创建表示食物的结构体,其中只需要记录其所在的行和列typedef struct Food {    int x;    int y;}Food, *pFood;//吃食物,等同于链表中新增一个节点pNode EatFood(pNode pHead, pFood pFood) {    pNode p_add = NULL, pt = NULL;    if (pFood->x == pHead->x&&pFood->y == pHead->y) {        p_add = (pNode)malloc(sizeof(Node));        score++;        pTail->next = p_add;        p_add->pre = pTail;        p_add->next = NULL;        pTail = p_add;        // 检查食物是否出现在蛇身的位置上        do {            *pFood = CreateFood();        } while (FoodInSnake(pHead, pFood));    }    return pHead;}

    其中,Food 结构体用来表示食物,其内部仅包含能够定位食物位置的 (x,y) 即可。另外,此段代码中,还调用了 FoodeInSnake() 函数,由于食物的位置是随机的,因此极有可能会和贪吃蛇重合,所以此函数的功能就是:如果重合,就重新生成食物。

    FoodInSnake() 函数的实现很简单,这里不再赘述:

    //判断食物的出现位置是否和蛇身重合bool FoodInSnake(pNode pHead, pFood pFood) {    pNode pt = NULL;    for (pt = pHead; pt != NULL; pt = pt->next) {        if (pFood->x == pt->x&&pFood->y == pt->y)            return true;    }    return false;}

    4) 贪吃蛇游戏界面的显示,最简单的制作方法就是:贪吃蛇每移动一次,都清除屏幕并重新生成一次。这样实现的问题在于,如果贪吃蛇的移动速度过快,则整个界面在渲染的同时,会掺杂着光标,并且屏幕界面会频繁闪动。因此,在渲染界面时,有必要将光标隐藏起来,这需要用到头文件,实现代码如下:

    // 隐藏光标void gotoxy(int x, int y) {    HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);    COORD pos;    pos.X = x;    pos.Y = y;    SetConsoleCursorPosition(handle, pos);}void HideCursor() {    CONSOLE_CURSOR_INFO cursor_info = { 1, 0 };    SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cursor_info);}

    同时,为了给整个界面渲染上颜色,也需要引入头文件,并使用如下函数:

    void color(int m) {    HANDLE consolehend;    consolehend = GetStdHandle(STD_OUTPUT_HANDLE);    SetConsoleTextAttribute(consolehend, m);}

    5) 需要注意的一点是,由此结束后,一定要手动释放双向链表占用的堆空间:

    //退出游戏前,手动销毁链表中各个节点void ExitGame(pNode *pHead){    pNode p_delete = NULL, p_head = NULL;    while (*pHead != NULL) {        p_head = (*pHead)->next;        if (p_head != NULL)            p_head->pre = NULL;        p_delete = *pHead;        free(p_delete);        p_delete = NULL;        *pHead = p_head;    }}

    解决以上问题之后,用双向链表实现贪吃蛇,基本上就没有难点了。读者可根据本节提供的实现思想,尝试独立实现。

    本节设计实现的贪吃蛇游戏,源码文件有 3 个,分别为 snake.h、snake.c 和 main.c,读者可直接点击贪吃蛇游戏进行下载。

    90f49e8c5680c406d28ecdf7669a4b03.png

    展开全文
  • 顾名思义,双向链表每个结点个指针域,一个指向前驱节点,一个指向后继结点。 但是双向链表在创建,插入,和删除操作上略不同。例如在插入和删除时,不必像单链表一样只能在目标的前驱结点上操作 (觉得它能...

    嘿,\(≧▽≦)/ 前几天在书上看见了双向链表,就学了一下,哈哈哈(〜^㉨^)〜

    顾名思义,双向链表每个结点有两个指针域,一个指向前驱节点,一个指向后继结点。

    但是双向链表在创建,插入,和删除操作上略有不同。例如在插入和删除时,不必像单链表一样只能在目标的前驱结点上操作

    (觉得它能实现查询数据时上一个,下一个的功能哦!!)



    下面只是我的一些想法,可能很菜啊    ╮( ̄▽ ̄")╭;


    结点定义如下:

    typedef struct Node
    {
        int data;
        struct Node *prior;  //指向前驱
        struct Node *next;   //指向后继
    }Node,*pNode;    //这里 pNode 等价于 Node* 是指向结点的指针

    函数声明如下:

    pNode creat(int n);        //创建双向链表
    void output(pNode h);    //打印双向链表
    int if_empty(pNode);     //判空
    int length(pNode h);      //返回链表长度
    void Insert(pNode h,int i,int data);     //插入
    void Delete(pNode h,int i);    //删除
    


    各项功能的函数定义如下:

    (1)创建双链表

    pNode creat(int n)  //创建链表
    {
        int i;
        pNode h,p,r;
        h=r=(pNode)malloc(sizeof(Node));//其中h是头指针,r是尾指针
        if(h==NULL)    //内存分配失败
            exit(-1);
        h->prior=h->next=NULL;  //将头节点的两个指针域都指向NULL
        printf("输入数据:");
        for(i=0; i<n; i++)
        {
            p=(pNode)malloc(sizeof(Node));
            if(h==NULL)      //内存分配失败
                exit(-1);
            scanf("%d",&p->data);
            p->next=NULL;
            r->next=p;
            p->prior=r;
            r=r->next;
        }
        return h;
    }
    

      双向链表的创建我采用的是尾插法,因为如果是头插法则会有两种情况,1是在头结点和NULL之间插,2是在头结点与其后结点之间插,稍微麻烦一点点。而尾插法只需插在尾结点和NULL之间。在文章最后我附上了双向链表的头插法。


    (2)打印链表,判空,返回链表长度

    void output(pNode h)   //打印链表
    {
        pNode p=h->next;
        printf("打印链表:");
        while(p)
        {
            printf("%d ",p->data);
            p=p->next;
        }
    }
    
    int empty(pNode h)    //判断链表是否为空
    {
        pNode p=h->next;
        if(p==NULL)
            return 1;
        else
            return 0;
    }
    
    int length(pNode h)    //返回链表长度
    {
        int length=0;
        pNode p=h->next;
        while(p)
        {
            length++;
            p=p->next;
        }
        return length;
    }
    
    


    (3)插入操作

    void Insert(pNode h,int i,int data) //插入 1<=i<=length+1
    {
        pNode p,r;
        p=r=h;
        int j=0;
        if(i==length(h)+1) //当插入的位置为length+1时
        {
            pNode q=(pNode)malloc(sizeof(Node));
            q->data=data;
            q->next=NULL;
            while(r->next) //找到链表尾结点
                r=r->next;
            r->next=q;
            q->prior=r;
        }
        else
        {
            while(p&&(j<i))  //找到第i个结点的
            {
                p=p->next;
                j++;
            }
            if(!p||(j>i)) 
                exit(-1);
            pNode q=(pNode)malloc(sizeof(Node));
            q->data=data;
            //插入的核心步骤
            q->prior=p->prior;
            p->prior->next=q;
            q->next=p;
            p->prior=q;
            //
        }
    }
    
      1.插入有两种情况,一是在两结点之间,另外一个是最后一个节点与NULL之间。

      2.对于两节点之间插入的情况,指针p指向了其中一个节点,则新结点先与另一个结点连接,之后再与该结点连接。因为指针p已经记录了该结点的位置,不用担心指向它的指针域被覆盖。

    (3)删除操作

    void Delete(pNode h,int i)  //删除
    {
        pNode p=h;
        int j=0;
        while(p&&(j<i))  //找到删除结点
        {
            p=p->next;
            j++;
    
        }
        if(p->next==NULL)
            p->prior->next=NULL;
        else
        {
            p->prior->next=p->next;
            p->next->prior=p->prior;
        }
        free(p);
    }

      删除操作有p->next==NULL和p->next!=NULL两种情况


    用于测试的主函数

    int main()
    {
        int n,i,data;
        pNode head;
        printf("输入输入数据个数");
        scanf("%d",&n);
        head=creat(n);
        output(head);
        printf("\n输入插入 位置i 和 数据data :\n");
        scanf("%d",&i);
        scanf("%d",&data);
        Insert(head,i,data);
        output(head);
    
    
        printf("\n要删除第几个数据");
        scanf("%d",&i);
        Delete(head,i);
        output(head);
        return 0;
    }


    \(≧▽≦)/\(≧▽≦)/\(≧▽≦)/\(≧▽≦)/\(≧▽≦)/\(≧▽≦)/~~~~~~先结束了。




    附录

    双向链表的头插法

    pNode creat(int n)
    {
        int i;
        pNode h,p;
        h=(pNode)malloc(sizeof(Node));
        if(h==NULL)
            exit(-1);
        h->prior=h->next=NULL;
        printf("输入数据:");
        p=(pNode)malloc(sizeof(Node));
        if(h==NULL)
            exit(-1);
        scanf("%d",&p->data);
        h->next=p;
        p->prior=h;
        p->next=NULL;
        for(i=1; i<n; i++)
        {
            p=(pNode)malloc(sizeof(Node));
            if(h==NULL)
                exit(-1);
            scanf("%d",&p->data);
            p->next=h->next;
            h->next->prior=p;
            h->next=p;
            p->prior=h;
        }
        return h;
    }
    



    转载于:https://www.cnblogs.com/zhanyeye/p/9746132.html

    展开全文
  • 又一 C++ 双向链表

    千次阅读 2008-03-17 10:18:00
    你可以有几种做法:一种就是先定义一双链节点——但是,它的名字必须叫Node,这是没办法的事;不然你就只好拷贝一份单链表的实现文件,把其中的Node全都替换成你的双链节点名字,但是这就不叫继承了。另一种做法...
    原书这部分内容很多,至少相对于循环链表是很多。相信当你把单链表的指针域搞清楚后,这部分应该难不倒你。现在我的问题是,能不能从单链表派生出双向链表?
    你可以有几种做法:
    一种就是先定义一个双链节点——但是,它的名字必须叫Node,这是没办法的事;不然你就只好拷贝一份单链表的实现文件,把其中的Node全都替换成你的双链节点名字,但是这就不叫继承了。
    另一种做法就是先定义一种结构例如这样的:
    template <class Type> class newtype
    {
    public:
    Type data;
    Node<newtype> *link;
    }
    当你派生双向链表时,这样写template <calss Type> class DblList : public List<newtype<Type> >,注意连续的两个“>”之间要有空格。或者根本不定义这样的结构,直接拿Node类型来做,例如我下面给出的。但是,请注意要完成“==” 的重载,否则,你又要重写Find函数,并且其他的某些操作也不方便。
    在开始完成你的从单链表派生出来的双向链表之前,要在单链表这个基类中添加修改当前指针和当前前驱指针的接口,如下所示:
    protected:
           void Put(Node<Type> *p)//尽量不用,双向链表将使用这个完成向前移动
           {
                  current = p;
           }
     
           void PutPrior(Node<Type> *p)//尽量不用,原因同上
           {
                  prior = p;
           }
    因为这个接口很危险,而且几乎用不到,所以我在前面并没有给出,但要完成双向链表最“杰出”的优点——向前移动当前指针,必须要使用。另外说的是,我从前也从来没计划从单链表派生双链表,下面你将看到,这个过程很让人烦人,甚至不如重写一个来的省事,执行效率也不是很好,这种费力不讨好的事做它有什么意思呢?的确,我也觉得我在钻牛角尖。(别拿鸡蛋丢我)

    定义和实现

    #ifndef DblList_H
    #define DblList_H
     
    #include "List.h"
     
    template <class Type> class DblList : public List< Node<Type> >
    {
    public:
           Type *Get()
           {
                  if (pGet() != NULL) return &pGet()->data.data;
                  else return NULL;
           }
     
           Type *Next()
           {
                  pNext();
                  return Get();
           }
     
           Type *Prior()
           {
                  if (pGetPrior != NULL)
                  {
                         Put(pGetPrior());
                         PutPrior( (Node< Node<Type> >*)pGet()->data.link);
                         return Get();
                  }
                  return NULL;
           }
     
           void Insert(const Type &value)
           {
                  Node<Type> newdata(value, (Node<Type>*)pGet());
                  List< Node<Type> >::Insert(newdata);
                  if (pGetNext()->link != NULL)
    pGetNext()->link->data.link = (Node<Type>*)pGetNext();
           }
     
           BOOL Remove()
           {
                  if (List< Node<Type> >::Remove())
                  {
                         pGet()->data.link = (Node<Type>*)pGetPrior();
                         return TURE;
                  }
                  return FALSE;
           }
     
    };
     
    #endif
    【说明】只完成了最重要的Insert和 Remove函数和最具特点的Prior()函数,其他的没有重新实现。所以,你在这里使用单链表的其他方法,我不保证一定正确。并且,这里的指针类型转换依赖于编译器实现,我也不能肯定其他的编译器编译出来也能正确。对于让不让Prior返回头节点的data,我考虑再三,反正用First();Get ();这样的组合也能返回,所以就不在乎他了,所以要是用Prior遍历直到返回NULL,就会将头节点的data输出来了。
    【补充】至于双向循环链表,也可以从这个双向链表派生(仿照派生循环链表的方法);或者从循环链表派生(仿照派生双向链表的方法),就不一一举例了(再这样下去,我就真闹心的要吐血了)。至此,可以得出一个结论,链表的各种结构都是能从单链表派生出来的。换句话说,单链表是根本所在,如果研究透了单链表,各种链式结构都不难。

    一小段测试程序

    void DblListTest_int()
    {
           DblList<int> a;
           for (int i = 10; i > 1; i--) a.Insert(i);
           for (i = 10; i > 1; i--) cout << *a.Next() << " ";
           a.First();
           cout << endl;
           cout << *a.Next() << endl;
           cout << *a.Next() << endl;
           cout << *a.Next() << endl;
           cout << *a.Next() << endl;
           a.Remove();
           cout << *a.Get() << endl;
           cout << *a.Prior() << endl;
           cout << *a.Prior() << endl;
           cout << *a.Prior() << endl;
    }
    【后记】从我对双向链表不负责任的实现来看,我并不想这么来实现双向链表,我只是尝试怎样最大限度的利用已有的类来实现这种类型。实践证明,不如重写一个。别人看起来也好看一些,自己写起来也不用这样闹心。不过,这个过程让我对函数的调用和返回的理解又更深了一步。如果你能第一次就写对这里的Insert函数,相信你一定对C++有一定的感触了。我也觉得,只有做一些创新,才能最已经很成熟的东西更深入的了解。比如,这些数据结构,在C++的标准库(STL)中都可以直接拿来用,我们为什么还辛辛苦苦的写,结果还不如人家原来的好。为了学习,这就是理由,这也是一切看起来很笨的事发生的理由。
     
    展开全文
  • 数据结构学习之双向链表

    千次阅读 2004-12-23 14:13:00
    你可以有几种做法: 一种就是先定义一双链节点--但是,它的名字必须叫Node,这是没办法的事;不然你就只好拷贝一份单链表的实现文件,把其中的Node全都替换成你的双链节点名字,但是这就不叫继承了。另一

    作者:happycock 来自:Yesky

    原书这部分内容很多,至少相对于循环链表是很多。相信当你把单链表的指针域搞清楚后,这部分应该难不倒你。现在我的问题是,能不能从单链表派生出双向链表?

    你可以有几种做法:

      一种就是先定义一个双链节点--但是,它的名字必须叫Node,这是没办法的事;不然你就只好拷贝一份单链表的实现文件,把其中的Node全都替换成你的双链节点名字,但是这就不叫继承了。
    另一种做法就是先定义一种结构例如这样的:

    template <class Type> class newtype
    {
    public:
    Type data;
    Node<newtype> *link;
    }


      当你派生双向链表时,这样写template <calss Type> class DblList : public List<newtype<Type> >,注意连续的两个">"之间要有空格。或者根本不定义这样的结构,直接拿Node类型来做,例如我下面给出的。但是,请注意要完成"=="的重载,否则,你又要重写Find函数,并且其他的某些操作也不方便。

      在开始完成你的从单链表派生出来的双向链表之前,要在单链表这个基类中添加修改当前指针和当前前驱指针的接口,如下所示:

    protected:
    void Put(Node<Type> *p)//尽量不用,双向链表将使用这个完成向前移动
    {
    current = p;
    }

    void PutPrior(Node<Type> *p)//尽量不用,原因同上
    {
    prior = p;
    }


      因为这个接口很危险,而且几乎用不到,所以我在前面并没有给出,但要完成双向链表最"杰出"的优点--向前移动当前指针,必须要使用。另外说的是,我从前也从来没计划从单链表派生双链表,下面你将看到,这个过程很让人烦人,甚至不如重写一个来的省事,执行效率也不是很好,这种费力不讨好的事做它有什么意思呢?的确,我也觉得我在钻牛角尖。

      定义和实现

    #ifndef DblList_H
    #define DblList_H

    #include "List.h"

    template <class Type> class DblList : public List< Node<Type> >
    {
    public:
    Type *Get()
    {
    if (pGet() != NULL) return &pGet()->data.data;
    else return NULL;
    }

    Type *Next()
    {
    pNext();
    return Get();
    }

    Type *Prior()
    {
    if (pGetPrior != NULL)
    {
    Put(pGetPrior());
    PutPrior( (Node< Node<Type> >*)pGet()->data.link);
    return Get();
    }
    return NULL;
    }

    void Insert(const Type &value)
    {
    Node<Type> newdata(value, (Node<Type>*)pGet());
    List< Node<Type> >::Insert(newdata);
    if (pGetNext()->link != NULL)
    pGetNext()->link->data.link = (Node<Type>*)pGetNext();
    }

    BOOL Remove()
    {
    if (List< Node<Type> >::Remove())
    {
    pGet()->data.link = (Node<Type>*)pGetPrior();
    return TURE;
    }
    return FALSE;
    }

    };

    #endif


      【说明】只完成了最重要的Insert和Remove函数和最具特点的Prior()函数,其他的没有重新实现。所以,你在这里使用单链表的其他方法,我不保证一定正确。并且,这里的指针类型转换依赖于编译器实现,我也不能肯定其他的编译器编译出来也能正确。对于让不让Prior返回头节点的data,我考虑再三,反正用First();Get();这样的组合也能返回,所以就不在乎他了,所以要是用Prior遍历直到返回NULL,就会将头节点的data输出来了。

      【补充】至于双向循环链表,也可以从这个双向链表派生(仿照派生循环链表的方法);或者从循环链表派生(仿照派生双向链表的方法),就不一一举例了(再这样下去,我就真闹心的要吐血了)。至此,可以得出一个结论,链表的各种结构都是能从单链表派生出来的。换句话说,单链表是根本所在,如果研究透了单链表,各种链式结构都不难。

      一小段测试程序

    void DblListTest_int()
    {
    DblList<int> a;
    for (int i = 10; i > 1; i--) a.Insert(i);
    for (i = 10; i > 1; i--) cout << *a.Next() << " ";
    a.First();
    cout << endl;
    cout << *a.Next() << endl;
    cout << *a.Next() << endl;
    cout << *a.Next() << endl;
    cout << *a.Next() << endl;
    a.Remove();
    cout << *a.Get() << endl;
    cout << *a.Prior() << endl;
    cout << *a.Prior() << endl;
    cout << *a.Prior() << endl;
    }


      【后记】从我对双向链表不负责任的实现来看,我并不想这么来实现双向链表,我只是尝试怎样最大限度的利用已有的类来实现这种类型。实践证明,不如重写一个。别人看起来也好看一些,自己写起来也不用这样闹心。不过,这个过程让我对函数的调用和返回的理解又更深了一步。如果你能第一次就写对这里的Insert函数,相信你一定对C++有一定的感触了。我也觉得,只有做一些创新,才能最已经很成熟的东西更深入的了解。比如,这些数据结构,在C++的标准库(STL)中都可以直接拿来用,我们为什么还辛辛苦苦的写,结果还不如人家原来的好。为了学习,这就是理由,这也是一切看起来很笨的事发生的理由。

    展开全文
  • 链表的示意图

    千次阅读 2017-04-13 17:12:32
    带头链表:固定一个节点作为头结点(数据域不保存有效数据),起一个标志位的作用,以后不管链表节点如果改变,此头结点固定不变。 ...不带头链表:头结点不固定,...双向链表:节点的指针域有个指针,可以从正反
  • 数据结构学习(C++)——双向链表

    千次阅读 2003-06-21 13:35:00
    你可以有几种做法:一种就是先定义一双链节点——但是,它的名字必须叫Node,这是没办法的事;不然你就只好拷贝一份单链表的实现文件,把其中的Node全都替换成你的双链节点名字,但是这就不叫继承了。另一种做法...
  • (1)在数据结构上附加一个域,使它存放指向前一结点的指针; (2)增加了一头结点,前驱指向链表的最后一结点; (3)链表的最后一结点指向头结点,使它构成一循环链表。 数据类型的定义 typedef int Da...
  • 双链表简介 ...所以在双向链表中的结点都个指针域,一个指向直接后继,另一个指向直接前驱。 所以,双向链表既可以从前往后查找,也可以从后往前。 示意图如下: 双链表的种操作 一、遍历 方法和单
  •  一般情况下我们会使用一个结构体head来作为双向链表的头部,head个指针域,一个指向链表的开始,一个指向链表的末尾。双向链表的指针域也两个,一个指向前一个节点,一个指向后一个节点。两个结构体定义如下...
  • 链表是实现线性表的链式存储结构的一种数据结构,链表根据结定义不同也有几个种类,分别为: 静态链表:用于没有指针类型的语言中。 单链表:链表的结点中只有指向直接后继结点的指针域。 循环链表:表中最后一个...
  • 双向链表:一个节点个指针域,分别指向前一个节点和后一个节点 循环链表:能通过任何一个节点找到其他所有的节点,将两种(双向/单向)链表的最后一个结点指向第一个结点从而实现循环 此处先讲解单向链表,单向...
  • 目录概念种常见链表单链表插入、删除查询循环链表双向链表对比单链表删除操作插入操作按值查询操作 概念 链表是一种物理存储单元上非连续、非顺序的存储结构,通过指针将一组零散的内存块串联在一起。由一系列结点...
  • 用链接关系显示表示元素之间的顺序关系称为...单向链表的节点是一二元组,元素域保存数据,指针域指向下一节点。 单向链表需要一变量指向首节点,尾结点需要指向空。 定义一节点: class Node(object): ...
  • 相对于线性表的顺序存储结构,不连续的存储结构就是链表了,针对链表以下几个注意点: 链表的存储空间不一定连续 对链表中的节点进行访问与位置有关,时间复杂度为O(n) 链表的插入与删除与位置无关,时间...
  • 原书这部分内容很多,至少相对于循环链表是很多。相信当你把单链表的指针域搞清楚后,这部分应该难不倒你。...你可以有几种做法: 一种就是先定义一双链节点--但是,它的名字必须叫Node,这是没办法的事;
  • 《数据结构 1800题》

    热门讨论 2012-12-27 16:52:03
    双向链表 11.在下面的程序段中,对 x的赋值语句的频度为(C )【北京工商大学 2001 一、10(3分)】 FOR i:=1 TO n DO FOR j:=1 TO n DO x:=x+1; A. O(2n) B.O(n) C.O(n2) D.O(log2n) 12.程序段 ...
  • 结构指针变量的说明和使用一个指针变量当用来指向一个结构变量时, 称之为结构指针变量。 结构指针变量中的值是所指向的结构变量的首地址。 通过结构指针即可访问该结构变量, 这与数组指针和函数指针的情况是相同的...
  • 链式存储结构就是在每个结点中至少包含一个指针域,用指针来体现数据元素之间逻辑上的联系。 1.2.2 线性结构和非线性结构 根据数据结构中各数据元素之间前后件关系的复杂程度,一般将数据结构分为两大类型:线性结构...
  • 数据结构(C++)有关练习题

    热门讨论 2008-01-02 11:27:18
    b. 创建一班机链表,每节点都包含指向一乘客链表指针; c. 该程序要顾客购票,查询班机起飞降落时间,班机订票情况等3功能,并实现菜单选项 5、 用C++编写一简单的行编辑器,每结点保存一...
  • 11.2.4 双向链表 420 11.2.5 结构中的位字段 423 11.3 结构与函数 424 11.3.1 结构作为函数的变元 424 11.3.2 结构指针作为函数变元 425 11.3.3 作为函数返回值的结构 426 11.3.4 修改程序 430 11.3.5 ...
  • 11.2.4 双向链表 420 11.2.5 结构中的位字段 423 11.3 结构与函数 424 11.3.1 结构作为函数的变元 424 11.3.2 结构指针作为函数变元 425 11.3.3 作为函数返回值的结构 426 11.3.4 修改程序 430 11.3.5 ...
  • 10.5 链表 10.6 有序表 10.7 字典 10.7.1 键的类型 10.7.2 字典示例 10.7.3 Lookup类 10.7.4 其他字典类 10.8 HashSet 10.9 位数组 10.9.1 BitArray 10.9.2 BitVector32 10.10 性能 10.11 小结 第11章 Language ...
  • C#编程经验技巧宝典

    热门讨论 2008-06-01 08:59:33
    27 <br>0056 强行改变运算符的运算顺序 27 <br>第3章 程序算法 29 <br>3.1 数据结构 30 <br>0057 如何实现单向链表 30 <br>0058 如何实现双向链表 35 <br>0059 如何实现堆栈 41 ...

空空如也

空空如也

1 2
收藏数 26
精华内容 10
关键字:

双向链表有几个指针域