程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> 關於C語言 >> C語言范例學習03-中,c語言范例03-

C語言范例學習03-中,c語言范例03-

編輯:關於C語言

C語言范例學習03-中,c語言范例03-


 

棧和隊列

這兩者都是重要的數據結構,都是線性結構。它們在日後的軟件開發中有著重大作用。後面會有實例講解。

兩者區別和聯系,其實總結起來就一句。棧,後進先出;隊列,先進先出。

可以將棧與隊列的存儲空間比作一個只夠一個身位的水管。

棧的水管,有一頭被堵住了。所以當人們進去後,想出來就只能讓最靠近出口的那位先出去,依次推出。(後進先出)。

隊列的水管,類似單向車道。所以當人們進去後,想出來就只能一直向前,走出來,不可以從入口出來。(先進先出)。

所以,道理還是很簡單的。接下來就是學習一些專屬函數就ok了。

 

 

實例090    應用棧實現進制轉換

問題:應用棧實現進制轉換,可以將十進制數轉換為其他進制數。

邏輯:首先我們來想一下進制轉換的算法,就如之前提到的,不斷地取余,求商。依次循環進行,再按順序輸出,獲取最終結果。

代碼

  1 #include<stdio.h>
  2 typedef struct
  3 {
  4     DataType *base;
  5     //有人問DataType是什麼東東。
  6     //DataType是一種數據類型,是數據結構專屬。采取的是類C的語言。所以需要將其映射為C數據類型。
  7     DataType *top;
  8     int stacksize;
  9     //提醒一句,stack是棧的英文。
 10 }SeqStack;
 11 
 12 
 13 //************接下來就是重點
 14 void Initial(SeqStack *s)
 15 //棧的初始化函數,構造一個空棧S。
 16 {
 17     s->base=(DataType *)malloc(STACK_SIZE *sizeof(DataType));
 18     //類似於鏈表的獲取存儲空間,連使用的函數都一樣。
 19     //注意,我這裡使用的是base。所以,這個棧是“向上生長”的棧。
 20     if(!s->base)
 21         exit(-1);
 22         //exit()是一個退出函數,其參數是退出狀態代碼。(0是正常退出狀態碼)
 23     s->top=s->base;
 24     //初始化時,棧內為空。從而可以這樣初始化其中的top變量。
 25     s->stacksize=STACK_SIZE;
 26     //獲取存儲空間大小。
 27 }
 28 int IsEmpty(SeqStack *S)
 29 //判斷棧是否為空的函數
 30 {
 31     return S->top==S->base;
 32     //返回值為判斷結果。
 33 }
 34 int IsFull(SeqStack *S)
 35 //判斷棧是否滿了。
 36 {
 37     return S->top-S->base==STACK_SIZE-1;
 38     //注意看,中間那個是減號。
 39 }
 40 void Push(SeqStack *S,DataType x)
 41 //棧的入棧函數
 42 {
 43     if(IsFull(S))
 44     {
 45         printf("棧上溢出");
 46         exit(1);
 47     }
 48     *S->top++=x;
 49     //入棧時,切記    棧指針top,“先壓後加”。
 50 }
 51 DataType Pop(SeqStack *S)
 52 //棧的出棧函數
 53 {
 54     if(IsEmpty(S))
 55     {
 56         printf("棧為空");
 57         exit(1);
 58     }
 59     return *--S->top;
 60     //注意上面是*(--s),別問我*--是什麼。出棧時,切記 棧指針to[,“先減後彈”。
 61 }
 62 DataType Top(SeqStack *S)
 63 //棧頂變化函數。
 64 //因為這個棧是從棧底構建的,所以棧底不變。變化的是棧頂。
 65 {
 66     if(IsEmpty(S))
 67     {
 68         printf("棧為空");
 69         exit(1);
 70     }
 71     return *(S->top-1);
 72     //改變棧頂地址。
 73 }
 74 //************以上是重點
 75 
 76 
 77 
 78 void conversion(int N,int B)
 79 //解決問題的進制轉換函數
 80 {
 81     int i;
 82     SeqStack *S;
 83     Initial(S);
 84     //初始化棧S
 85     while(N)
 86     {
 87         Push(S,N%B);
 88         N=N/B;
 89     }
 90     //這個循環不用我講了吧。就知平時進制轉化的循環。
 91     while(!IsEmpty(S))
 92     {
 93         i=Pop(S);
 94         printf("%d",i);
 95         //依次輸出棧中元素,其實就是我們的結果了。
 96     }
 97 }
 98 void main()
 99 //主函數不用細講。就是完成輸入,輸出,調用變換函數。
