回 帖 发 新 帖 刷新版面

主题:线性表(链表)、栈与队列

线性表、栈与队列

一、结构定义
  线性表有两种方式表示,这里说的是存储方式,包括连续的数组表示和链表表示两种。仅以链表表示为例。

  首先要定义结点类型,总的来说一个结点应该包括两个部分,指针域和数据域。

  struct _DNode
  {
    LPVOID data;/*数据域*/
    LPVOID link;/*指针域*/
  };

  指针域很简单无非是前趋指针和后继指针,但数据域却是不定的,我们对这个链表的要求是要有高适应性,所以应该做一个开放的数据域,就用一个空指针表示吧。于是修改成:

  struct _DNode
  {
    LPVOID data;/*数据域*/
    LPVOID pre;
    LPVOID next;
  };

  这里我提倡一种习惯,就是善于使用宏。在C语言里宏是个非常好的工具,它帮助我们更好的书写代码。常用的方法就是用#define和typedef。

  严格来说只有#define是宏定义,但typedef我们可以看成是宏(别名),它可以更灵活的使用。

  在上面代码之前,添加

  typedef void far *LPVOID;
  typedef struct _DNode DNode;
  typedef DNode *LPDNode;

  于是结构体也可以再修改为

  struct _DNode
  {
    LPVOID data;/*数据域*/
    LPDNode pre;
    LPDNode next;
  };

  有了结点类型,就可以定义链表类型了。

  struct _DList
  {
    LPDNode head,tail;/*指向链表的头和尾,头表示第一个元素*/
    DWORD length;/*结点的个数*/
  };

  其中DWORD是双字,即32位长的数据类型,可以宏定义为typedef unsigned long DWORD;

  同样的在前面再加上它的宏名

  typedef struct _DList DList;
  typedef DList *LPDList;

  今后我们定义数据的时候都将直接使用这些宏名。用它的好处会在以后体会到的。

  OK,到这里顶点集定义完毕,我们要定义它们的边集,也就是常用操作。

二、常用操作
  线性表的操作想一想需要哪此,查找、插入、删除,另外还包括整个线性表的初始化和最后的销毁。全部操作列在下面

  BOOL InitList(LPDList *pl);/*初始化*/
  BOOL DestroyList(LPDList pl);/*销毁*/
  LPVOID GetElem(LPDList pl,int index,BOOL fromtail);/*查找*/
  BOOL ListInsert(LPVOID data,LPDList pl,int index,BOOL fromtail);/*插入*/
  BOOL ListDelete(LPDList pl,int index,BOOL fromtail);/*删除*/

  除了这些基本操作之外,还有一些很常用的

  BOOL ClearList(LPDList pl);/*清空链表*/
  DWORD ListLength(LPDList pl);/*返回链表长*/
  BOOL ListEmpty(LPDList pl);/*判断是否为空*/

  当然前面有宏名定义
  typedef int BOOL;
  #define TRUE 1
  #define FALSE 0

  以上这些定义和申明全部放在一个MyList.h文件中,然后在对应的MyList.c中写这些操作的实现代码。

  #include "MyList.h"
  #include "malloc.h"
  #include "assert.h"
  BOOL InitList(LPDList *pl)
  {
    (*pl)=(LPDList)malloc(sizeof(DList));
    if((*pl)==NULL)return FALSE;
    (*pl)->head=(*pl)->tail=NULL;
    (*pl)->length=0;
    return TRUE;
  }
  BOOL DestroyList(LPDList pl)
  {
    LPDNode p=pl->head,q;
    while(p!=NULL)
    {
       q=p;
       p=p->next;
       free(q);
    }
    free(pl);
  }
  LPVOID GetElem(LPDList pl,int index,BOOL fromtail)/*fromtail表示是否从尾部开始*/
  {
    LPDNode p;
    if(index<0||index>=pl->length)return NULL;
    if(fromtail)
    {
      p=pl->tail;
      while(index--)p=p->pre;
    }
    else
    {
      p=pl->head;
      while(index--)p=p->next;
    }
    return p->data;
  }
  BOOL ListInsert(LPVOID data,LPDList pl,DWORD index,BOOL fromtail)
  {
    LPDNode p,q;
    if(pl->length==0)
    {
      q=pl->head=pl->tail=(LPDNode)malloc(sizeof(DNode));
      q->next=q->pre=NULL;
      q->data=data;
      pl->length++;
      return TRUE;
    }
    if(index>=pl->length)return FALSE;
    q=(LPDNode)malloc(sizeof(DNode));
    q->data=data;
    if(fromtail)
    {
      p=pl->tail;
      while(index--)p=p->pre;
      q->pre=p;
      q->next=p->next;
      if(p->next)
        p->next->pre=q;
      else
        pl->tail=q;
      p->next=q;
    }
    else
    {
      p=pl->head;
      while(index--)p=p->next;
      q->next=p;
      q->pre=p->pre;
      if(p->pre!=NULL)
        p->pre->next=q;
      else
        pl->head=q;
      p->pre=q;
    }
    pl->length++;
    return TRUE;
  }
  BOOL ListDelete(LPDList pl,DWORD index,BOOL fromtail)
  {
    LPDNode p;
    if(index>=pl->length)return FALSE;
    if(fromtail)
    {
      p=pl->tail;
      while(index--)p=p->pre;
    }
    else
    {
      p=pl->head;
      while(index--)p=p->next;
    }
    if(p->pre)
      p->pre->next=p->next;
    else
      pl->head=p->next;
    if(p->next)
      p->next->pre=p->pre;
    else
      pl->tail=p->pre;
    free(p);
    pl->length--;
    return TRUE;
  }

  有了这些定义,就可以方便的操作链表了。

  其它操作
  BOOL ClearList(LPDList pl)
  {
    while(ListDelete(pl,0,FALSE));
    return pl->length==0;
  }
  最后两个完全用宏定义了:
  #define ListLength(pl) (pl->length)
  #define ListEmpty(pl) (pl->length==0)

  到这里就全部完成了。

