回 帖 发 新 帖 刷新版面

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

线性表、栈与队列

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

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

  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个回复)

沙发

这里是通过调试的代码:

/*File: DList.h*/
#ifndef _DLIST_DCL
#define _DLIST_DCL

typedef int BOOL;
typedef unsigned long DWORD;
typedef void *LPVOID;
#define TRUE 1
#define FALSE 0
#define NULL 0

typedef struct _DNode DNode;
typedef DNode *LPDNode;
typedef struct _DList DList;
typedef DList *LPDList;
struct _DNode
{
    LPVOID data;/*数据域*/
    LPDNode pre;
    LPDNode next;
};
struct _DList
{
    LPDNode head,tail;/*指向链表的头和尾,头表示第一个元素*/
    DWORD length;/*结点的个数*/
};

BOOL InitList(LPDList *pl);/*初始化*/
BOOL DestroyList(LPDList pl);/*销毁*/
LPVOID GetElem(LPDList pl,DWORD index,BOOL fromtail);/*查找*/
BOOL ListInsert(LPVOID data,LPDList pl,DWORD index,BOOL fromtail);/*插入*/
BOOL ListDelete(LPDList pl,DWORD index,BOOL fromtail);/*删除*/
BOOL ClearList(LPDList pl);/*清空链表*/
#define ListLength(pl) (pl->length)/*返回链表长*/
#define ListEmpty(pl) (pl->length==0)/*判断是否为空*/

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

#define InitStack(ps) InitList(ps)
#define DestroyStack(ps) DestroyList(ps)
#define GetTop(ps) GetElem(ps,0,FALSE)
#define Push(ps) ListInsert(ps,0,FALSE)
#define Pop(ps,e) ((e=GetElem(ps,0,FALSE)),ListDelete(ps,0,FALSE))
#define StackLength(ps) ListLength(ps)
#define StackEmpty(ps) ListEmpty(ps)
#define ClearStack(ps) ClearList(ps)

#define InitQueue(pq) InitList(pq)
#define DestroyQueue(pq) DestroyList(pq)
#define GetHead(pq) GetElem(pq,0,FALSE)
#define EnQueue(pq,e) ListInsert(e,pq,0,TRUE)
#define DeQueue(pq,e) ((e=GetElem(pq,0,FALSE)),ListDelete(pq,0,FALSE))
#define QueueLength(pq) ListLength(pq)
#define QueueEmpty(pq) ListEmpty(pq)
#define ClearQueue(pq) ClearList(pq)

#endif

/*File DList.c*/
#include "DList.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);
  return TRUE;
}
LPVOID GetElem(LPDList pl,DWORD index,BOOL fromtail)/*fromtail表示是否从尾部开始*/
{
  LPDNode p;
  if(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;
  }
  
最后是测试例子:
#include "DList.h"
#include <stdio.h>
void main()
{
    LPDQueue que;
    LPVOID sz_tmp[3];
    InitQueue(&que);
    EnQueue(que,"Hello!Program World!");
    EnQueue(que,"3.14");
    EnQueue(que,"中国");
    DeQueue(que,sz_tmp[0]);
    DeQueue(que,sz_tmp[1]);
    //printf("%d\n",QueueLength(que));
    DeQueue(que,sz_tmp[2]);
    printf("%s,%s,%s\n",sz_tmp[0],sz_tmp[1],sz_tmp[2]);
    DestroyQueue(que);
}

板凳

顶一下,辛苦楼主了
建议多帖一些

3 楼

对我们这些菜鸟很好  斑竹以后多照顾一下

4 楼

上面这些句子以及程序全都是我一个键一个键敲出来的~~~可能没什么人看

5 楼

谢谢斑竹了,辛苦了哦

6 楼


楼主,有没有完整而简单的一个单链表的插入一个数,然后删除一个数,再遍历[em1]

7 楼

[quote]上面这些句子以及程序全都是我一个键一个键敲出来的~~~可能没什么人看[/quote]
我看我看我看我看我看我看我看我看我看我看我看我看我看

8 楼

我顶,请再多贴一些贴。让我们学习

9 楼

辛苦了。支持。

10 楼

谢谢兄台了
帮助我们啊

我来回复

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