100 {
101     int n,d;
102     printf("Input the integer you want to transform:");
103     scanf("%d",&n);
104     printf("Input the integer of the system:");
105     scanf("%d",&d);
106     printf("result:");
107     conversion(n,d);
108     getch();
109 }

反思:棧的應用主要就是初始化、判斷空、判斷滿、入棧、出棧、棧頂六個函數。懂了這六個函數,應用就沒什麼了。

其中的技巧,代碼備注也提到了,比如:“先壓後加”等。不過這是針對這個棧的,一個“向上生長”的棧。

 

ps:其實還有用棧設置密碼、實現編輯程序等。但其中關於棧的部分基本大同小異,所以不再贅述。

如果,還有不懂,可以看看視屏。比如,張洪瑞與嚴蔚敏老師的視屏講解。(我不會告訴你,計算機考研的專業課可是要求嚴蔚敏老師的書。不過建議張洪瑞老師的視屏。主要嚴蔚敏老師講的。總感覺和我不是一個時代,事實上,也確實不是一個時代的。)

 

 

 

實例095    鏈隊列

問題:采取鏈式存儲法編程實現元素入隊、出隊以及將隊列中的元素顯示出來,要求整個過程以菜單選擇的形式實現。

邏輯:邏輯與棧沒有太多區別。將隊列的主要操作:創建、入隊、出隊、展示等功能分為多個函數進行。最後主函數實現題目要求的界面功能,及輸入,調用等功能。