三、栈与队列
  从本质上看,栈与队列都是线性表,所以没有必要再定义栈和队列,老规矩用宏。

  typedef DList DStack;
  typedef DList *LPDStack;
  typedef DList DQueue;
  typedef DList *LPDQueue;

  所谓栈,就是LIFO,后进先出,它的操作类似的有:
  #define InitStack(ps) InitList(ps)
  #define DestroyStack(ps) DestroyList(ps)
  #define GetTop(ps) GetItem(ps,0,FALSE)
  #define Push(ps) ListInsert(ps,0,FALSE)
  #define Pop(ps,e) ((e=GetItem(ps,0,FALSE)),ListDelete(ps,0,FALSE))

  此外还有
  #define StackLength(ps) ListLength(ps)
  #define StackEmpty(ps) ListEmpty(ps)
  #define ClearStack(ps) ClearList(ps)

  其中用得多的是Push(ps),压栈,和Pop(ps,e)弹栈。

  所谓队列,就是FIFO,先进先出,它的操作类似的有:
  #define InitQueue(pq) InitList(pq)
  #define DestroyQueue(pq) DestroyList(pq)
  #define GetHead(pq) GetItem(pq,0,FALSE)
  #define EnQueue(pq,e) ListInsert(e,pq,0,TRUE)
  #define DeQueue(pq,e) ((e=GetItem(pq,0,FALSE)),ListDelete(pq,0,FALSE))

  类似的还有
  #define QueueLength(pq) ListLength(pq)
  #define QueueEmpty(pq) ListEmpty(pq)
  #define ClearQueue(pq) ClearList(pq)

  其中用得多的是EnQueue(pq,e),入队,和DeQueue(pq,e)出队。

回复列表 (共49个回复)

41 楼

ADT不需要自己重新定义了的吧,是不是#define 是不是已经就这样带过去了?

42 楼

看过.谢谢版主!

43 楼

顶顶,看看队列.

44 楼


#include "DList.h"
#include <stdio.h>
最终实现代码少了#include "DList.c"哦,过不还是感谢楼主
好意

45 楼

谢谢斑竹!
这些代码对我们这些初学者来说真的很有用.
谢谢!!

46 楼

struct _DNode
  {
    LPVOID data;/*数据域*/
    LPVOID link;/*指针域*/
  };
...大哥。。。我这里看不懂。。。  LPVOID是甚么意思,好像没定义

47 楼

谢谢楼主!!!!

48 楼

很好,学习了。
最近在复习算法

49 楼

数据结构有关的系统类库有哪些啊?

我来回复

您尚未登录,请登录后再回复。点此登录或注册