回 帖 发 新 帖 刷新版面

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

线性表、栈与队列

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

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

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

21 楼

楼主辛苦了
支持CGCG!!!

22 楼

看,经典呀!顶~~~~~~~~~~~~

23 楼

我想请教下~~~~如何统计输入到链表里的字符~数字和空格啊?新学不太懂啊~~~

24 楼

好帖..希望楼主多发.....

25 楼

这是我的单链表操作[em1]
#include <iostream.h>
#include <stdlib.h>
#include <string.h>
typedef char Datatype;      //此数据类型采用字符型

typedef struct node
{
     Datatype data;      //数据
     struct node* next;  //下一结点
}Linklist;

Linklist *head=NULL;              //head为全局变量,指向头结点 

void CreatLinklist()        //创建函数 
{
    Linklist *p, *s;
    Datatype x;
    int z=1;                //循环控件 
    head=new Linklist;        //头节点开辟内存 
    head->next=NULL;
    p=head;
    cout<<"建立一个线性表"
        << "请逐个输入字符,以'#'号结束\n";
    while(z)
    {
        cout<<"输入字符:";
        cin>>x;
        if(x!='#')
        {
            s=new Linklist;        //开辟内存 
            s->data=x;
            s->next=p->next;    
            p->next=s;            
            p=s;
        }else z=0;
    }
}
int LenLinklist()        //求链表长度
{
    int n=0;
    Linklist *p=head;            //p指向头结点
    while(p.next)
    {    p=p->next    n++;    }    //p指向第n个结点 
    return n;        
}
void InsertLinklist(int i,Datatype x)
//在链表的第i个位置插入一个x的新元素 
{
    int j;
    Linklist *s,*p;
    s=new Linklist
    s->data=x;
    if(i<1)                //位置小于1,错!    
    {    cout<<"输入位置不合法。";
        delete s;
        return 0;
    }
    else
    {
        j=0;
        p=head;
        while(p!=NULL&&j<j-1)    //循环查找插入位置 
        {
            j++;p=p->next;
        }
        else
        {
            cout<<"为找到插入位置。";
            delete s;
            return 0;
        }
    }
}
void DeleteLinklist(int i)    
//在链表的第i个位置删除结点  
{
    Linklist *p,*s;
    int j=0;
    if(head->next==NULL)    cout<<"无元素可删";
    if(i<1)
    {
        cout<<"位置不合法";
        return 0;
    }
    p=head;
    while(p->next && j<i-1)//循环直到p指向第i-1个元素 
    {    p=p->next;    j++;}
    if(!(p->next)|| j>i-1)
    {
        cout<<"没找到删除位置";
        return 0;
    }
    s=p->next;    p->next=s->next;
    delete s;
}
void PrintLinklist()         //输出函数 
{
    Linklist *p=head;
    cout<<"显示链表所有元素:";
     if(head->next==NULL)    cout<<"链表为空";
     else while(p->next!=NULL) 
             {
                 cout<<p->next->data;
                 p->next;
             }
}
void SearchLinklist(Datatype x)
{
    Linklist *p=head;
    while(p!=NULL)
    {
        if(strcmp(p->data,x)==0)
            cout<<p->data;
        p->next;
    }
}

int main()
{
    Datatype x;
    int i;
    CreatLinklist();
    cout<<"输入位置i和数值x:";
    cin>>i>>x;
    InsertLinklist(i,x);
    cout<<"输入要删除的元素位序:";
    cin>>i;
    PrintLinklist(); 
    DeleteLinklist(i);
    cout<<"表长:"<<LenLinklist();
    PrintLinklist(); 
    SearchLinklist(x);
    return 0;
}

26 楼

27 楼

[quote]上面这些句子以及程序全都是我一个键一个键敲出来的~~~可能没什么人看[/quote]
写得极好,比较清晰.

28 楼

这是写给初学者的吗?
我怎么看不太懂啊?

29 楼

多谢斑竹,辛苦了

30 楼

能不能用类来定义结点和链表啊??

我来回复

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