代碼:

  1 #include<stdio.h>
  2 #include<stdlib.h>
  3 typedef int ElemType;
  4 //怕你們不懂之前說的DataType到底怎麼弄。這個就是一個例子。如果沒有這句話,你們會發現在VC裡,這個程序報錯報炸了。
  5 typedef struct node
  6 //這個結構體是用來存儲節點的。
  7 {
  8     ElemType data;
  9     //節點數據
 10     struct node *next;
 11     //節點指向下一個節點地址
 12 }quenode;
 13 //這個節點結構體名稱。
 14 struct quefr
 15 //這個結構體用來存放隊列的隊首、隊尾地址指針。
 16 {
 17     quenode *front,*rear;
 18     //quenode是什麼?額,就是我們之前定義的節點結構體。
 19 };
 20 void creat(struct quefr *q)
 21 //定義函數creat,用來創建、初始化隊列。
 22 {
 23     quenode *h;
 24     h=(quenode*)malloc(sizeof(quenode));
 25     //獲取存儲空間,空間大小與quenode同步。
 26     h->next=NULL;
 27     //初始化h->next,為空。
 28     q->front=h;
 29     q->rear=h;
 30     //初始化相關的quefr中數據,均為h。(隊列剛剛建立,那麼隊首,隊尾當然是同一個元素(結構體quenode的h))。
 31 }
 32 void enqueue(struct quefr *q,int x)
 33 //定義入隊函數enqueue,用來實現元素x進入隊列q。
 34 {
 35     quenode *s;
 36     //建立一個新的節點s,用來存儲新的入隊元素x。
 37     s=(quenode*)malloc(sizeof(quenode));
 38     //依舊是獲取存儲空間
 39     s->data=x;
 40     //向quenode結構體s導入新的入隊元素x。
 41     s->next=NULL;
 42     //s的指針域設置為空,作為初始化。
 43     //也許,有人就問了。這裡設置為空,那麼隊列還怎麼連接起來啊。
 44     //嗯。其實,有關連接方面的數據處理統一交給了quefr處理了。看下面就知道了。
 45     q->rear->next=s;
 46     //這裡實現了隊列元素的連接。切記,其中rear指針的數據類型可是quenode,所以可以實現調用next,可以實現連接。
 47     q->rear=s;
 48     //這時隊尾指針地址改變,因為增加了新的元素了。(這個程序設定是隊首地址不變的,向隊尾添加元素。)
 49     //我這個程序設定的是隊尾入隊,隊首出隊。
 50 }
 51 ElemType dequeue(struct quefr *q)
 52 //定義出隊函數dequeue,用來實現元素離開隊列q。(由於,之前提到的隊列性質,所以離開隊列的函數必然是隊首元素。)
 53 {
 54     quenode *p;
 55     ElemType x;
 56     //初始化,詳細參考上面的。
 57     p=(quenode*)malloc(sizeof(quenode));
 58     //有人說,初始化,入隊要獲取存儲空間就算了。為什麼出隊還要獲取存儲空間呢?
 59     //其實,不是沒有道理。但是,1,我們對數據處理部分采用的一直都是結構體quenode,出隊是便於數據處理,當然還是得建立結構體,自然就要獲取存儲空間;2.很簡單一句,便於模塊化操作。
 60     if(q->front==q->rear)
 61     //判斷原隊列是否為空
 62     {
 63         printf("queue is NULL\n");
 64         x=0;
 65     }
 66     else
 67     {
 68         p=q->front->next;
 69         //賦值指針p,為q指針的下一個地址。
 70         q->front->next=p->next;
 71         //重新定位q->front->next,從而重新定位了q。
 72         //這兩句也許有些人會看得有點暈,可以自己畫畫圖,或者參考一些之前的鏈表。主要就是把原來q的地址遺失了。重新定位q。
 73         if(p->next==NULL)
 74         //判斷原隊列是否就一個元素
 75             q->rear=q->front;
 76             //重新定位。很好奇這步有沒有必要。
 77         x=p->data;
 78         //獲取出隊列元素
 79         free(p);
 80         //釋放空白空間。
 81     }
 82     return(x);
 83     //返回出隊元素
 84 }
 85 void display(struct quefr dq)
 86 //定義函數display,用於展示隊列內的所有元素
 87 {
 88     quenode *p;
 89     p=(quenode*)malloc(sizeof(quenode));
 90     //為p分配存儲空間
 91     p=dq.front->next;
 92     //賦值p
 93     while(p!=NULL)
 94     //等同於判斷dq.front->next是否不為空
 95     {
 96         printf("data=%d\n",p->data);
 97         //展示元素
 98         p=p->next;
 99         //將計數器指向下一個元素地址。
100     }
101     printf("end\n");
102     //最終輸出end,表示元素展示完畢。
103 }
104 main()
105 //建立主函數作為程序入口,實現題目要求的界面,以及輸入,調用,輸出。
106 {
107     struct quefr *que;
108     //定義*que的數據類型為struct quefr。
109     int n,i,x,sel;
110     void display();
111     void creat();
112     void enqueue();
113     ElemType dequeue();
114     do
115     {
116         printf("\n");
117         printf("    1    creat queue    \n");
118         printf("    2    into the queue    \n");
119         printf("    3    delete from queue    \n");
120         printf("    4    display    \n");
121         printf("    5    exit    \n");
122         printf("--------------------------------------------\n");
123         printf("please choose(1,2,3,4,5)\n");
124         scanf("%d",&sel);
125         switch(sel)
126         //采用switch判斷語句。
127         {
128         case 1:
129             que=(struct quefr*)malloc(sizeof(struct quefr));
130             creat(que);
131             printf(
132                 "please input the number of element which do you want to creat:");
133             scanf("%d",&n);
134             for(i=1;i<=n;i++)
135             {
136                 scanf("%d",&x);
137                 enqueue(que,x);
138             }
139             break;
140         case 2:
141             printf("please input the element:");
142             scanf("%d",&x);
143             enqueue(que,x);
144             break;
145         case 3:
146             printf("x=%d\n",dequeue(que));
147             break;
148         case 4:
149             display(*que);
150             break;
151         case 5:
152             exit(0);
153         }
154     }
155     while(sel<=4);
156     //使用了do while循環。當sel為5時,sel>4,跳出循環。
157 }

