代碼面試之廣義表

廣義表的基本概念node

廣義表(Lists,又稱列表)是線性表的推廣。線性表定義爲n>=0個元素a1,a2,a3,…,an的有限序列。線性表的元素僅限於原子項,原子是做爲結構上不可分割的成分,它能夠是一個數或一個結構,若放鬆對錶元素的這種限制,允許它們具備其自身結構,這樣就產生了廣義表的概念。算法

     廣義表是n (n>=0)個元素a1,a2,a3,…,an的有限序列,其中ai或者是原子項,或者是一個廣義表。一般記做LS=(a1,a2,a3,…,an)。LS是廣義表的名字,n爲它的長度。若ai是廣義表,則稱它爲LS的子表。數據結構

抽象數據類型廣義表的定義以下:函數

ADT Glistatom

{spa

   數據對象: D={ei | i=1,2,..,n;n>=0 ;  eiÎAtomSet 或ei ÎGlist,指針

                                     AtomSet爲某個數據對象}對象

   數據關係:R1={< ei-1, ei > | ei-1 , ei ÎD,2<=i<=n}blog

   基本操做:遞歸

       InitGList( &L);

             操做結果:建立空的廣義表L。

       CreateGList(&L,S);

             初始條件:S是廣義表的書寫形式串。

             操做結果:由S建立廣義表L。

       DestroyGList(&L);

            初始條件:廣義表L存在。

            操做結果:銷燬廣義表L。

CopyGList( &T,L);

            初始條件:廣義表L存在。

            操做結果:由廣義表L複製獲得廣義表T。

       GListLength(L);

            初始條件:廣義表L存在。

            操做結果:求廣義表L的長度,即元素個數。

       GListDepth(L);

            初始條件:廣義表L存在。

            操做結果:求廣義表L的深度。

       GListEmpty (L);

            初始條件:廣義表L存在。

            操做結果:斷定廣義表L是否爲空。

       GetHead(L);

            初始條件:廣義表L存在。

            操做結果:取廣義表L的頭。

GetTail( &T,L);

            初始條件:廣義表L存在。

            操做結果:取廣義表L的尾。

       InsertFirst_GL(&L,e);

            初始條件:廣義表L存在。

            操做結果:插入元素e做爲廣義表L的第一元素。

       DeleteFirst_GL(&L,&e);

            初始條件:廣義表L存在。

            操做結果:刪除廣義表L的第一元素,並用e返回其值。

       Traverse_GL (L,visit());

            初始條件:廣義表L存在。

            操做結果:遍歷廣義表L,用函數visit處理每一個元素。

一般用圓括號將廣義表括起來,用逗號分隔其中的元素。爲了區別原子和廣義表,書寫時用大寫字母表示廣義表,用小寫字母表示原子。若廣義表LS(n>=1)非空,則a1是LS的表頭,其他元素組成的表(a2,…an)稱爲LS的表尾。

    顯然廣義表是遞歸定義的,這是由於在定義廣義表時又用到了廣義表的概念。廣義表的例子以下:

(1)A=()——A是一個空表,其長度爲零。

(2)B=(e)——表B只有一個原子e,B的長度爲1。

(3)C=(a,(b,c,d))——表C的長度爲2,兩個元素分別

         爲原子a和子表(b,c,d)。

(4)D=(A,B,C)——表D的長度爲3,三個元素

        都是廣義表。顯然,將子表的值代入後,

         則有D=(( ),(e),(a,(b,c,d)))。

(5)E=(E)——這是一個遞歸的表,它的長度爲2,E至關於一個無限的廣義表E=(a,(a,(a,(a,…)))).

從上述定義和例子可推出廣義表的三個重要結論:

(1)廣義表的元素能夠是子表,而子表的元素還能夠是子表,。由此,廣義表是一個多層次的結構,能夠用圖形象地表示。P108

 

(2)廣義表可爲其它表所共享。例如在上述例(4)中,廣義表A,B,C爲D的子表,則在D中能夠沒必要列出子表的值,而是經過子表的名稱來引用。

 

(3)廣義表的遞歸性。

     綜上所述,廣義表不只是線性表的推廣,也是樹的推廣。

由表頭、表尾的定義可知:任何一個非空廣義表其表頭多是原子,也多是列表,而其表尾一定是列表。

          gethead(B)=e         gettail(B)=(  )

          gethead(D)=A        gettail(D)=(B,C)

 

      因爲(B,C)爲非空廣義表,則可繼續分解獲得:

         gethead(B,C)=B         gettail(B,C)=(C)

  

    注意廣義表( )和( ( ) )不一樣。前者是長度爲0的空表,

對其不能作求表頭的和表尾的運算;然後者是長度爲1的非空表(只不過該表中惟一的一個元素是空表)。對其可進行分解,獲得表頭和表尾均爲空表( )。

 

廣義表的存儲結構

因爲廣義表(a1,a2,a3,…an)中的數據元素能夠具備不一樣的結構,(或是原子,或是廣義表),所以,難以用順序存儲結構表示,一般採用鏈式存儲結構,每一個數據元素可用一個結點表示。

    因爲廣義表中有兩種數據元素,原子或廣義表,所以,須要兩種結構的結點:一種是表結點,用以表示列表;一種是原子結點,用以表示原子。 

  若列表不空,則可分解成表頭和表尾;反之,一對肯定的表頭和表尾可惟一肯定列表。由此,一個表結點可由三個域組成:標誌域、指示表頭的指針域和指示表尾的指針域;而原子結點只需兩個域:標誌域和值域。

一、僅有表結點由三個域組成:

    標誌域、指示表頭的指針域和指示表尾的指針域;而原子域只需兩個域:標誌域和值域。

 

頭尾鏈表存儲表示

  1. typedef enum {ATOM,LIST } ElemTag;  //ATOM==0:表示原子,LIST==1:表示子表  
  2. typedef struct GLNode {  
  3.     ElemTag  tag;  //公共部分,用以區分原子部分和表結點  
  4.     union {       //原子部分和表結點的聯合部分  
  5.       AtomType  atom; //atom是原子結點的值域,AtomType由用戶定義  
  6.       struct { struct GLNode *hp, *tp;} ptr;  
  7.              // ptr是表結點的指針域,ptr.hp 和ptr.tp分別指向表頭和表尾  
  8.     };  
  9. } *Glist;  //廣義表類型  


示例如圖:

 

這種存儲結構的三個特色:

1。除空表的表頭指針爲空外,對任何非空列表,其表頭指針均指向一個表結點,且該結點中的hp域指示列表表頭,tp域指向列表表尾(除非表尾爲空,則指針爲空,不然必爲表結點);

2。容易分清列表中原子和子表所在層次。如在列表D中,原子e和a在同一層次上,而b、c和d在同一層次且比e和a低一層,B和C是同一層的子表;

3。最高層的表結點個數即爲列表的長度。

 

二、表結點和原子結點均由三個域組成:標誌域、指示表頭的指針域和指示表尾的指針域;原子結點的三個域爲:標誌域、值域和指示表尾的指針域。

 

其類型定義以下:

擴展線性鏈表存儲表示

  1. Typedef enum { ATOM,LIST} ElemTag;                                
  2.     //ATOM==0:表示原子,LIST==1:表示子表  
  3. Typedef struct GLNode {  
  4.     ElemTag    tag;  //公共部分,用以區分原子部分和表結點  
  5.     union {  //原子部分和表結點的聯合部分  
  6.         AtomType    atom;  //原子結點的值域  
  7.         struct GLNode  *hp;  //表結點的表頭指針  
  8.         };  
  9.         struct GLNode    *tp;    
  10.                 //至關於線性鏈表的next,指向下一個元素結點  
  11. } *Glist;  //廣義表類型Glist 是一種擴展的線性鏈表  


示例如圖:

 

 

廣義表基本操做的實現

咱們以頭尾表示法存儲廣義表,討論廣義表的有關操做的實現。因爲廣義表的定義是遞歸的,所以相應的算法通常也都是遞歸的。

⒈廣義表的取頭、取尾

GList Head(GList ls)
{
if ls->tag = = 1
then p = ls->hp;
return p;
}
算法5.6

GList Tail(GList ls)
{
if ls->tag = = 1
then p = ls->tp;
return p;
}
算法5.7

⒉創建廣義表的存儲結構

int Create(GList *ls, char * S)
{ Glist p; char *sub;
if StrEmpty(S) *ls = NULL;
else {
if (!(*ls = (GList)malloc(sizeof(GLNode)))) return 0;
if (StrLength(S) = = 1) {
(*ls)->tag = 0;
(*ls)->data = S;
}
else {
(*ls)->tag = 1;
p = *ls;
hsub =SubStr(S,2,StrLength(S)-2);
do {
sever(sub,hsub);
Create(&(p->ptr.hp), sub);
q = p;
if (!StrEmpty(sub)){
if (!(p = (GList)malloc(sizeof(GLNode)))) return 0;;
p->tag = 1;
q->ptr.tp = p;
}
}while (!StrEmpty(sub));
q->ptr.tp = NULL;
}
}
return 1;
}
算法5.8

int sever(char *str, char *hstr)
{
int n = StrLength(str);
i= 1; k = 0;
for (i = 1, k = 0; i <= n || k != 0; ++i)
{
ch=SubStr(str,i,1);
if (ch = = '(') ++k;
else if (ch = = ')') --k;
}
if (i <= n)
{
hstr =SubStr(str,1,i-2);
str= SubStr(str,i,n-i+1);
}
else {
StrCopy(hstr,str);
ClearStr(str);
}
}

⒊以表頭、表尾創建廣義表

int Merge(GList ls1,GList ls2, Glist *ls)
{
if (!(*ls = (GList)malloc(sizeof(GLNode)))) return 0;
*ls->tag = 1;
*ls->hp = ls1;
*ls->tp = ls2;
return 1;
}
算法5.10

4.求廣義表的深度

 
int Depth(GList ls)
{
if (!ls)
return 1; /*空表深度爲1*/
if (ls->tag = = 0)
return 0; /*單元素深度爲0*/
for (max = 0,p = ls; p; p = p->ptr.tp) {
dep = Depth(p->ptr.hp); /*求以p->ptr.hp 尾頭指針的子表深度*/
if (dep > max) max = dep;
}
return max+1; /*非空表的深度是各元素的深度的最大值加1*/
}
算法5.11

 

⒌複製廣義表

int CopyGList(GList ls1, GList *ls2)
{
if (!ls1) *ls2 = NULL; /*複製空表*/
else {
if (!(*ls2 = (Glist)malloc(sizeof(Glnode)))) return 0; /*建表結點*/
(*ls2)->tag = ls1->tag;
if (ls1->tag = = 0) (*ls2)->data = ls1->data; /*複製單元素*/
else {
CopyGList(&((*ls2)->ptr.hp), ls1->ptr.hp); /*複製廣義表ls1->ptr.hp 的一個副本*/
CopyGList(&((*ls2)->ptr.tp) , ls1->ptr.tp); /*複製廣義表ls1->ptr.tp 的一個副本*/
}
}
return 1;
}

 

廣義表的二叉樹實現的形式

輸入二叉樹的廣義表形式,將其轉變爲二叉樹形式,至於怎樣輸入廣義表須要程序規定。

定義包含頭文件文件t11.h中

#include"stdio.h"
#include"string.h"
#include"ctype.h"
#include"malloc.h"
#include"stdlib.h"  //atoi(),exit();
#include"io.h"      //eof()
#include"math.h"

#define  TRUE  1
#define  FALSE  0
#define  OK   1
#define  ERROR 0
typedef int Status;
typedef int Boolean;
定義數據結構頭文件gercs.h中
#define INIT_STACK 100
#define INIT_STACK_ADD  20
typedef struct node            //  二叉樹的數據結構定義
{
    char data;
 struct node *lchild;
 struct node *rchild;
}*Bitree,pBitree;
typedef struct 
{
    char *bottom;
 char *top;
 int stacksize;
}Sqstack;
struct Binode
{
 Bitree lp;
};
struct Sqstack1
{
 Binode *bottom;
 Binode *top;
 int stacksize;
};
typedef struct
{
     Binode *front;
  Binode *rear;
  int queuesize;
}*linkqueue,queue;
  
Sqstack W;
定義包含實現函數功能模塊gercs.cpp中
void inittree(Bitree &T)
{
 T=NULL;
}
void initqueue(queue &Q)
{
 Q.front=(Binode*)malloc(INIT_STACK*sizeof(Binode));
 Q.rear=Q.front;
 Q.queuesize=INIT_STACK;
}
void initstack(Sqstack &L)
{
 L.bottom=(char*)malloc(INIT_STACK*sizeof(char));
 if(!L.bottom)
 {
  printf("內存分配失敗!!");
  exit(0);
 }
 L.top=L.bottom;
 L.stacksize=INIT_STACK;
 
}
void initstack1(Sqstack1 &L)
{
 L.bottom=(Binode*)malloc(INIT_STACK*sizeof(Binode));
 if(!L.bottom)
 {
  printf("內存分配失敗!!");
  exit(0);
 }
 L.top=L.bottom;
 L.stacksize=INIT_STACK;
 
}
void enqueue(queue &Q,Bitree T)
{
 if(Q.rear-Q.front > Q.queuesize)
 {
  Q.front=(Binode*)realloc(Q.front,(Q.queuesize,INIT_STACK_ADD)*sizeof(Binode));
  Q.rear=Q.front+Q.queuesize;
  Q.queuesize+=INIT_STACK_ADD;
 }
 (Q.rear++)->lp=T;
}
void push(Sqstack &L,char ch)
{
    if(L.top-L.bottom > L.stacksize)
 {
  L.bottom=(char*)realloc(L.bottom,(L.stacksize+INIT_STACK_ADD)*sizeof(char));
  L.top=L.bottom+L.stacksize;
  L.stacksize+=INIT_STACK_ADD;
 }
 *(L.top++)=ch;
}
void push1(Sqstack1 &L,Bitree ch)
{
    if(L.top-L.bottom > L.stacksize)
 {
  L.bottom=(Binode*)realloc(L.bottom,(L.stacksize+INIT_STACK_ADD)*sizeof(Binode));
  L.top=L.bottom+L.stacksize;
  L.stacksize+=INIT_STACK_ADD;
 }
 (L.top++)->lp=ch;
}
Status pop(Sqstack &L,char &e)
{
 if(L.bottom == L.top)
  return ERROR;
 else
 {
  e=*(--L.top);
  return OK;
 }
}
Status pop1(Sqstack1 &L,Bitree &e)
{
 if(L.bottom == L.top)
  return ERROR;
 else
 {
  e=(--L.top)->lp;
  return OK;
 }
}
Status emptystack(Sqstack L)
{
 if(L.bottom == L.top)
  return OK;
 else
  return ERROR;
}
Status emptystack1(Sqstack1 L)
{
 if(L.bottom == L.top)
  return OK;
 else
  return ERROR;
}
Sqstack shuru(Sqstack &S)
{
 Sqstack R;
 initstack(R);
 char ch,ch1;
 printf("輸入廣義表二叉樹形式:");
 ch=getchar();
 while(10 != ch)
 {
  if(ch >= 'a' && ch <= 'z')//|| ch >= 'A' && ch <='Z')
  {
   push(S,ch);
   ch1=getchar();
   if(')' == ch1)
   {
    push(S,10);
    push(S,10);
   }
   ch=ch1;
  }
  else
  {
            ch1=getchar();
   if(',' == ch && ',' == ch1)
    push(S,10);
   ch=ch1;
  }
 }
 
 while(!emptystack(S))
 {
  pop(S,ch);
  //printf("%-3c",ch);
  push(R,ch);
 }
 return R;
}
void Createtree(Bitree &T)           // 先序建立二叉樹
{
 // printf("進入了!");
 char ch;
 pop(W,ch);
 // printf("ch=%c  ",ch);
 if(ch == 10)                           //  若是是回車鍵置爲空
  T=NULL;
 else
 {
  T=(Bitree)malloc(sizeof(pBitree));
  if(!T)
  {
   printf("分配失敗!");
   exit(0);
  }
  T->data=ch;
  Createtree(T->lchild);
  Createtree(T->rchild); 
 }
 // getchar();
}                               // 建立二叉樹結束
void xianxu(Bitree T,Sqstack1 &S)   //  先序非遞歸遍歷,採用的是保存右孩子的地址經過棧實現
{
 printf("先序非遞歸遍歷輸出:\n");
    if(T)
 {
  if(T->rchild)
   push1(S,T->rchild);    //  專門壓入右孩子指針地址
  printf("%-3c",T->data);
  T=T->lchild;            //  更新指針
 }else
  exit(0);
 while(T || !emptystack1(S))     //  循環終止條件
 {
  if(T!= NULL)             //  始終做爲一個點存在加以判斷,一種狀況是一直指向左孩子,另外一種就是出棧彈出的右孩子
  {
   printf("%-3c",T->data);    //  輸出節點的數據域
   if(T->rchild != NULL)    // 該節點存在右孩子,將右孩子入棧
    push1(S,T->rchild);
   T=T->lchild;       //  更新指針,始終往左走
  }
  else
   pop1(S,T);  
 }
}
void cengci(Bitree T,queue &Q)
{
    if(!T)
 {
  printf("樹爲空!");
  exit(0);
 }
 Binode *P,*S;
 S=P=Q.front;
 enqueue(Q,T);
 while(P != Q.rear)
 {
  if(P->lp->lchild)
   enqueue(Q,P->lp->lchild);
  if(P->lp->rchild)
   enqueue(Q,P->lp->rchild);
  P++;
 }
 printf("層次遍歷爲:\n");
 while(S < Q.rear)
 {
  printf("%-3c",S->lp->data);
  S++;
 }
}
 
最後就是定義主函數函數調用了,包含於頭文件main_gercs.cpp中
#include"t11.h"
#include"gercs.h"
#include"gercs.cpp"
void main()
{
 // char ch;
 Bitree S;
    queue U;
 Sqstack1 Q;
    initstack(W);
 initstack1(Q);
 inittree(S);
 initqueue(U);
 W=shuru(W);
 /* while(!emptystack(W))
 {
 pop(W,ch);
 printf("%-3c",ch);
 
    }*/
 Createtree(S);
 xianxu(S,Q);
 printf("\n");
 cengci(S,U);
    printf("\n");
 
 
}

  編譯運行以後:

相關文章
相關標籤/搜索