主题:线性表(链表)、栈与队列
rickone
[专家分:15390] 发布于 2006-04-01 15:03:00
线性表、栈与队列
一、结构定义
线性表有两种方式表示,这里说的是存储方式,包括连续的数组表示和链表表示两种。仅以链表表示为例。
首先要定义结点类型,总的来说一个结点应该包括两个部分,指针域和数据域。
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个回复)
沙发
rickone [专家分:15390] 发布于 2006-04-01 15:02:00
这里是通过调试的代码:
/*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);
}
板凳
海上飞洪 [专家分:520] 发布于 2006-04-01 23:04:00
顶一下,辛苦楼主了
建议多帖一些
3 楼
中国台湾 [专家分:2140] 发布于 2006-04-02 20:23:00
对我们这些菜鸟很好 斑竹以后多照顾一下
4 楼
rickone [专家分:15390] 发布于 2006-04-02 23:00:00
上面这些句子以及程序全都是我一个键一个键敲出来的~~~可能没什么人看
5 楼
冷月星光 [专家分:16520] 发布于 2006-04-03 12:47:00
谢谢斑竹了,辛苦了哦
6 楼
boy198300 [专家分:0] 发布于 2006-04-04 22:41:00
楼主,有没有完整而简单的一个单链表的插入一个数,然后删除一个数,再遍历[em1]
7 楼
凡尘 [专家分:9680] 发布于 2006-04-05 16:09:00
[quote]上面这些句子以及程序全都是我一个键一个键敲出来的~~~可能没什么人看[/quote]
我看我看我看我看我看我看我看我看我看我看我看我看我看
8 楼
lvlongyun [专家分:0] 发布于 2006-04-07 12:48:00
我顶,请再多贴一些贴。让我们学习
9 楼
xuexiziji [专家分:90] 发布于 2006-04-07 17:41:00
辛苦了。支持。
10 楼
无话可说 [专家分:70] 发布于 2006-04-08 23:26:00
谢谢兄台了
帮助我们啊
我来回复