反思:之所以將隊列的問題分為多個函數分別解決,是為了更好地模塊化操作。越發地感受到編程中模塊化操作的重要性,及好處。

 

 

 

實例096    循環緩沖區問題

ps:這個問題原本想將代碼打出來的,但我覺得之前隊列程序已經將主要架構顯現出來了。所以這個問題只會簡單一提,如果有人有需要,可以找我要。

邏輯:隊列的這個實例主要采用了循環隊列。可以很好地解決順序隊列“假溢出“的現象。這裡的循環隊列和之前循環鏈表一樣,就是將順序隊列構建成一個首尾相連的循環表。這樣可以更好地解決我之前在循環鏈表提到的循環存儲問題。

 

總結:到此為止,棧與隊列隊列的問題就差不多了。你會發現,這兩個就是差不多的東西。事實上,也就兩種特殊的順序表。操作思想是一致的。更重要的是體會到其中越發明顯的模塊化操作的思想。

 

 

 

 

串與廣義表

編程中的數據處理,無非數值處理與非數值處理,而非數值處理基本上是字符串數據。而現在計算機的硬件及其架構更多地是為了數值計算,所以相對而言,處理字符串數據就更為復雜了。其中廣義表是線性表的推廣,目前多用於人工智能等領域的表處理語言中。所以,有這方面興趣的可以多多關注。

 

實例098    簡單的文本編輯器

問題:要求實現三個功能:第一,要求對指定行輸入字符串;第二,刪除指定行的字符串;第三,顯示輸入的字符串的內容。

邏輯: 串其實就是由零個,或多個字符串組成的有限序列。

    串的存儲方式由兩種:靜態存儲與動態存儲。 其中動態存儲結構又有兩種:鏈式存儲結構與堆結構存儲。這個問題的解決采用了鏈式存儲結構。

    串的鏈式存儲結構是包含數據域和指針域的節點結構(是不是感覺很眼熟,和鏈表好像啊)。

    由於每個節點只存放一個字符,太過浪費空間,為節省空間,故令每個節點存放若干個字符(題目中采用了50個字符為一塊),這種結構叫塊鏈結構。(題目中采用了這種結構)。

代碼

  1 #include<stdio.h>
  2 #include<stdlib.h>
  3 #define MAX 100
  4 void Init();
  5 void input();
  6 void Delline();
  7 void List();
  8 int Menu();
  9 //僅僅是函數聲明,由於主函數位於最後面,所以這五個語句去掉也可以。
 10 
 11 typedef struct node
 12 //定義存放字符串的節點。
 13 {
 14     char data[50];
 15     struct node *next;
 16 }strnode;
 17 
 18 typedef struct head
 19 //定義每行的頭結點。
 20 {
 21     int number;
 22     int length;
 23     strnode *next;
 24 }headnode;
 25 //如之前的一樣,兩個結構體,前者作為數據節點,存儲數據,後者則負責存儲的位置等數據以外的事情。
 26 
 27 headnode Head[MAX];
 28 //定義有100行。
 29 
 30 void Init()
 31 //定義函數Init,用來初始化串。
 32 {
 33     int i;
 34     for(i=0;i<MAX;i++)
 35     {
 36         Head[i].length=0;
 37         //設定長度均為0
 38     }
 39 }
 40 
 41 int Menu()
 42 //定義函數Menu,用來控制函數的人機交互界面。
 43 {
 44     int i;
 45     i=0;
 46     printf("1.Input\n");
 47     printf("2.Delete\n");
 48     printf("3.List\n");
 49     printf("4.Exit\n");
 50     while(i<=0||i>4)
 51     {
 52         printf("please choose\n");
 53         scanf("%d",&i);
 54     }
 55     return i;
 56     //返還用戶的輸入函數
 57 }
 58 
 59 void input()
 60 //定義函數input,作為串的輸入函數。
 61 {
 62     strnode *p,*find();
 63     int i,j,LineNum;
 64     char ch;
 65     while(1)
 66     {
 67         j=-1;
 68         //這裡j=-1,其實,由於采用的是while(1)循環。計數器的增加在開頭 j++。所以,其實還是從j=0開始的。
 69         printf("input the number of line(0-100),101-exit:\n");
 70         scanf("%d",&LineNum);
 71         //輸入要輸入字符串的所在行數。
 72         if(LineNum<0||LineNum>=MAX)
 73             return;
 74             //超出可能的行數,即直接返回。
 75         printf("please input,#-end\n");
 76         i=LineNum;
 77         Head[i].number=LineNum;
 78         Head[i].next=(strnode*)malloc(sizeof(strnode));
 79         //申請存儲空間。
 80         p=Head[i].next;
 81         //p就是我們將要存儲字符串的頭結點(字符串的第一個字符的地址)
 82         ch=getchar();
 83         while(ch!='#')
 84         {
 85             j++;
 86             //驗證前面所說的j問題
 87             if(j>=50)
 88             //這裡采用了50為一行字符串的上限。
 89             {
 90                 p->next=(strnode*)malloc(sizeof(strnode));
 91                 p=p->next;
 92                 //大於50了,就會重新申請存儲空間,繼續存儲字符串。所以一個head節點只存儲50字符串長度的數據。
 93                 //但是,由於我們並沒有修改number,所以在其表現來看,我們輸入的超過50字符的字符串還是同一個字符串。
 94                 //這裡可以將這個方法,應用到更多的地方。
 95             }
 96             p->data[j%50]=ch;
 97             //理解j%50即可。
 98             ch=getchar();
 99         }
100         Head[i].length=j+1;
101         //因為我們j是從0開始的。
102     }
103 }
104 
105 void Delline()
106 //定義函數Delline(),用來刪除確定的字符串。
107 {
108     strnode *p,*q;
109     int i,LineNum;
110     while(1)
111     {
112         printf("input the number of line which do you want to delete(0-100),101-exit:\n");
113         scanf("%d",&LineNum);
114         //輸入具體要刪除的字符串所在的行數。
115         if(LineNum<0||LineNum>=MAX)
116             return;
117         i=LineNum;
118         p=Head[i].next;
119         //中間這四行代碼同input函數一樣
120         if(Head[i].length>0)
121         //判斷所要刪除的函數並非空
122             while(p!=NULL)
123             //判斷所要刪除字符串的下一個字符串非空時,采取的處理。
124             {
125                 q=p->next;
126                 free(p);
127                 p=q;
128                 //和之前鏈表,棧等的刪除相同。
129             }
130             Head[i].length=0;
131             Head[i].number=0;
132             //確定該字符串的頭文件中length、number均為0,等同於消除存在。
133             //正如之前Head[i].length>0這句代碼,有時可通過對length、number的判斷來顯示,該Head是否為空。
134     }
135 }
136 
137 void List()
138 //自定義函數List(),用來展示所有的字符串。
139 {
140     strnode *p;
141     int i,j,m,n;
142     for(i=0;i<MAX;i++)
143     //老樣子,這個串,說白了數據深度就兩層。所以兩個循環就OK了。第二個循環在後面。要看成整體,中間只是多個判斷和順序。
144     //其中我是用printf一個字符一個字符地輸出的。所以一個字符串就是一層循環。
145     //但是,如果你要換成一個字符串一個字符串的輸出,類似putstr。自己改啊。
146     {
147         if(Head[i].length>0)
148         //這裡就驗證了之前提到的用length判斷是否為空。空時就不會輸出該字符串了。
149         {
150             printf("line%d:  ",Head[i].number);
151             //輸出所要輸出字符串所在的行數。
152             n=Head[i].length;
153             m=1;
154             p=Head[i].next;
155             for(j=0;j<n;j++)
156             //第二個循環。用來將字符串中字符一個一個輸出。
157                 if(j>=50*m)
158                 //還記得我之前在輸入函數中提到的50長度的問題嗎?這裡就有了解決之道。
159                 {
160                     p=p->next;
161                     m++;
162                 }
163                 else
164                     printf("%c",p->data[j%50]);
165                     //輸出一個個字符。
166                 printf("\n");
167         }
168     }
169     printf("\n");
170 }
171 
172 main()
173 //創建主函數mian(),作為程序的入口。控制程序的選擇界面、函數調用等。
174 {
175     int sel;
176     Init();
177     while(1)
178     {
179         sel=Menu();
180         switch(sel)
181         {
182             case 1:
183                 input();
184                 break;
185             case 2:
186                 Delline();
187                 break;
188             case 3:
189                 List();
190                 break;
191             case 4:
192                 exit(0);
193         }
194     }
195     //主函數不做解釋了。這和之前的一模一樣啊。
196 }

 反思:其中關於塊鏈結構的應用那兩段代碼,應當好好理解。是個很好用的思路。

 

 

 

實例099   廣義表的存儲        實例100    廣義表的存儲

ps:廣義表是一種非線性的列表。是線性表和樹的推廣。廣泛應用與人工智能領域。有興趣的小伙伴要多多注意了。

問題:我將兩個問題和在了一起。編程實現用動態鏈接結構存儲廣義表與廣義表的復制,要求輸出該廣義表的長度和深度,以及表頭和表尾。

邏輯: 在一個廣義表中,數據元素可以是一個單一元素,也可以是子表,相應地在對應的存儲結構中,存儲結構節點也有單元素節點和子表節點之分。單元素節點包含值域和指向其後繼結點的指針域。對於子表節點,應包含指向子表中第一個節點的表頭指針和指向其後繼結點的指針域。為了區分廣義表中單元素節點和子表元素,我們設置tag作為標志,當tag=0時表示本節點為單元素,當tag=1時表示本節點為子表。

    當tag=1時,說明其為子表節點,則第二域是sublist域,存放的是指向其子表的指針,next域存放指向與該節點同層的直接後繼結點的指針,當該節點是所在層的最後一個節點時,此時next為空(和之前鏈表等,一樣)。

    廣義表的復制可以采用遞歸實現。

    其實,廣義表這個東西如果有圖,看一下圖,就立馬懂了。我過會兒找找,有的話,就貼上。(我才不會說,我還不知道怎麼添加圖片呢。)

代碼

  1 #include<stdio.h>
  2 #include<stdlib.h>
  3 typedef char ElemType;
  4 //替換的實現
  5 typedef struct lnode
  6 //
  7 {
  8     int tag;
  9     union
 10     //由於,節點元素要麼是單一元素,要麼是子表元素。所以采用了union來統一管理,同時實現存儲空間的節約。
 11     {
 12         ElemType data;
 13         //用來存放單一元素。
 14         struct lnode *sublist;
 15         //用來存放子表元素--子表頭結點指針地址。
 16     }val;
 17     //值域
 18     struct lnode *next;
 19     //指向後繼節點的指針域。
 20 }GLNode;
 21 //廣義表結構體名。
 22 
 23 void creatGList(struct lnode **gl)
 24 //創建函數creatGList(),用來實現廣義表的創建。
 25 {
 26     char ch;
 27     ch=getchar();
 28     if(ch=='#')
 29     {
 30         *gl=NULL;
 31     }
 32     else if(ch=='(')
 33     //輸入的字符若是左括號,則建立子表。
 34     {
 35         *gl=malloc(sizeof(struct lnode));
 36         //創建存儲空間。
 37         (*gl)->tag=1;
 38         //表明是子表
 39         creatGList(&((*gl)->val.sublist));
 40         //調用函數creatGList(),創建子表。
 41     }
 42     else
 43     {
 44         *gl=malloc(sizeof(struct lnode));
 45         //創建存儲空間
 46         (*gl)->tag=0;
 47         //表明是字符。
 48         (*gl)->val.data=ch;
 49         //存儲輸入的字符。
 50     }
 51     ch=getchar();
 52     if(*gl==NULL)
 53     {
 54         ;
 55     }
 56     else if(ch==',')
 57     //輸入的是",",表示遞歸構造後繼表。
 58     {
 59         creatGList(&((*gl)->next));
 60     }
 61     else
 62         (*gl)->next=NULL;
 63     return;
 64 }
 65 
 66 void printGList(struct lnode *gl)
 67 //創建函數printGList(),用於輸出廣義表。
 68 {
 69     if(gl->tag==1)
 70     //如果類型是子表,則輸出括號
 71     {
 72         printf("(");
 73         //先輸出左括號
 74         if(gl->val.sublist==NULL)
 75         {
 76             printf("#");
 77         }
 78         else
 79         {
 80             printGList(gl->val.sublist);
 81             //遞歸輸出子表
 82         }
 83         printf(")");
 84         //輸出右括號
 85     }
 86     else
 87     //否則,即輸出的是字符。
 88         printf("%c",gl->val.data);
 89         //直接輸出字符。
 90     if(gl->next!=NULL)
 91     {
 92         printf(",");
 93         printGList(gl->next);
 94         //遞歸輸出後繼表。
 95     }
 96     return;
 97 }
 98 
 99 int GLLength(GLNode *gl)
100 //創建函數GLLength(),用來計算廣義表的長度。
101 {
102     int n=0;
103     gl=gl->val.sublist;
104     while(gl!=NULL)
105     {
106         n++;
107         //長度計數。
108         gl=gl->next;
109         //指向下一個節點。
110     }
111     return n;
112 }
113 
114 int GLDepth(GLNode *gl)
115 //創建函數GLDepth(),用來計算廣義表的深度。
116 {
117     int max=0,dep;
118     if(gl->tag==0)
119         return 0;
120     gl=gl->val.sublist;
121     if(gl=NULL)
122         return 1;
123     while(gl!=NULL)
124     {
125         if(gl->tag==1)
126         //類型為子表
127         {
128             dep=GLDepth(gl);
129             //遞歸計算廣義表的深度。
130             if(dep>max)
131                 max=dep;
132                 //記錄下最大深度。
133         }
134         gl=gl->next;
135         //指向下一個節點。
136     }
137     return(max+1);
138 }
139 
140 GLNode *GLCopy(GLNode *gl)
141 //創建函數GLCopy(),用來實現廣義表的復制。
142 {
143     GLNode *q;
144     if(gl==NULL)
145         return NULL;
146     q=(GLNode*)malloc(sizeof(GLNode));
147     //申請存儲空間
148     q->tag=gl->tag;
149     //復制元素類型。
150     if(gl->tag==1)
151         q->val.sublist=GLCopy(gl->val.sublist);
152         //遞歸復制子表
153     else
154         q->val.data=gl->val.sublist;
155         //遞歸復制數據元素
156     q->next=GLCopy(gl->next);
157     //復制next信息,指向下一個節點,遞歸復制。
158     return q;
159 }
160 GLNode *head(GLNode *gl)
161 //創建函數head(),用來實現計算廣義表的表頭。
162 {
163     GLNode *p=gl->val.sublist;
164     //初始化,賦值p.
165     GLNode *q,*t;
166     //初始化q,t。
167     if(gl==NULL)
168     {
169         printf("NULL\n");
170         return NULL;
171     }
172     if(gl->tag==0)
173     //如果廣義表為空。
174     {
175         printf("atom is not head! \n");
176         //輸出廣義表沒有表頭。
177         return NULL;
178     }
179     if(p->tag==0)
180     //如果元素的類型為字符
181     {
182         q=(GLNode*)malloc(sizeof(GLNode));
183         //為q申請存儲空間。
184         q->tag=0;
185         //記錄數據類型
186         q->val.data=p->val.data;
187         //復制數據。
188         q->next=NULL;
189         //單個元素的下一個節點為空。
190     }
191     else
192     {
193         t=(GLNode*)malloc(sizeof(GLNode));
194         //申請存儲空間。
195         t->tag=1;
196         //記錄元素的類型為子表
197         t->val.sublist=p->val.sublist;
198         //賦值子表
199         t->next=NULL;
200         //單個元素的下一個節點為空。
201         q=GLCopy(t);
202         //復制子表。
203         free(t);
204         //釋放t的存儲空間。
205     }
206     return q;
207     //返回q。
208 }
209 
210 GLNode *tail(GLNode *gl)
211 //創建函數tail(),用來計算廣義表的表尾。
212 //概念同上,故不做注釋。參考上一個函數。
213 {
214     GLNode *p=gl->val.sublist;
215     GLNode *q,*t;
216     if(gl==NULL)
217     {
218         printf("NULL\n");
219         return NULL;
220     }
221     if(gl->tag==0)
222     {
223         printf("atom is not tail!\n");
224         return NULL;
225     }
226     p=p->next;
227     t=(GLNode*)malloc(sizeof(GLNode));
228     t->tag=1;
229     t->val.sublist=p;
230     t->next=NULL;
231     q=GLCopy(t);
232     free(t);
233     return q;
234 }
235 
236 main()
237 //創建主函數main(),作為程序入口。用來執行輸入、調用函數等功能。
238 {
239     int len=0;
240     int dep=0;
241     struct lnode *g,*v;
242     struct lnode *h;
243     printf("Input Example: (a,b,(c,d,(e,f,g),h,i),j)");
244     printf("\n");
245     printf("PS:輸入的‘()’是圓括號,並不是尖括號。");
246     //添加備注。
247     printf("\n");
248     creatGList(&h);
249     //創建廣義表。切記這裡是&h。
250     len=GLLength(h);
251     //計算廣義表長度。
252     dep=GLDepth(h);
253     //計算廣義表深度。
254     printf("\n");
255     printf("The length is:");
256     printf("%d",len);
257     printf("\n");
258     printf("The depth is:");
259     printf("%d",dep);
260     printf("\n");
261     v=head(h);
262     g=tail(h);
263     //賦值。
264     if(v!=NULL)
265     //計算,並輸出廣義表的表頭。
266     {
267         printf("The Head is:");
268         printGList(v);
269         printf("\n");
270     }
271     if(g!=NULL)
272     //計算,並輸出廣義表的表尾。
273     {
274         printf("The Tail is:");
275         printGList(g);
276         printf("\n");
277     }
278     if(h!=NULL)
279     //展示整個廣義表。
280     {
281         printf("Glist is:");
282         printGList(h);
283         printf("\n");
284     }
285     else
286         printf("Glist is NULL");
287 }

反思:其中有著不少的小點,需要記憶、理解。值得好好看看。

PS:1.關鍵字union:union 關鍵字的用法與struct 的用法非常類似。
union 維護足夠的空間來置放多個數據成員中的“一種”,而不是為每一個數據成員配置空間,在union 中所有的數據成員共用一個空間,同一時間只能儲存其中一個數據成員,所有的數據成員具有相同的起始地址。例子如下:
union StateMachine
{
   char character;
   int number;
   char *str;
   double exp;
};
一個union 只配置一個足夠大的空間以來容納最大長度的數據成員,以上例而言,最大長度是double 型態,所以StateMachine 的空間大小就是double 數據類型的大小。
在C++裡,union 的成員默認屬性頁為public。union 主要用來壓縮空間。如果一些數據不可能在同一時間同時被用到,則可以使用union。

以上關於union的說明采摘自:http://c.biancheng.net/cpp/html/450.html, 其中還有關於大小端模式、確認系統大小端方法的具體說明,感興趣的可以看一看。

 

 

 

到這裡,棧與隊列,串與廣義表就完成了。下一次就是這章的最後知識點--樹與圖。

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved