數據結構(C語言版)嚴蔚敏 課後答案

數據結構(C語言版)嚴蔚敏
第1章 緒論
1.1 簡述下列術語:數據,數據元素、數據對象、數據結構、存儲結構、數據類型和抽象數據類型。
解:數據是對客觀事物的符號表示。在計算機科學中是指所有能輸入到計算機中並被計算機程序處理的符號的總稱。
數據元素是數據的基本單位,在計算機程序中通常作爲一個整體進行考慮和處理。
數據對象是性質相同的數據元素的集合,是數據的一個子集。
數據結構是相互之間存在一種或多種特定關係的數據元素的集合。
存儲結構是數據結構在計算機中的表示。
數據類型是一個值的集合和定義在這個值集上的一組操作的總稱。
抽象數據類型是指一個數學模型以及定義在該模型上的一組操作。是對一般數據類型的擴展。
1.2 試描述數據結構和抽象數據類型的概念與程序設計語言中數據類型概念的區別。
解:抽象數據類型包含一般數據類型的概念,但含義比一般數據類型更廣、更抽象。一般數據類型由具體語言系統內部定義,直接提供給編程者定義用戶數據,因此稱它們爲預定義數據類型。抽象數據類型通常由編程者定義,包括定義它所使用的數據和在這些數據上所進行的操作。在定義抽象數據類型中的數據部分和操作部分時,要求只定義到數據的邏輯結構和操作說明,不考慮數據的存儲結構和操作的具體實現,這樣抽象層次更高,更能爲其他用戶提供良好的使用接口。
1.3 設有數據結構(D,R),其中
, ,
試按圖論中圖的畫法慣例畫出其邏輯結構圖。
解:

1.4 試仿照三元組的抽象數據類型分別寫出抽象數據類型複數和有理數的定義(有理數是其分子、分母均爲自然數且分母不爲零的分數)。
解:
ADT Complex{
數據對象:D={r,i|r,i爲實數}
數據關係:R={<r,i>}
基本操作:
InitComplex(&C,re,im)
操作結果:構造一個複數C,其實部和虛部分別爲re和im
DestroyCmoplex(&C)
操作結果:銷燬複數C
Get(C,k,&e)
操作結果:用e返回複數C的第k元的值
Put(&C,k,e)
操作結果:改變複數C的第k元的值爲e
IsAscending©
操作結果:如果複數C的兩個元素按升序排列,則返回1,否則返回0
IsDescending©
操作結果:如果複數C的兩個元素按降序排列,則返回1,否則返回0
Max(C,&e)
操作結果:用e返回複數C的兩個元素中值較大的一個
Min(C,&e)
操作結果:用e返回複數C的兩個元素中值較小的一個
}ADT Complex

ADT RationalNumber{
數據對象:D={s,m|s,m爲自然數,且m不爲0}
數據關係:R={<s,m>}
基本操作:
InitRationalNumber(&R,s,m)
操作結果:構造一個有理數R,其分子和分母分別爲s和m
DestroyRationalNumber(&R)
操作結果:銷燬有理數R
Get(R,k,&e)
操作結果:用e返回有理數R的第k元的值
Put(&R,k,e)
操作結果:改變有理數R的第k元的值爲e
IsAscending®
操作結果:若有理數R的兩個元素按升序排列,則返回1,否則返回0
IsDescending®
操作結果:若有理數R的兩個元素按降序排列,則返回1,否則返回0
Max(R,&e)
操作結果:用e返回有理數R的兩個元素中值較大的一個
Min(R,&e)
操作結果:用e返回有理數R的兩個元素中值較小的一個
}ADT RationalNumber
1.5 試畫出與下列程序段等價的框圖。
(1) product=1; i=1;
while(i<=n){
product = i;
i++;
}
(2) i=0;
do {
i++;
} while((i!=n) && (a[i]!=x));
(3) switch {
case x<y: z=y-x; break;
case x=y: z=abs(x
y); break;
default: z=(x-y)/abs(x)abs(y);
}
1.6 在程序設計中,常用下列三種不同的出錯處理方式:
(1) 用exit語句終止執行並報告錯誤;
(2) 以函數的返回值區別正確返回或錯誤返回;
(3) 設置一個整型變量的函數參數以區別正確返回或某種錯誤返回。
試討論這三種方法各自的優缺點。
解:(1)exit常用於異常錯誤處理,它可以強行中斷程序的執行,返回操作系統。
(2)以函數的返回值判斷正確與否常用於子程序的測試,便於實現程序的局部控制。
(3)用整型函數進行錯誤處理的優點是可以給出錯誤類型,便於迅速確定錯誤。
1.7 在程序設計中,可採用下列三種方法實現輸出和輸入:
(1) 通過scanf和printf語句;
(2) 通過函數的參數顯式傳遞;
(3) 通過全局變量隱式傳遞。
試討論這三種方法的優缺點。
解:(1)用scanf和printf直接進行輸入輸出的好處是形象、直觀,但缺點是需要對其進行格式控制,較爲煩瑣,如果出現錯誤,則會引起整個系統的崩潰。
(2)通過函數的參數傳遞進行輸入輸出,便於實現信息的隱蔽,減少出錯的可能。
(3)通過全局變量的隱式傳遞進行輸入輸出最爲方便,只需修改變量的值即可,但過多的全局變量使程序的維護較爲困難。
1.8 設n爲正整數。試確定下列各程序段中前置以記號@的語句的頻度:
(1) i=1; k=0;
while(i<=n-1){
@ k += 10
i;
i++;
}
(2) i=1; k=0;
do {
@ k += 10i;
i++;
} while(i<=n-1);
(3) i=1; k=0;
while (i<=n-1) {
i++;
@ k += 10
i;
}
(4) k=0;
for(i=1; i<=n; i++) {
for(j=i; j<=n; j++)
@ k++;
}
(5) for(i=1; i<=n; i++) {
for(j=1; j<=i; j++) {
for(k=1; k<=j; k++)
@ x += delta;
}
(6) i=1; j=0;
while(i+j<=n) {
@ if(i>j) j++;
else i++;
}
(7) x=n; y=0; // n是不小於1的常數
while(x>=(y+1)*(y+1)) {
@ y++;
}
(8) x=91; y=100;
while(y>0) {
@ if(x>100) { x -= 10; y–; }
else x++;
}
解:(1) n-1
(2) n-1
(3) n-1
(4) n+(n-1)+(n-2)+…+1=
(5) 1+(1+2)+(1+2+3)+…+(1+2+3+…+n)=
=
=
(6) n
(7) 向下取整
(8) 1100
1.9 假設n爲2的乘冪,並且n>2,試求下列算法的時間複雜度及變量count的值(以n的函數形式表示)。
int Time(int n) {
count = 0; x=2;
while(x<n/2) {
x *= 2; count++;
}
return count;
}
解:
count=
1.11 已知有實現同一功能的兩個算法,其時間複雜度分別爲 和 ,假設現實計算機可連續運算的時間爲 秒(100多天),又每秒可執行基本操作(根據這些操作來估算算法時間複雜度) 次。試問在此條件下,這兩個算法可解問題的規模(即n值的範圍)各爲多少?哪個算法更適宜?請說明理由。
解: n=40
n=16
則對於同樣的循環次數n,在這個規模下,第二種算法所花費的代價要大得多。故在這個規模下,第一種算法更適宜。
1.12 設有以下三個函數:
, ,
請判斷以下斷言正確與否:
(1) f(n)是O(g(n))
(2) h(n)是O(f(n))
(3) g(n)是O(h(n))
(4) h(n)是O(n3.5)
(5) h(n)是O(nlogn)
解:(1)對 (2)錯 (3)錯 (4)對 (5)錯
1.13 試設定若干n值,比較兩函數 和 的增長趨勢,並確定n在什麼範圍內,函數 的值大於 的值。
解: 的增長趨勢快。但在n較小的時候, 的值較大。
當n>438時,
1.14 判斷下列各對函數 和 ,當 時,哪個函數增長更快?
(1) ,
(2) ,
(3) ,
(4) ,
解:(1)g(n)快 (2)g(n)快 (3)f(n)快 (4) f(n)快
1.15 試用數學歸納法證明:
(1)
(2)
(3)
(4)
1.16 試寫一算法,自大至小依次輸出順序讀入的三個整數X,Y和Z的值
解:
int max3(int x,int y,int z)
{
if(x>y)
if(x>z) return x;
else return z;
else
if(y>z) return y;
else return z;
}
1.17 已知k階斐波那契序列的定義爲
, ,…, , ;

試編寫求k階斐波那契序列的第m項值的函數算法,k和m均以值調用的形式在函數參數表中出現。
解:k>0爲階數,n爲數列的第n項
int Fibonacci(int k,int n)
{
if(k<1) exit(OVERFLOW);
int p,x;
p=new int[k+1];
if(!p) exit(OVERFLOW);
int i,j;
for(i=0;i<k+1;i++){
if(i<k-1) p[i]=0;
else p[i]=1;
}
for(i=k+1;i<n+1;i++){
x=p[0];
for(j=0;j<k;j++) p[j]=p[j+1];
p[k]=2
p[k-1]-x;
}
return p[k];
}
1.18 假設有A,B,C,D,E五個高等院校進行田徑對抗賽,各院校的單項成績均已存入計算機,並構成一張表,表中每一行的形式爲
項目名稱 性別 校名 成績 得分
編寫算法,處理上述表格,以統計各院校的男、女總分和團體總分,並輸出。
解:
typedef enum{A,B,C,D,E} SchoolName;
typedef enum{Female,Male} SexType;
typedef struct{
char event[3]; //項目
SexType sex;
SchoolName school;
int score;
} Component;
typedef struct{
int MaleSum; //男團總分
int FemaleSum; //女團總分
int TotalSum; //團體總分
} Sum;
Sum SumScore(SchoolName sn,Component a[],int n)
{
Sum temp;
temp.MaleSum=0;
temp.FemaleSum=0;
temp.TotalSum=0;
int i;
for(i=0;i<n;i++){
if(a[i].schoolsn){
if(a[i].sex
Male) temp.MaleSum+=a[i].score;
if(a[i].sex==Female) temp.FemaleSum+=a[i].score;
}
}
temp.TotalSum=temp.MaleSum+temp.FemaleSum;
return temp;
}
1.19 試編寫算法,計算 的值並存入數組a[0…arrsize-1]的第i-1個分量中(i=1,2,…,n)。假設計算機中允許的整數最大值爲maxint,則當n>arrsize或對某個 ,使 時,應按出錯處理。注意選擇你認爲較好的出錯處理方法。
解:
#include<iostream.h>
#include<stdlib.h>
#define MAXINT 65535
#define ArrSize 100
int fun(int i);

int main()
{
int i,k;
int a[ArrSize];
cout<<“Enter k:”;
cin>>k;
if(k>ArrSize-1) exit(0);
for(i=0;i<=k;i++){
if(i==0) a[i]=1;
else{
if(2ia[i-1]>MAXINT) exit(0);
else a[i]=2ia[i-1];
}
}
for(i=0;i<=k;i++){
if(a[i]>MAXINT) exit(0);
else cout<<a[i]<<" ";
}
return 0;
}
1.20 試編寫算法求一元多項式的值 的值 ,並確定算法中每一語句的執行次數和整個算法的時間複雜度。注意選擇你認爲較好的輸入和輸出方法。本題的輸入爲 , 和 ,輸出爲 。
解:
#include<iostream.h>
#include<stdlib.h>
#define N 10
double polynomail(int a[],int i,double x,int n);
int main()
{
double x;
int n,i;
int a[N];
cout<<“輸入變量的值x:”;
cin>>x;
cout<<“輸入多項式的階次n:”;
cin>>n;
if(n>N-1) exit(0);
cout<<“輸入多項式的係數a[0]–a[n]:”;
for(i=0;i<=n;i++) cin>>a[i];
cout<<"The polynomail value is "<<polynomail(a,n,x,n)<<endl;
return 0;
}
double polynomail(int a[],int i,double x,int n)
{
if(i>0) return a[n-i]+polynomail(a,i-1,x,n)*x;
else return a[n];
}
本算法的時間複雜度爲o(n)。
第2章 線性表
2.1 描述以下三個概念的區別:頭指針,頭結點,首元結點(第一個元素結點)。
解:頭指針是指向鏈表中第一個結點的指針。首元結點是指鏈表中存儲第一個數據元素的結點。頭結點是在首元結點之前附設的一個結點,該結點不存儲數據元素,其指針域指向首元結點,其作用主要是爲了方便對鏈表的操作。它可以對空表、非空表以及首元結點的操作進行統一處理。
2.2 填空題。
解:(1) 在順序表中插入或刪除一個元素,需要平均移動表中一半元素,具體移動的元素個數與元素在表中的位置有關。
(2) 順序表中邏輯上相鄰的元素的物理位置必定緊鄰。單鏈表中邏輯上相鄰的元素的物理位置不一定緊鄰。
(3) 在單鏈表中,除了首元結點外,任一結點的存儲位置由其前驅結點的鏈域的值指示。
(4) 在單鏈表中設置頭結點的作用是插入和刪除首元結點時不用進行特殊處理。
2.3 在什麼情況下用順序表比鏈表好?
解:當線性表的數據元素在物理位置上是連續存儲的時候,用順序表比用鏈表好,其特點是可以進行隨機存取。
2.4 對以下單鏈表分別執行下列各程序段,並畫出結果示意圖。

解:

2.5 畫出執行下列各行語句後各指針及鏈表的示意圖。
L=(LinkList)malloc(sizeof(LNode)); P=L;
for(i=1;i<=4;i++){
P->next=(LinkList)malloc(sizeof(LNode));
P=P->next; P->data=i2-1;
}
P->next=NULL;
for(i=4;i>=1;i–) Ins_LinkList(L,i+1,i
2);
for(i=1;i<=3;i++) Del_LinkList(L,i);
解:

2.6 已知L是無表頭結點的單鏈表,且P結點既不是首元結點,也不是尾元結點,試從下列提供的答案中選擇合適的語句序列。
a. 在P結點後插入S結點的語句序列是__________________。
b. 在P結點前插入S結點的語句序列是__________________。
c. 在表首插入S結點的語句序列是__________________。
d. 在表尾插入S結點的語句序列是__________________。
(1) P->next=S;
(2) P->next=P->next->next;
(3) P->next=S->next;
(4) S->next=P->next;
(5) S->next=L;
(6) S->next=NULL;
(7) Q=P;
(8) while(P->next!=Q) P=P->next;
(9) while(P->next!=NULL) P=P->next;
(10) P=Q;
(11) P=L;
(12) L=S;
(13) L=P;
解:a. (4) (1)
b. (7) (11) (8) (4) (1)
c. (5) (12)
d. (9) (1) (6)
2.7 已知L是帶表頭結點的非空單鏈表,且P結點既不是首元結點,也不是尾元結點,試從下列提供的答案中選擇合適的語句序列。
a. 刪除P結點的直接後繼結點的語句序列是____________________。
b. 刪除P結點的直接前驅結點的語句序列是____________________。
c. 刪除P結點的語句序列是____________________。
d. 刪除首元結點的語句序列是____________________。
e. 刪除尾元結點的語句序列是____________________。
(1) P=P->next;
(2) P->next=P;
(3) P->next=P->next->next;
(4) P=P->next->next;
(5) while(P!=NULL) P=P->next;
(6) while(Q->next!=NULL) { P=Q; Q=Q->next; }
(7) while(P->next!=Q) P=P->next;
(8) while(P->next->next!=Q) P=P->next;
(9) while(P->next->next!=NULL) P=P->next;
(10) Q=P;
(11) Q=P->next;
(12) P=L;
(13) L=L->next;
(14) free(Q);
解:a. (11) (3) (14)
b. (10) (12) (8) (3) (14)
c. (10) (12) (7) (3) (14)
d. (12) (11) (3) (14)
e. (9) (11) (3) (14)
2.8 已知P結點是某雙向鏈表的中間結點,試從下列提供的答案中選擇合適的語句序列。
a. 在P結點後插入S結點的語句序列是_______________________。
b. 在P結點前插入S結點的語句序列是_______________________。
c. 刪除P結點的直接後繼結點的語句序列是_______________________。
d. 刪除P結點的直接前驅結點的語句序列是_______________________。
e. 刪除P結點的語句序列是_______________________。
(1) P->next=P->next->next;
(2) P->priou=P->priou->priou;
(3) P->next=S;
(4) P->priou=S;
(5) S->next=P;
(6) S->priou=P;
(7) S->next=P->next;
(8) S->priou=P->priou;
(9) P->priou->next=P->next;
(10) P->priou->next=P;
(11) P->next->priou=P;
(12) P->next->priou=S;
(13) P->priou->next=S;
(14) P->next->priou=P->priou;
(15) Q=P->next;
(16) Q=P->priou;
(17) free§;
(18) free(Q);
解:a. (7) (3) (6) (12)
b. (8) (4) (5) (13)
c. (15) (1) (11) (18)
d. (16) (2) (10) (18)
e. (14) (9) (17)
2.9 簡述以下算法的功能。
(1) Status A(LinkedList L) { //L是無表頭結點的單鏈表
if(L && L->next) {
Q=L; L=L->next; P=L;
while(P->next) P=P->next;
P->next=Q; Q->next=NULL;
}
return OK;
}
(2) void BB(LNode s, LNode q) {
p=s;
while(p->next!=q) p=p->next;
p->next =s;
}
void AA(LNode pa, LNode pb) {
//pa和pb分別指向單循環鏈表中的兩個結點
BB(pa,pb);
BB(pb,pa);
}
解:(1) 如果L的長度不小於2,將L的首元結點變成尾元結點。
(2) 將單循環鏈表拆成兩個單循環鏈表。
2.10 指出以下算法中的錯誤和低效之處,並將它改寫爲一個既正確又高效的算法。
Status DeleteK(SqList &a,int i,int k)
{
//本過程從順序存儲結構的線性表a中刪除第i個元素起的k個元素
if(i<1||k<0||i+k>a.length) return INFEASIBLE;//參數不合法
else {
for(count=1;count<k;count++){
//刪除第一個元素
for(j=a.length;j>=i+1;j–) a.elem[j-i]=a.elem[j];
a.length–;
}
return OK;
}
解:
Status DeleteK(SqList &a,int i,int k)
{
//從順序存儲結構的線性表a中刪除第i個元素起的k個元素
//注意i的編號從0開始
int j;
if(i<0||i>a.length-1||k<0||k>a.length-i) return INFEASIBLE;
for(j=0;j<=k;j++)
a.elem[j+i]=a.elem[j+i+k];
a.length=a.length-k;
return OK;
}
2.11 設順序表va中的數據元素遞增有序。試寫一算法,將x插入到順序表的適當位置上,以保持該表的有序性。
解:
Status InsertOrderList(SqList &va,ElemType x)
{
//在非遞減的順序表va中插入元素x並使其仍成爲順序表的算法
int i;
if(va.lengthva.listsize)return(OVERFLOW);
for(i=va.length;i>0,x<va.elem[i-1];i–)
va.elem[i]=va.elem[i-1];
va.elem[i]=x;
va.length++;
return OK;
}
2.12 設 和 均爲順序表, 和 分別爲 和 中除去最大共同前綴後的子表。若 空表,則 ;若 =空表,而 空表,或者兩者均不爲空表,且 的首元小於 的首元,則 ;否則 。試寫一個比較 , 大小的算法。
解:
Status CompareOrderList(SqList &A,SqList &B)
{
int i,k,j;
k=A.length>B.length?A.length:B.length;
for(i=0;i<k;i++){
if(A.elem[i]>B.elem[i]) j=1;
if(A.elem[i]<B.elem[i]) j=-1;
}
if(A.length>k) j=1;
if(B.length>k) j=-1;
if(A.length
B.length) j=0;
return j;
}
2.13 試寫一算法在帶頭結點的單鏈表結構上實現線性表操作Locate(L,x);
解:
int LocateElem_L(LinkList &L,ElemType x)
{
int i=0;
LinkList p=L;
while(p&&p->data!=x){
p=p->next;
i++;
}
if(!p) return 0;
else return i;
}
2.14 試寫一算法在帶頭結點的單鏈表結構上實現線性表操作Length(L)。
解:
//返回單鏈表的長度
int ListLength_L(LinkList &L)
{
int i=0;
LinkList p=L;
if§ p=p-next;
while§{
p=p->next;
i++;
}
return i;
}
2.15 已知指針ha和hb分別指向兩個單鏈表的頭結點,並且已知兩個鏈表的長度分別爲m和n。試寫一算法將這兩個鏈表連接在一起,假設指針hc指向連接後的鏈表的頭結點,並要求算法以儘可能短的時間完成連接運算。請分析你的算法的時間複雜度。
解:
void MergeList_L(LinkList &ha,LinkList &hb,LinkList &hc)
{
LinkList pa,pb;
pa=ha;
pb=hb;
while(pa->next&&pb->next){
pa=pa->next;
pb=pb->next;
}
if(!pa->next){
hc=hb;
while(pb->next) pb=pb->next;
pb->next=ha->next;
}
else{
hc=ha;
while(pa->next) pa=pa->next;
pa->next=hb->next;
}
}
2.16 已知指針la和lb分別指向兩個無頭結點單鏈表中的首元結點。下列算法是從表la中刪除自第i個元素起共len個元素後,將它們插入到表lb中第i個元素之前。試問此算法是否正確?若有錯,請改正之。
Status DeleteAndInsertSub(LinkedList la,LinkedList lb,int i,int j,int len)
{
if(i<0||j<0||len<0) return INFEASIBLE;
p=la; k=1;
while(k<i){ p=p->next; k++; }
q=p;
while(k<=len){ q=q->next; k++; }
s=lb; k=1;
while(k<j){ s=s->next; k++; }
s->next=p; q->next=s->next;
return OK;
}
解:
Status DeleteAndInsertSub(LinkList &la,LinkList &lb,int i,int j,int len)
{
LinkList p,q,s,prev=NULL;
int k=1;
if(i<0||j<0||len<0) return INFEASIBLE;
// 在la表中查找第i個結點
p=la;
while(p&&k<i){
prev=p;
p=p->next;
k++;
}
if(!p)return INFEASIBLE;
// 在la表中查找第i+len-1個結點
q=p; k=1;
while(q&&k<len){
q=p->next;
k++;
}
if(!q)return INFEASIBLE;
// 完成刪除,注意,i=1的情況需要特殊處理
if(!prev) la=q->next;
else prev->next=q->next;
// 將從la中刪除的結點插入到lb中
if(j=1){
q->next=lb;
lb=p;
}
else{
s=lb; k=1;
while(s&&k<j-1){
s=s->next;
k++;
}
if(!s)return INFEASIBLE;
q->next=s->next;
s->next=p; //完成插入
}
return OK;
}
2.17 試寫一算法,在無頭結點的動態單鏈表上實現線性表操作Insert(L,i,b),並和在帶頭結點的動態單鏈表上實現相同操作的算法進行比較。
2.18試寫一算法,實現線性表操作Delete(L,i),並和在帶頭結點的動態單鏈表上實現相同操作的算法進行比較。
2.19 已知線性表中的元素以值遞增有序排列,並以單鏈表作存儲結構。試寫一高效的算法,刪除表中所有值大於mink且小於maxk的元素(若表中存在這樣的元素),同時釋放被刪結點空間,並分析你的算法的時間複雜度(注意,mink和maxk是給定的兩個參變量,它們的值可以和表中的元素相同,也可以不同)。
解:
Status ListDelete_L(LinkList &L,ElemType mink,ElemType maxk)
{
LinkList p,q,prev=NULL;
if(mink>maxk)return ERROR;
p=L;
prev=p;
p=p->next;
while(p&&p->data<maxk){
if(p->data<=mink){
prev=p;
p=p->next;
}
else{
prev->next=p->next;
q=p;
p=p->next;
free(q);
}
}
return OK;
}
2.20 同2.19題條件,試寫一高效的算法,刪除表中所有值相同的多餘元素(使得操作後的線性表中所有元素的值均不相同),同時釋放被刪結點空間,並分析你的算法的時間複雜度。
解:
void ListDelete_LSameNode(LinkList &L)
{
LinkList p,q,prev;
p=L;
prev=p;
p=p->next;
while§{
prev=p;
p=p->next;
if(p&&p->dataprev->data){
prev->next=p->next;
q=p;
p=p->next;
free(q);
}
}
}
2.21 試寫一算法,實現順序表的就地逆置,即利用原表的存儲空間將線性表 逆置爲 。
解:
// 順序表的逆置
Status ListOppose_Sq(SqList &L)
{
int i;
ElemType x;
for(i=0;i<L.length/2;i++){
x=L.elem[i];
L.elem[i]=L.elem[L.length-1-i];
L.elem[L.length-1-i]=x;
}
return OK;
}
2.22 試寫一算法,對單鏈表實現就地逆置。
解:
// 帶頭結點的單鏈表的逆置
Status ListOppose_L(LinkList &L)
{
LinkList p,q;
p=L;
p=p->next;
L->next=NULL;
while§{
q=p;
p=p->next;
q->next=L->next;
L->next=q;
}
return OK;
}
2.23 設線性表 , ,試寫一個按下列規則合併A,B爲線性表C的算法,即使得
當 時;
當 時。
線性表A,B和C均以單鏈表作存儲結構,且C表利用A表和B表中的結點空間構成。注意:單鏈表的長度值m和n均未顯式存儲。
解:
// 將合併後的結果放在C表中,並刪除B表
Status ListMerge_L(LinkList &A,LinkList &B,LinkList &C)
{
LinkList pa,pb,qa,qb;
pa=A->next;
pb=B->next;
C=A;
while(pa&&pb){
qa=pa; qb=pb;
pa=pa->next; pb=pb->next;
qb->next=qa->next;
qa->next=qb;
}
if(!pa)qb->next=pb;
pb=B;
free(pb);
return OK;
}
2.24 假設有兩個按元素值遞增有序排列的線性表A和B,均以單鏈表作存儲結構,請編寫算法將A表和B表歸併成一個按元素值遞減有序(即非遞增有序,允許表中含有值相同的元素)排列的線性表C,並要求利用原表(即A表和B表)的結點空間構造C表。
解:
// 將合併逆置後的結果放在C表中,並刪除B表
Status ListMergeOppose_L(LinkList &A,LinkList &B,LinkList &C)
{
LinkList pa,pb,qa,qb;
pa=A;
pb=B;
qa=pa; // 保存pa的前驅指針
qb=pb; // 保存pb的前驅指針
pa=pa->next;
pb=pb->next;
A->next=NULL;
C=A;
while(pa&&pb){
if(pa->datadata){
qa=pa;
pa=pa->next;
qa->next=A->next; //將當前最小結點插入A表表頭
A->next=qa;
}
else{
qb=pb;
pb=pb->next;
qb->next=A->next; //將當前最小結點插入A表表頭
A->next=qb;
}
}
while(pa){
qa=pa;
pa=pa->next;
qa->next=A->next;
A->next=qa;
}
while(pb){
qb=pb;
pb=pb->next;
qb->next=A->next;
A->next=qb;
}
pb=B;
free(pb);
return OK;
}
2.25 假設以兩個元素依值遞增有序排列的線性表A和B分別表示兩個集合(即同一表中的元素值各不相同),現要求另闢空間構成一個線性表C,其元素爲A和B中元素的交集,且表C中的元素有依值遞增有序排列。試對順序表編寫求C的算法。
解:
// 將A、B求交後的結果放在C表中
Status ListCross_Sq(SqList &A,SqList &B,SqList &C)
{
int i=0,j=0,k=0;
while(i<A.length && j<B.length){
if(A.elem[i]<B.elem[j]) i++;
else
if(A.elem[i]>B.elem[j]) j++;
else{
ListInsert_Sq(C,k,A.elem[i]);
i++;
k++;
}
}
return OK;
}
2.26 要求同2.25題。試對單鏈表編寫求C的算法。
解:
// 將A、B求交後的結果放在C表中,並刪除B表
Status ListCross_L(LinkList &A,LinkList &B,LinkList &C)
{
LinkList pa,pb,qa,qb,pt;
pa=A;
pb=B;
qa=pa; // 保存pa的前驅指針
qb=pb; // 保存pb的前驅指針
pa=pa->next;
pb=pb->next;
C=A;
while(pa&&pb){
if(pa->datadata){
pt=pa;
pa=pa->next;
qa->next=pa;
free(pt);
}
else
if(pa->data>pb->data){
pt=pb;
pb=pb->next;
qb->next=pb;
free(pt);
}
else{
qa=pa;
pa=pa->next;
}
}
while(pa){
pt=pa;
pa=pa->next;
qa->next=pa;
free(pt);
}
while(pb){
pt=pb;
pb=pb->next;
qb->next=pb;
free(pt);
}
pb=B;
free(pb);
return OK;
}
2.27 對2.25題的條件作以下兩點修改,對順序表重新編寫求得表C的算法。
(1) 假設在同一表(A或B)中可能存在值相同的元素,但要求新生成的表C中的元素值各不相同;
(2) 利用A表空間存放表C。
解:
(1)
// A、B求交,然後刪除相同元素,將結果放在C表中
Status ListCrossDelSame_Sq(SqList &A,SqList &B,SqList &C)
{
int i=0,j=0,k=0;
while(i<A.length && j<B.length){
if(A.elem[i]<B.elem[j]) i++;
else
if(A.elem[i]>B.elem[j]) j++;
else{
if(C.length
0){
ListInsert_Sq(C,k,A.elem[i]);
k++;
}
else
if(C.elem[C.length-1]!=A.elem[i]){
ListInsert_Sq(C,k,A.elem[i]);
k++;
}
i++;
}
}
return OK;
}
(2)
// A、B求交,然後刪除相同元素,將結果放在A表中
Status ListCrossDelSame_Sq(SqList &A,SqList &B)
{
int i=0,j=0,k=0;
while(i<A.length && j<B.length){
if(A.elem[i]<B.elem[j]) i++;
else
if(A.elem[i]>B.elem[j]) j++;
else{
if(k0){
A.elem[k]=A.elem[i];
k++;
}
else
if(A.elem[k]!=A.elem[i]){
A.elem[k]=A.elem[i];
k++;
}
i++;
}
}
A.length=k;
return OK;
}
2.28 對2.25題的條件作以下兩點修改,對單鏈表重新編寫求得表C的算法。
(1) 假設在同一表(A或B)中可能存在值相同的元素,但要求新生成的表C中的元素值各不相同;
(2) 利用原表(A表或B表)中的結點構成表C,並釋放A表中的無用結點空間。
解:
(1)
// A、B求交,結果放在C表中,並刪除相同元素
Status ListCrossDelSame_L(LinkList &A,LinkList &B,LinkList &C)
{
LinkList pa,pb,qa,qb,pt;
pa=A;
pb=B;
qa=pa; // 保存pa的前驅指針
qb=pb; // 保存pb的前驅指針
pa=pa->next;
pb=pb->next;
C=A;
while(pa&&pb){
if(pa->datadata){
pt=pa;
pa=pa->next;
qa->next=pa;
free(pt);
}
else
if(pa->data>pb->data){
pt=pb;
pb=pb->next;
qb->next=pb;
free(pt);
}
else{
if(pa->data
qa->data){
pt=pa;
pa=pa->next;
qa->next=pa;
free(pt);
}
else{
qa=pa;
pa=pa->next;
}
}
}
while(pa){
pt=pa;
pa=pa->next;
qa->next=pa;
free(pt);
}
while(pb){
pt=pb;
pb=pb->next;
qb->next=pb;
free(pt);
}
pb=B;
free(pb);
return OK;
}
(2)
// A、B求交,結果放在A表中,並刪除相同元素
Status ListCrossDelSame_L(LinkList &A,LinkList &B)
{
LinkList pa,pb,qa,qb,pt;
pa=A;
pb=B;
qa=pa; // 保存pa的前驅指針
qb=pb; // 保存pb的前驅指針
pa=pa->next;
pb=pb->next;
while(pa&&pb){
if(pa->datadata){
pt=pa;
pa=pa->next;
qa->next=pa;
free(pt);
}
else
if(pa->data>pb->data){
pt=pb;
pb=pb->next;
qb->next=pb;
free(pt);
}
else{
if(pa->dataqa->data){
pt=pa;
pa=pa->next;
qa->next=pa;
free(pt);
}
else{
qa=pa;
pa=pa->next;
}
}
}
while(pa){
pt=pa;
pa=pa->next;
qa->next=pa;
free(pt);
}
while(pb){
pt=pb;
pb=pb->next;
qb->next=pb;
free(pt);
}
pb=B;
free(pb);
return OK;
}
2.29 已知A,B和C爲三個遞增有序的線性表,現要求對A表作如下操作:刪去那些既在B表中出現又在C表中出現的元素。試對順序表編寫實現上述操作的算法,並分析你的算法的時間複雜度(注意:題中沒有特別指明同一表中的元素值各不相同)。
解:
// 在A中刪除既在B中出現又在C中出現的元素,結果放在D中
Status ListUnion_Sq(SqList &D,SqList &A,SqList &B,SqList &C)
{
SqList Temp;
InitList_Sq(Temp);
ListCross_L(B,C,Temp);
ListMinus_L(A,Temp,D);
}
2.30 要求同2.29題。試對單鏈表編寫算法,請釋放A表中的無用結點空間。
解:
// 在A中刪除既在B中出現又在C中出現的元素,並釋放B、C
Status ListUnion_L(LinkList &A,LinkList &B,LinkList &C)
{
ListCross_L(B,C);
ListMinus_L(A,B);
}
// 求集合A-B,結果放在A表中,並刪除B表
Status ListMinus_L(LinkList &A,LinkList &B)
{
LinkList pa,pb,qa,qb,pt;
pa=A;
pb=B;
qa=pa; // 保存pa的前驅指針
qb=pb; // 保存pb的前驅指針
pa=pa->next;
pb=pb->next;
while(pa&&pb){
if(pb->datadata){
pt=pb;
pb=pb->next;
qb->next=pb;
free(pt);
}
else
if(pb->data>pa->data){
qa=pa;
pa=pa->next;
}
else{
pt=pa;
pa=pa->next;
qa->next=pa;
free(pt);
}
}
while(pb){
pt=pb;
pb=pb->next;
qb->next=pb;
free(pt);
}
pb=B;
free(pb);
return OK;
}
2.31 假設某個單向循環鏈表的長度大於1,且表中既無頭結點也無頭指針。已知s爲指向鏈表中某個結點的指針,試編寫算法在鏈表中刪除指針s所指結點的前驅結點。
解:
// 在單循環鏈表S中刪除S的前驅結點
Status ListDelete_CL(LinkList &S)
{
LinkList p,q;
if(S
S->next)return ERROR;
q=S;
p=S->next;
while(p->next!=S){
q=p;
p=p->next;
}
q->next=p->next;
free§;
return OK;
}
2.32 已知有一個單向循環鏈表,其每個結點中含三個域:pre,data和next,其中data爲數據域,next爲指向後繼結點的指針域,pre也爲指針域,但它的值爲空,試編寫算法將此單向循環鏈表改爲雙向循環鏈表,即使pre成爲指向前驅結點的指針域。
解:
// 建立一個空的循環鏈表
Status InitList_DL(DuLinkList &L)
{
L=(DuLinkList)malloc(sizeof(DuLNode));
if(!L) exit(OVERFLOW);
L->pre=NULL;
L->next=L;
return OK;
}
// 向循環鏈表中插入一個結點
Status ListInsert_DL(DuLinkList &L,ElemType e)
{
DuLinkList p;
p=(DuLinkList)malloc(sizeof(DuLNode));
if(!p) return ERROR;
p->data=e;
p->next=L->next;
L->next=p;
return OK;
}
// 將單循環鏈表改成雙向鏈表
Status ListCirToDu(DuLinkList &L)
{
DuLinkList p,q;
q=L;
p=L->next;
while(p!=L){
p->pre=q;
q=p;
p=p->next;
}
if(pL) p->pre=q;
return OK;
}
2.33 已知由一個線性鏈表表示的線性表中含有三類字符的數據元素(如:字母字符、數字字符和其他字符),試編寫算法將該線性表分割爲三個循環鏈表,其中每個循環鏈表表示的線性表中均只含一類字符。
解:
// 將單鏈表L劃分成3個單循環鏈表
Status ListDivideInto3CL(LinkList &L,LinkList &s1,LinkList &s2,LinkList &s3)
{
LinkList p,q,pt1,pt2,pt3;
p=L->next;
pt1=s1;
pt2=s2;
pt3=s3;
while§{
if(p->data>=‘0’ && p->data<=‘9’){
q=p;
p=p->next;
q->next=pt1->next;
pt1->next=q;
pt1=pt1->next;
}
else
if((p->data>=‘A’ && p->data<=‘Z’) ||
(p->data>=‘a’ && p->data<=‘z’)){
q=p;
p=p->next;
q->next=pt2->next;
pt2->next=q;
pt2=pt2->next;
}
else{
q=p;
p=p->next;
q->next=pt3->next;
pt3->next=q;
pt3=pt3->next;
}
}
q=L;
free(q);
return OK;
}
在2.34至2.36題中,“異或指針雙向鏈表”類型XorLinkedList和指針異或函數XorP定義爲:
typedef struct XorNode {
char data;
struct XorNode *LRPtr;
} XorNode, *XorPointer;
typede struct { //無頭結點的異或指針雙向鏈表
XorPointer Left, Right; //分別指向鏈表的左側和右端
} XorLinkedList;
XorPointer XorP(XorPointer p, XorPointer q);
// 指針異或函數XorP返回指針p和q的異或值
2.34 假設在算法描述語言中引入指針的二元運算“異或”,若a和b爲指針,則a⊕b的運算結果仍爲原指針類型,且
a⊕(a⊕b)=(a⊕a)⊕b=b
(a⊕b)⊕b=a⊕(b⊕b)=a
則可利用一個指針域來實現雙向鏈表L。鏈表L中的每個結點只含兩個域:data域和LRPtr域,其中LRPtr域存放該結點的左鄰與右鄰結點指針(不存在時爲NULL)的異或。若設指針L.Left指向鏈表中的最左結點,L.Right指向鏈表中的最右結點,則可實現從左向右或從右向左遍歷此雙向鏈表的操作。試寫一算法按任一方向依次輸出鏈表中各元素的值。
解:
Status TraversingLinkList(XorLinkedList &L,char d)
{
XorPointer p,left,right;
if(d
’l’||d==‘L’){
p=L.Left;
left=NULL;
while(p!=NULL){
VisitingData(p->data);
left=p;
p=XorP(left,p->LRPtr);
}
}
else
if(d==‘r’||d==‘R’){
p=L.Right;
right=NULL;
while(p!=NULL){
VisitingData(p->data);
right=p;
p=XorP(p->LRPtr,right);
}
}
else return ERROR;
return OK;
}
2.35 採用2.34題所述的存儲結構,寫出在第i個結點之前插入一個結點的算法。
2.36 採用2.34題所述的存儲結構,寫出刪除第i個結點的算法。
2.37 設以帶頭結點的雙向循環鏈表表示的線性表 。試寫一時間複雜度O(n)的算法,將L改造爲 。
解:
// 將雙向鏈表L=(a1,a2,…,an)改造爲(a1,a3,…,an,…,a2)
Status ListChange_DuL(DuLinkList &L)
{
int i;
DuLinkList p,q,r;
p=L->next;
r=L->pre;
i=1;
while(p!=r){
if(i%20){
q=p;
p=p->next;
// 刪除結點
q->pre->next=q->next;
q->next->pre=q->pre;
// 插入到頭結點的左面
q->pre=r->next->pre;
r->next->pre=q;
q->next=r->next;
r->next=q;
}
else p=p->next;
i++;
}
return OK;
}
2.38 設有一個雙向循環鏈表,每個結點中除有pre,data和next三個域外,還增設了一個訪問頻度域freq。在鏈表被起用之前,頻度域freq的值均初始化爲零,而每當對鏈表進行一次Locate(L,x)的操作後,被訪問的結點(即元素值等於x的結點)中的頻度域freq的值便增1,同時調整鏈表中結點之間的次序,使其按訪問頻度非遞增的次序順序排列,以便始終保持被頻繁訪問的結點總是靠近表頭結點。試編寫符合上述要求的Locate操作的算法。
解:
DuLinkList ListLocate_DuL(DuLinkList &L,ElemType e)
{
DuLinkList p,q;
p=L->next;
while(p!=L && p->data!=e) p=p->next;
if(p
L) return NULL;
else{
p->freq++;
// 刪除結點
p->pre->next=p->next;
p->next->pre=p->pre;
// 插入到合適的位置
q=L->next;
while(q!=L && q->freq>p->freq) q=q->next;
if(qL){
p->next=q->next;
q->next=p;
p->pre=q->pre;
q->pre=p;
}
else{
// 在q之前插入
p->next=q->pre->next;
q->pre->next=p;
p->pre=q->pre;
q->pre=p;
}
return p;
}
}
在2.39至2.40題中,稀疏多項式採用的順序存儲結構SqPoly定義爲
typedef struct {
int coef;
int exp;
} PolyTerm;
typedef struct { //多項式的順序存儲結構
PolyTerm *data;
int last;
} SqPoly;
2.39 已知稀疏多項式 ,其中 , , 。試採用存儲量同多項式項數m成正比的順序存儲結構,編寫求 的算法( 爲給定值),並分析你的算法的時間複雜度。
解:
typedef struct{
int coef;
int exp;
} PolyTerm;
typedef struct{
PolyTerm *data;
int last;
} SqPoly;
// 建立一個多項式
Status PolyInit(SqPoly &L)
{
int i;
PolyTerm p;
cout<<“請輸入多項式的項數:”;
cin>>L.last;
L.data=(PolyTerm )malloc(L.lastsizeof(PolyTerm));
if(!L.data) return ERROR;
p=L.data;
for(i=0;i<L.last;i++){
cout<<“請輸入係數:”;
cin>>p->coef;
cout<<“請輸入指數:”;
cin>>p->exp;
p++;
}
return OK;
}
// 求多項式的值
double PolySum(SqPoly &L,double x0)
{
double Pn,x;
int i,j;
PolyTerm p;
p=L.data;
for(i=0,Pn=0;i<L.last;i++,p++){
for(j=0,x=1;jexp;j++) x=x
x0;
Pn=Pn+p->coef
x;
}
return Pn;
}
2.40 採用2.39題給定的條件和存儲結構,編寫求 的算法,將結果多項式存放在新闢的空間中,並分析你的算法的時間複雜度。
解:
// 求兩多項式的差
Status PolyMinus(SqPoly &L,SqPoly &L1,SqPoly &L2)
{
PolyTerm *p,*p1,*p2;
p=L.data;
p1=L1.data;
p2=L2.data;
int i=0,j=0,k=0;
while(i<L1.last&&j<L2.last){
if(p1->expexp){
p->coef=p1->coef;
p->exp=p1->exp;
p++; k++;
p1++; i++;
}
else
if(p1->exp>p2->exp){
p->coef=-p2->coef;
p->exp=p2->exp;
p++; k++;
p2++; j++;
}
else{
if(p1->coef!=p2->coef){
p->coef=(p1->coef)-(p2->coef);
p->exp=p1->exp;
p++; k++;
}
p1++; p2++;
i++; j++;
}
}
if(i<L1.last)
while(i<L1.last){
p->coef=p1->coef;
p->exp=p1->exp;
p++; k++;
p1++; i++;
}
if(j<L2.last)
while(j<L2.last){
p->coef=-p2->coef;
p->exp=p2->exp;
p++; k++;
p2++; j++;
}
L.last=k;
return OK;
}
在2.41至2.42題中,稀疏多項式採用的循環鏈表存儲結構LinkedPoly定義爲
typedef struct PolyNode {
PolyTerm data;
struct PolyNode *next;
} PolyNode, *PolyLink;
typedef PolyLink LinkedPoly;
2.41 試以循環鏈表作稀疏多項式的存儲結構,編寫求其導函數的方法,要求利用原多項式中的結點空間存放其導函數多項式,同時釋放所有無用結點。
解:
Status PolyDifferential(LinkedPoly &L)
{
LinkedPoly p,q,pt;
q=L;
p=L->next;
while(p!=L){
if(p->data.exp
0){
pt=p;
p=p->next;
q->next=p;
free(pt);
}
else{
p->data.coef=p->data.coef
p->data.exp;
p->data.exp–;
q=p;
p=p->next;
}
}
return OK;
}
2.42 試編寫算法,將一個用循環鏈表表示的稀疏多項式分解成兩個多項式,使這兩個多項式中各自僅含奇次項或偶次項,並要求利用原鏈表中的結點空間構成這兩個鏈表。
解:
// 將單鏈表L劃分成2個單循環鏈表
Status ListDivideInto2CL(LinkedPoly &L,LinkedPoly &L1)
{
LinkedPoly p,p1,q,pt;
q=L;
p=L->next;
p1=L1;
while(p!=L){
if(p->data.exp%2==0){
pt=p;
p=p->next;
q->next=p;
pt->next=p1->next;
p1->next=pt;
p1=p1->next;
}
else{
q=p;
p=p->next;
}
}
return OK;
}
第3章 棧和隊列
3.1 若按教科書3.1.1節中圖3.1(b)所示鐵道進行車廂調度(注意:兩側鐵道均爲單向行駛道),則請回答:
(1) 如果進站的車廂序列爲123,則可能得到的出站車廂序列是什麼?
(2) 如果進站的車廂序列爲123456,則能否得到435612和135426的出站序列,並請說明爲什麼不能得到或者如何得到(即寫出以 ‘S’表示進棧和以 ‘X’表示出棧的棧操作序列)。
解:(1) 123 231 321 213 132
(2) 可以得到135426的出站序列,但不能得到435612的出站序列。因爲4356出站說明12已經在棧中,1不可能先於2出棧。
3.2 簡述棧和線性表的差別。
解:線性表是具有相同特性的數據元素的一個有限序列。棧是限定僅在表尾進行插入或刪除操作的線性表。
3.3 寫出下列程序段的輸出結果(棧的元素類型SElemType爲char)。
void main()
{
Stack S;
char x,y;
InitStack(S);
x= ‘c’; y= ‘k’;
Push(S,x); Push(S, ‘a’); Push(S,y);
Pop(S,x); Push(S, ‘t’); Push(S,x);
Pop(S,x); Push(S, ‘s’);
while(!StackEmpty(S)) { Pop(S,y); printf(y); }
printf(x);
}
解:stack
3.4 簡述以下算法的功能(棧的元素類型SElemType爲int)。
(1) status algo1(Stack S)
{
int i,n,A[255];
n=0;
while(!StackEmpty(S)) { n++; Pop(S,A[n]); }
for(i=1;i<=n;i++) Push(S,A[i]);
}
(2) status algo2(Stack S,int e)
{
Stack T; int d;
InitStack(T);
while(!StackEmpty(S)){
Pop(S,d);
if(d!=e) Push(T,d);
}
while(!StackEmpty(T)){
Pop(T,d);
Push(S,d);
}
}
解:(1) 棧中的數據元素逆置 (2) 如果棧中存在元素e,將其從棧中清除
3.5 假設以S和X分別表示入棧和出棧的操作,則初態和終態均爲空棧的入棧和出棧的操作序列可以表示爲僅由S和X組成的序列。稱可以操作的序列爲合法序列(例如,SXSX爲合法序列,SXXS爲非法序列)。試給出區分給定序列爲合法序列或非法序列的一般準則,並證明:兩個不同的合法(棧操作)序列(對同一輸入序列)不可能得到相同的輸出元素(注意:在此指的是元素實體,而不是值)序列。
解:任何前n個序列中S的個數一定大於X的個數。
設兩個合法序列爲:
T1=S……X……S……
T2=S……X……X……
假定前n個操作都相同,從第n+1個操作開始,爲序列不同的起始操作點。由於前n個操作相同,故此時兩個棧(不妨爲棧A、B)的存儲情況完全相同,假設此時棧頂元素均爲a。
第n+1個操作不同,不妨T1的第n+1個操作爲S,T2的第n+1個操作爲X。T1爲入棧操作,假設將b壓棧,則T1的輸出順序一定是先b後a;而T2將a退棧,則其輸出順序一定是先a後b。由於T1的輸出爲……ba……,而T2的輸出順序爲……ab……,說明兩個不同的合法棧操作序列的輸出元素的序列一定不同。
3.6 試證明:若藉助棧由輸入序列12…n得到的輸出序列爲 (它是輸入序列的一個排列),則在輸出序列中不可能出現這樣的情形:存在着i<j<k使 < < 。
解:這個問題和3.1題比較相似。因爲輸入序列是從小到大排列的,所以若 < < ,則可以理解爲通過輸入序列 可以得到輸出序列 ,顯然通過序列123是無法得到312的,參見3.1題。所以不可能存在着i<j<k使 < < 。
3.7 按照四則運算加、減、乘、除和冪運算(↑)優先關係的慣例,並仿照教科書3.2節例3-2的格式,畫出對下列算術表達式求值時操作數棧和運算符棧的變化過程:
A-B×C/D+E↑F
解:BC=G G/D=H A-H=I E^F=J I+J=K
步驟 OPTR棧 OPND棧 輸入字符 主要操作
1 # A-B
C/D+E^F# PUSH(OPND,A)
2 # A -B
C/D+E^F# PUSH(OPTR,-)
3 #- A B
C/D+E^F# PUSH(OPND,B)
4 #- A B C/D+E^F# PUSH(OPTR,)
5 #-* A B C/D+E^F# PUSH(OPND,C)
6 #-* A B C /D+E^F# Operate(B,*,C)
7 #- A G /D+E^F# PUSH(OPTR,/)
8 #-/ A G D+E^F# PUSH(OPND,D)
9 #-/ A G D +E^F# Operate(G,/,D)
10 #- A H +E^F# Operate(A,-,H)
11 # I +E^F# PUSH(OPTR,+)
12 #+ I E^F# PUSH(OPND,E)
13 #+ I E ^F# PUSH(OPTR,^)
14 #+^ I E F# PUSH(OPND,F)
15 #+^ I E F # Operate(E,^,F)
16 #+ I J # Operate(I,+,J)
17 # K # RETURN
3.8 試推導求解n階梵塔問題至少要執行的move操作的次數。
解:
3.9 試將下列遞推過程改寫爲遞歸過程。
void ditui(int n)
{
int i;
i = n;
while(i>1)
cout<<i–;
}
解:
void ditui(int j)
{
if(j>1){
cout<<j;
ditui(j-1);
}
return;
}
3.10 試將下列遞歸過程改寫爲非遞歸過程。
void test(int &sum)
{
int x;
cin>>x;
if(x0) sum=0;
else
{
test(sum);
sum+=x;
}
cout<<sum;
}
解:
void test(int &sum)
{
Stack s;
InitStack(s);
int x;
do{
cin>>x;
Push(s,x);
}while(x>0);
while(!StackEmpty(s)){
Pop(s,x);
sum+=x;
cout<<sum<<endl;
}
DestoryStack(s);
}
3.11 簡述隊列和堆棧這兩種數據類型的相同點和差異處。
解:棧是一種運算受限的線性表,其限制是僅允許在表的一端進行插入和刪除運算。
隊列也是一種運算受限的線性表,其限制是僅允許在表的一端進行插入,而在表的另一端進行刪除。
3.12 寫出以下程序段的輸出結果(隊列中的元素類型QElemType爲char)。
void main()
{
Queue Q;
InitQueue(Q);
char x= ‘e’, y= ‘c’;
EnQueue(Q, ‘h’);
EnQueue(Q, ‘r’);
EnQueue(Q, y);
DeQueue(Q, x);
EnQueue(Q, x);
DeQueue(Q, x);
EnQueue(Q, ‘a’);
While(!QueueEmpty(Q))
{
DeQueue(Q,y);
cout<<y;
}
cout<<x;
}
解:char
3.13 簡述以下算法的功能(棧和隊列的元素類型均爲int)。
void algo3(Queue &Q)
{
Stack S;
int d;
InitStack(S);
while(!QueueEmpty(Q))
{
DeQueue(Q, d);
Push(S, d);
}
while(!StackEmpty(S))
{
Pop(S, d);
EnQueue(Q, d);
}
}
解:隊列逆置
3.14 若以1234作爲雙端隊列的輸入序列,試分別求出滿足以下條件的輸出序列:
(1) 能由輸入受限的雙端隊列得到,但不能由輸出受限的雙端隊列得到的輸出序列。
(2) 能由輸出受限的雙端隊列得到,但不能由輸入受限的雙端隊列得到的輸出序列。
(3) 既不能由輸入受限的雙端隊列得到,也不能由輸出受限的雙端隊列得到的輸出序列。
3.15 假設以順序存儲結構實現一個雙向棧,即在一維數組的存儲空間中存在着兩個棧,它們的棧底分別設在數組的兩個端點。試編寫實現這個雙向棧tws的三個操作:初始化inistack(tws)、入棧push(tws,i,x)和出棧pop(tws,i)的算法,其中i爲0或1,用以分別指示設在數組兩端的兩個棧,並討論按過程(正/誤狀態變量可設爲變參)或函數設計這些操作算法各有什麼有缺點。
解:
class DStack{
ElemType *top[2];
ElemType *p;
int stacksize;
int di;
public:
DStack(int m)
{
p=new ElemType[m];
if(!p) exit(OVERFLOW);
top[0]=p+m/2;
top[1]=top[0];
stacksize=m;
}
~DStack(){delete p;}
void Push(int i,ElemType x)
{
di=i;
if(di
0){
if(top[0]>=p) *top[0]–=x;
else cerr<<“Stack overflow!”;
}
else{
if(top[1]<p+stacksize-1) *++top[1]=x;
else cerr<<“Stack overflow!”;
}
}
ElemType Pop(int i)
{
di=i;
if(di==0){
if(top[0]<top[1]) return *++top[0];
else cerr<<“Stack empty!”;
}else{
if(top[1]>top[0]) return *top[1]–;
else cerr<<“Stack empty!”;
}
return OK;
}
};

// 鏈棧的數據結構及方法的定義
typedef struct NodeType{
ElemType data;
NodeType *next;
}NodeType,*LinkType;
typedef struct{
LinkType top;
int size;
}Stack;

void InitStack(Stack &s)
{
s.top=NULL;
s.size=0;
}

void DestroyStack(Stack &s)
{
LinkType p;
while(s.top){
p=s.top;
s.top=p->next;
delete p;
s.size–;
}
}

void ClearStack(Stack &s)
{
LinkType p;
while(s.top){
p=s.top;
s.top=p->next;
delete p;
s.size–;
}
}

int StackLength(Stack s)
{
return s.size;
}

Status StackEmpty(Stack s)
{
if(s.size==0) return TRUE;
else return FALSE;
}

Status GetTop(Stack s,ElemType &e)
{
if(!s.top) return ERROR;
else{
e=s.top->data;
return OK;
}
}

Status Push(Stack &s,ElemType e)
{
LinkType p;
p=new NodeType;
if(!p) exit(OVERFLOW);
p->next=s.top;
s.top=p;
p->data=e;
s.size++;
return OK;
}

Status Pop(Stack &s,ElemType &e)
{
LinkType p;
if(s.top){
e=s.top->data;
p=s.top;
s.top=p->next;
delete p;
s.size–;
}
return OK;
}
// 從棧頂到棧底用Visit()函數遍歷棧中每個數據元素
void StackTraverse(Stack s,Status (*Visit)(ElemType e))
{
LinkType p;
p=s.top;
while§ Visit(p->data);
}
3.16 假設如題3.1所屬火車調度站的入口處有n節硬席或軟席車廂(分別以H和S表示)等待調度,試編寫算法,輸出對這n節車廂進行調度的操作(即入棧或出棧操作)序列,以使所有的軟席車廂都被調整到硬席車廂之前。
解:
int main()
{
Stack s;
char Buffer[80];
int i=0,j=0;
InitStack(s);
cout<<“請輸入硬席(H)和軟席車廂(S)序列:”;
cin>>Buffer;
cout<<Buffer<<endl;
while(Buffer[i]){
if(Buffer[i]‘S’){
Buffer[j]=Buffer[i];
j++;
}
else Push(s,Buffer[i]);
i++;
}
while(Buffer[j]){
Pop(s,Buffer[j]);
j++;
}
cout<<Buffer<<endl;
return 0;
}
3.17 試寫一個算法,識別一次讀入的一個以@爲結束符的字符序列是否爲形如‘序列1&序列2’模式的字符序列。其中序列1和序列2中都不含字符‘&’,且序列2是序列1的逆序列。例如,‘a+b&b+a’是屬該模式的字符序列,而‘1+3&3-1’則不是。
解:
BOOL Symmetry(char a[])
{
int i=0;
Stack s;
InitStack(s);
ElemType x;
while(a[i]!=’&’ && a[i]){
Push(s,a[i]);
i++;
}
if(a[i]) return FALSE;
i++;
while(a[i]){
Pop(s,x);
if(x!=a[i]){
DestroyStack(s);
return FALSE;
}
i++;
}
return TRUE;
}
3.18 試寫一個判別表達式中開、閉括號是否配對出現的算法。
解:
BOOL BracketCorrespondency(char a[])
{
int i=0;
Stack s;
InitStack(s);
ElemType x;
while(a[i]){
switch(a[i]){
case ‘(’:
Push(s,a[i]);
break;
case ‘[’:
Push(s,a[i]);
break;
case ‘)’:
GetTop(s,x);
if(x
’(’) Pop(s,x);
else return FALSE;
break;
case ‘]’:
GetTop(s,x);
if(x==’[’) Pop(s,x);
else return FALSE;
break;
default:
break;
}
i++;
}
if(s.size!=0) return FALSE;
return TRUE;
}
3.20 假設以二維數組g(1…m, 1…n)表示一個圖像區域,g[i,j]表示該區域中點(i,j)所具顏色,其值爲從0到k的整數。編寫算法置換點(i0,j0)所在區域的顏色。約定和(i0,j0)同色的上、下、左、右的鄰接點爲同色區域的點。
解:
#include <iostream.h>
#include <stdlib.h>

typedef struct{
int x;
int y;
}PosType;
typedef struct{
int Color;
int Visited;
PosType seat;
}ElemType;

#include “d:\VC99\Stack.h”

#define M 8
#define N 8

ElemType g[M][N];

void CreateGDS(ElemType g[M][N]);
void ShowGraphArray(ElemType g[M][N]);
void RegionFilling(ElemType g[M][N],PosType CurPos,int NewColor);

int main()
{
CreateGDS(g);
ShowGraphArray(g);

PosType StartPos;
StartPos.x=5;
StartPos.y=5;
int FillColor=6;
RegionFilling(g,StartPos,FillColor);
cout<<endl;
ShowGraphArray(g);
return 0;

}

void RegionFilling(ElemType g[M][N],PosType CurPos,int FillColor)
{
Stack s;
InitStack(s);
ElemType e;
int OldColor=g[CurPos.x][CurPos.y].Color;

Push(s,g[CurPos.x][CurPos.y]);
while(!StackEmpty(s)){
	Pop(s,e);
	CurPos=e.seat;
	g[CurPos.x][CurPos.y].Color=FillColor;
	g[CurPos.x][CurPos.y].Visited=1;

	if(CurPos.x<M && 
			!g[CurPos.x+1][CurPos.y].Visited &&
			g[CurPos.x+1][CurPos.y].Color==OldColor
	)
		Push(s,g[CurPos.x+1][CurPos.y]);
	if(CurPos.x>0 &&
		!g[CurPos.x-1][CurPos.y].Visited &&
		g[CurPos.x-1][CurPos.y].Color==OldColor
	)
		Push(s,g[CurPos.x-1][CurPos.y]);
	if(CurPos.y<N &&
		!g[CurPos.x][CurPos.y+1].Visited &&
		g[CurPos.x][CurPos.y+1].Color==OldColor
	)
		Push(s,g[CurPos.x][CurPos.y+1]);
	if(CurPos.y>0 &&
		!g[CurPos.x][CurPos.y-1].Visited &&
		g[CurPos.x][CurPos.y-1].Color==OldColor
	)
		Push(s,g[CurPos.x][CurPos.y-1]);
}

}

void CreateGDS(ElemType g[M][N])
{
int i,j;
for(i=0;i<M;i++)
for(j=0;j<N;j++){
g[i][j].seat.x=i;
g[i][j].seat.y=j;
g[i][j].Visited=0;
g[i][j].Color=0;
}
for(i=2;i<5;i++)
for(j=2;j<4;j++)
g[i][j].Color=3;
for(i=5;i<M-1;i++)
for(j=3;j<6;j++)
g[i][j].Color=3;
}

void ShowGraphArray(ElemType g[M][N])
{
int i,j;
for(i=0;i<M;i++){
for(j=0;j<N;j++)
cout<<g[i][j].Color;
cout<<endl;
}
}
3.21 假設表達式有單字母變量和雙目四則運算符構成。試寫一個算法,將一個通常書寫形式且書寫正確的表達式轉換爲逆波蘭表達式。
解:
// 輸入的表達式串必須爲#…#格式
void InversePolandExpression(char Buffer[])
{
Stack s;
InitStack(s);
int i=0,j=0;
ElemType e;

Push(s,Buffer[i]);
i++;
while(Buffer[i]!='#'){
	if(!IsOperator(Buffer[i])){	// 是操作數
		Buffer[j]=Buffer[i];
		i++;
		j++;
	}
	else{	// 是操作符
		GetTop(s,e);
		if(Prior(e,Buffer[i])){// 當棧頂優先權高於當前序列時,退棧
			Pop(s,e);
			Buffer[j]=e;
			j++;
		}
		else{
			Push(s,Buffer[i]);
			i++;
		}
	}
}
while(!StackEmpty(s)){
	Pop(s,e);
	Buffer[j]=e;
	j++;
}

}

Status IsOpertor(char c)
{
char p="#±/";
while(*p){
if(*p==c)
return TRUE;
p++;
}
return FALSE;
}

Status Prior(char c1,char c2)
{
char ch[]="#±*/";
int i=0,j=0;
while(ch[i] && ch[i]!=c1) i++;
if(i2) i–; // 加和減可認爲是同級別的運算符
if(i
4) i–; // 乘和除可認爲是同級別的運算符
while(ch[j] && ch[j]!=c2) j++;
if(j2) j–;
if(j
4) j–;
if(i>=j) return TRUE;
else return FALSE;
}
3.22 如題3.21的假設條件,試寫一個算法,對以逆波蘭式表示的表達式求值。
解:
char CalVal_InverPoland(char Buffer[])
{
Stack Opnd;
InitStack(Opnd);
int i=0;
char c;
ElemType e1,e2;

while(Buffer[i]!='#'){
	if(!IsOperator(Buffer[i])){
		Push(Opnd,Buffer[i]);
	}
	else{
		Pop(Opnd,e2);
		Pop(Opnd,e1);
		c=Cal(e1,Buffer[i],e2);
		Push(Opnd,c);
	}
	i++;
}
return c;

}

char Cal(char c1,char op,char c2)
{
int x,x1,x2;
char ch[10];
ch[0]=c1;
ch[1]=’\0’;
x1=atoi(ch);

ch[0]=c2;
ch[1]='\0';
x2=atoi(ch);

switch(op){
case '+':
	x=x1+x2;
	break;
case '-':
	x=x1-x2;
	break;
case '*':
	x=x1*x2;
	break;
case '/':
	x=x1/x2;
	break;
default:
	break;
}
itoa(x,ch,10);
return ch[0];

}
3.23 如題3.21的假設條件,試寫一個算法,判斷給定的非空後綴表達式是否爲正確的逆波蘭表達式,如果是,則將它轉化爲波蘭式。
解:
#include <iostream.h>
#include <stdlib.h>
#include <string.h>
#include “d:\VC99\DSConstant.h”

typedef char ARRAY[30];
typedef ARRAY ElemType;
typedef struct NodeType{
ElemType data;
NodeType *next;
}NodeType,*LinkType;
typedef struct{
LinkType top;
int size;
}Stack;

void InitStack(Stack &s);
Status Push(Stack &s,ElemType e);
Status Pop(Stack &s,ElemType e);
Status IsOperator(char c);
Status StackEmpty(Stack s);

Status InvToFroPoland(char a[]);

int main()
{
char a[30];
cout<<“請輸入逆波蘭算術表達式字符序列:”;
cin>>a;
if(InvToFroPoland(a)) cout<<a<<endl;
else cout<<“輸入逆波蘭算術表達式字符序列錯誤!”;
return 0;
}

Status InvToFroPoland(char a[])
{
Stack s;
InitStack(s);
int i=0;
ElemType ch;
ElemType c1;
ElemType c2;

while(a[i]!='#'){
	if(!IsOperator(a[i])){
		if(a[i]>='0' && a[i]<='9'){
			ch[0]=a[i];	ch[1]='\0';
			Push(s,ch);
		}
		else return FALSE;
	}
	else{
		ch[0]=a[i];
		ch[1]='\0';
		if(!StackEmpty(s)){
			Pop(s,c2);
			if(!StackEmpty(s)){
				Pop(s,c1);
				strcat(ch,c1);
				strcat(ch,c2);
				Push(s,ch);
			}
			else return FALSE;
		}
		else return FALSE;
	}
	i++;
}
if(!StackEmpty(s)){
	Pop(s,c1);
	strcpy(a,c1);
}
else return FALSE;
if(!StackEmpty(s)) return FALSE;
return OK;

}
void InitStack(Stack &s)
{
s.top=NULL;
s.size=0;
}

Status Push(Stack &s,ElemType e)
{
LinkType p;
p=new NodeType;
if(!p) exit(OVERFLOW);
p->next=s.top;
s.top=p;
strcpy(p->data,e);
s.size++;
return OK;
}

Status Pop(Stack &s,ElemType e)
{
LinkType p;
if(s.top){
strcpy(e,s.top->data);
p=s.top;
s.top=p->next;
delete p;
s.size–;
}
return OK;
}

Status StackEmpty(Stack s)
{
if(s.size==0) return TRUE;
else return FALSE;
}

Status IsOperator(char c)
{
char p="#±/";
while(*p){
if(*p==c)
return TRUE;
p++;
}
return FALSE;
}
3.24 試編寫如下定義的遞歸函數的遞歸算法,並根據算法畫出求g(5,2)時棧的變化過程。

解:
int g(int m,int n);
int main()
{
int m,n;
cout<<“請輸入m和n的值:”;
cin>>m>>n;
if(n>=0) cout<<g(m,n)<<endl;
else cout<<“No Solution!”;
return 0;
}
int g(int m,int n)
{
if(m>0)
return(g(m-1,2*n)+n);
else return 0;
}
假設主函數的返回地址爲0,遞歸函數3條語句的地址分別爲1、2、3。
3 0 64
3 1 32
3 2 16
3 3 8
3 4 4
0 5 2
3.25 試寫出求遞歸函數F(n)的遞歸算法,並消除遞歸:

解:
#include <iostream.h>
#define N 20
int main()
{
int i;
int a[N];
int n;
cout<<“請輸入n:”;
cin>>n;
for(i=0;i<n+1;i++){
if(i<1) a[i]=1;
else a[i]=i*a[i/2];
}
cout<<a[n]<<endl;
return 0;
}
3.26 求解平方根 的迭代函數定義如下:

其中,p是A的近似平方根,e是結果允許誤差。試寫出相應的遞歸算法,並消除遞歸。
解:
#include <iostream.h>
double Sqrt(double A,double p,double e);
int main()
{
double A,p,e;
cout<<“請輸入A p e:”;
cin>>A>>p>>e;
cout<<Sqrt(A,p,e)<<endl;
return 0;
}
double Sqrt(double A,double p,double e)
{
if((pp-A)>-e && (pp-A)<e)
return p;
else
return Sqrt(A,(p+A/p)/2,e);
}
3.27 已知Ackerman函數的定義如下:

(1) 寫出遞歸算法;
(2) 寫出非遞歸算法;
(3) 根據非遞歸算法,畫出求akm(2,1)時棧的變化過程。

解:
unsigned int akm(unsigned int m,unsigned int n)
{
unsigned int g;
if(m0)
return n+1;
else
if(n
0) return akm(m-1,1);
else{
g=akm(m,n-1);
return akm(m-1,g);
}
}
非遞歸算法:
int akm1(int m,int n)
{
Stack s;
InitStack(s);
ElemType e,e1,d;
e.mval=m; e.nval=n;
Push(s,e);
do{
while(e.mval){
while(e.nval){
e.nval–;
Push(s,e);
}
e.mval–; e.nval=1;
}
if(StackLength(s)>1){
e1.nval=e.nval;
Pop(s,e);
e.mval–;
e.nval=e1.nval+1;
}
}while(StackLength(s)!=1||e.mval!=0);
return e.nval+1;
}

0,akm(2,1) 0 2 1 g=akm(2,0)
1,akm(2,0) 6 2 0 akm=akm(m-1,1)=akm(1,1)
2,akm(1,1) 4 1 1 g=akm(m,n-1)=akm(1,0)
3,akm(1,0) 6 1 0 akm=akm(m-1,1)=akm(0,1)
4,akm(0,1) 4 0 1 akm=n+1=2 退棧

0,akm(2,1) 0 2 1 g=akm(2,0)
1,akm(2,0) 6 2 0 akm=akm(m-1,1)=akm(1,1)
2,akm(1,1) 4 1 1 g=akm(m,n-1)=akm(1,0)
3,akm(1,0) 6 1 0 akm=akm(m-1,1)=akm(0,1)=2 退棧

0,akm(2,1) 0 2 1 g=akm(2,0)
1,akm(2,0) 6 2 0 akm=akm(m-1,1)=akm(1,1)
2,akm(1,1) 4 1 1 g=akm(m,n-1)=akm(1,0)=2; akm=akm(m-1,g)=akm(0,2);
3,akm(0,2) 7 0 2 akm=n+1=3 退棧

0,akm(2,1) 0 2 1 g=akm(2,0)
1,akm(2,0) 6 2 0 akm=akm(m-1,1)=akm(1,1)
2,akm(1,1) 4 1 1 g=akm(m,n-1)=akm(1,0)=2; akm=akm(m-1,g)=akm(0,2)=3; 退棧

0,akm(2,1) 0 2 1 g=akm(2,0)
1,akm(2,0) 6 2 0 akm=akm(m-1,1)=akm(1,1)=3 退棧

0,akm(2,1) 0 2 1 g=akm(2,0)=3; akm=akm(1,3)
1,akm(1,3) 6 1 3 g=akm(1,2); akm(m-1,g)
2,akm(1,2) 6 1 2 g=akm(1,1); akm(m-1,g)
3,akm(1,1) 6 1 1 g=akm(1,0); akm(m-1,g)
4,akm(1,0) 6 1 0 akm=akm(0,1)
5,akm(0,1) 4 0 1 akm(0,1)=2退棧

0,akm(2,1) 0 2 1 g=akm(2,0)=3; akm=akm(1,3)
1,akm(1,3) 6 1 3 g=akm(1,2); akm(m-1,g)
2,akm(1,2) 6 1 2 g=akm(1,1); akm(m-1,g)
3,akm(1,1) 6 1 1 g=akm(1,0); akm(m-1,g)
4,akm(1,0) 6 1 0 akm=akm(0,1)=2退棧

0,akm(2,1) 0 2 1 g=akm(2,0)=3; akm=akm(1,3)
1,akm(1,3) 6 1 3 g=akm(1,2); akm(m-1,g)
2,akm(1,2) 6 1 2 g=akm(1,1); akm(m-1,g)
3,akm(1,1) 6 1 1 g=akm(1,0)=2; akm(m-1,g)=akm(0,2)
4,akm(0,2) 7 0 2 akm=n+1=3 退棧

0,akm(2,1) 0 2 1 g=akm(2,0)=3; akm=akm(1,3)
1,akm(1,3) 6 1 3 g=akm(1,2); akm(m-1,g)
2,akm(1,2) 6 1 2 g=akm(1,1); akm(m-1,g)
3,akm(1,1) 6 1 1 g=akm(1,0)=2; akm(m-1,g)=akm(0,2)=3退棧

0,akm(2,1) 0 2 1 g=akm(2,0)=3; akm=akm(1,3)
1,akm(1,3) 6 1 3 g=akm(1,2); akm(m-1,g)
2,akm(1,2) 6 1 2 g=akm(1,1)=3; akm(m-1,g)=akm(0,3)
3,akm(0,3) 7 0 3 akm=n+1=4 退棧

0,akm(2,1) 0 2 1 g=akm(2,0)=3; akm=akm(1,3)
1,akm(1,3) 6 1 3 g=akm(1,2); akm(m-1,g)
2,akm(1,2) 6 1 2 g=akm(1,1)=3; akm(m-1,g)=akm(0,3)=4 退棧

0,akm(2,1) 0 2 1 g=akm(2,0)=3; akm=akm(1,3)
1,akm(1,3) 6 1 3 g=akm(1,2)=4; akm(m-1,g)=akm(0,4)
2,akm(0,4) 7 0 4 akm=n+1=5 退棧

0,akm(2,1) 0 2 1 g=akm(2,0)=3; akm=akm(1,3)
1,akm(1,3) 6 1 3 g=akm(1,2)=4; akm(m-1,g)=akm(0,4)=5退棧

0,akm(2,1) 0 2 1 g=akm(2,0)=3; akm=akm(1,3)=5退棧

akm(2,1)=5;
3.28 假設以帶頭結點的循環鏈表表示隊列,並且只設一個指針指向隊尾元素結點(注意不設頭指針),試編寫相應的隊列初始化、入隊列何處隊列的算法。
解:
typedef int ElemType;
typedef struct NodeType{
ElemType data;
NodeType *next;
}QNode,*QPtr;
typedef struct{
QPtr rear;
int size;
}Queue;
Status InitQueue(Queue& q)
{
q.rear=NULL;
q.size=0;
return OK;
}
Status EnQueue(Queue& q,ElemType e)
{
QPtr p;
p=new QNode;
if(!p) return FALSE;
p->data=e;
if(!q.rear){
q.rear=p;
p->next=q.rear;
}
else{
p->next=q.rear->next;
q.rear->next=p;
q.rear=p;
}
q.size++;
return OK;
}
Status DeQueue(Queue& q,ElemType& e)
{
QPtr p;
if(q.size0)return FALSE;
if(q.size
1){
p=q.rear;
e=p->data;
q.rear=NULL;
delete p;
}
else{
p=q.rear->next;
e=p->data;
q.rear->next=p->next;
delete p;
}
q.size–;
return OK;
}
3.29 如果希望循環隊列中的元素都能得到利用,則需設置一個標誌域tag,並以tag的值爲0和1來區分,尾指針和頭指針值相同時的隊列狀態是“空”還是“滿”。試編寫與此結構相應的入隊列和出隊列的算法,並從時間和空間角度討論設標誌和不設標誌這兩種方法的使用範圍(如當循環隊列容量較小而隊列中每個元素佔的空間較多時,哪一種方法較好)。
解:
#define MaxQSize 4
typedef int ElemType;
typedef struct{
ElemType base;
int front;
int rear;
Status tag;
}Queue;
Status InitQueue(Queue& q)
{
q.base=new ElemType[MaxQSize];
if(!q.base) return FALSE;
q.front=0;
q.rear=0;
q.tag=0;
return OK;
}
Status EnQueue(Queue& q,ElemType e)
{
if(q.frontq.rear&&q.tag) return FALSE;
else{
q.base[q.rear]=e;
q.rear=(q.rear+1)%MaxQSize;
if(q.rear
q.front)q.tag=1;
}
return OK;
}
Status DeQueue(Queue& q,ElemType& e)
{
if(q.frontq.rear&&!q.tag)return FALSE;
else{
e=q.base[q.front];
q.front=(q.front+1)%MaxQSize;
q.tag=0;
}
return OK;
}
設標誌節省存儲空間,但運行時間較長。不設標誌則正好相反。
3.30 假設將循環隊列定義爲:以域變量rear和length分別指示循環隊列中隊尾元素的位置和內含元素的個數。試給出此循環隊列的隊滿條件,並寫出相應的入隊列和出隊列的算法(在出隊列的算法中要返回隊頭元素)。
解:
#define MaxQSize 4
typedef int ElemType;
typedef struct{
ElemType *base;
int rear;
int length;
}Queue;
Status InitQueue(Queue& q)
{
q.base=new ElemType[MaxQSize];
if(!q.base) return FALSE;
q.rear=0;
q.length=0;
return OK;
}
Status EnQueue(Queue& q,ElemType e)
{
if((q.rear+1)%MaxQSize
(q.rear+MaxQSize-q.length)%MaxQSize)
return FALSE;
else{
q.base[q.rear]=e;
q.rear=(q.rear+1)%MaxQSize;
q.length++;
}
return OK;
}
Status DeQueue(Queue& q,ElemType& e)
{
if((q.rear+MaxQSize-q.length)%MaxQSize==q.rear)
return FALSE;
else{
e=q.base[(q.rear+MaxQSize-q.length)%MaxQSize];
q.length–;
}
return OK;
}
3.31 假設稱正讀和反讀都相同的字符序列爲“迴文”,例如,‘abba’和‘abcba’是迴文,‘abcde’和‘ababab’則不是迴文。試寫一個算法判別讀入的一個以‘@’爲結束符的字符序列是否是“迴文”。
解:
Status SymmetryString(char
p)
{
Queue q;
if(!InitQueue(q)) return 0;
Stack s;
InitStack(s);
ElemType e1,e2;
while(*p){
Push(s,*p);
EnQueue(q,*p);
p++;
}
while(!StackEmpty(s)){
Pop(s,e1);
DeQueue(q,e2);
if(e1!=e2) return FALSE;
}
return OK;
}
3.32 試利用循環隊列編寫求k階菲波那契序列中前n+1項的算法,要求滿足: 而 ,其中max爲某個約定的常數。(注意:本題所用循環隊列的容量僅爲k,則在算法執行結束時,留在循環隊列中的元素應是所求k階菲波那契序列中的最後k項)
解:
int Fibonacci(int k,int n)
{
if(k<1) exit(OVERFLOW);
Queue q;
InitQueue(q,k);
ElemType x,e;
int i=0;
while(i<=n){
if(i<k-1){
if(!EnQueue(q,0)) exit(OVERFLOW);
}
if(ik-1){
if(!EnQueue(q,1)) exit(OVERFLOW);
}
if(i>=k){
// 隊列求和
x=sum(q);
DeQueue(q,e);
EnQueue(q,x);
}
i++;
}
return q.base[(q.rear+q.MaxSize-1)%q.MaxSize];
}
3.33 在順序存儲結構上實現輸出受限的雙端循環隊列的入列和出列(只允許隊頭出列)算法。設每個元素表示一個待處理的作業,元素值表示作業的預計時間。入隊列採取簡化的短作業優先原則,若一個新提交的作業的預計執行時間小於隊頭和隊尾作業的平均時間,則插入在隊頭,否則插入在隊尾。
解:
// Filename:Queue.h
typedef struct{
ElemType *base;
int front;
int rear;
Status tag;
int MaxSize;
}DQueue;
Status InitDQueue(DQueue& q,int size)
{
q.MaxSize=size;
q.base=new ElemType[q.MaxSize];
if(!q.base) return FALSE;
q.front=0;
q.rear=0;
q.tag=0;
return OK;
}
Status EnDQueue(DQueue& q,ElemType e)
{
if(q.front
q.rear&&q.tag) return FALSE;
if(q.frontq.rear&&!q.tag){ // 空隊列
q.base[q.rear]=e;
q.rear=(q.rear+1)%q.MaxSize;
if(q.rear
q.front)q.tag=1;
}
else{ // 非空非滿
if(e<(q.base[q.front]+q.base[(q.rear+q.MaxSize-1)%q.MaxSize])/2){
// 從隊頭入隊
q.front=(q.front+q.MaxSize-1)%q.MaxSize;
q.base[q.front]=e;
if(q.rearq.front)q.tag=1;
}
else{ // 從隊尾入隊
q.base[q.rear]=e;
q.rear=(q.rear+1)%q.MaxSize;
if(q.rear
q.front)q.tag=1;
}
}
return OK;
}
Status DeDQueue(DQueue& q,ElemType& e)
{
if(q.front==q.rear&&!q.tag)return FALSE;
else{ // 非空隊列
e=q.base[q.front];
q.front=(q.front+1)%q.MaxSize;
q.tag=0;
}
return OK;
}
// Filename:XT333.cpp 主程序文件
#include <iostream.h>
#include <stdlib.h>
typedef int ElemType;
#include “D:\VC99\Queue.h”

int main()
{
int t1,t2,t3,t4;
ElemType e;
cout<<"請輸入作業a1、a2、a3、a4的執行時間: ";
cin>>t1>>t2>>t3>>t4;
DQueue dq;
InitDQueue(dq,5);
EnDQueue(dq,t1);
EnDQueue(dq,t2);
EnDQueue(dq,t3);
EnDQueue(dq,t4);
while(dq.front!=dq.rear||dq.tag){
DeDQueue(dq,e);
cout<<e<<endl;
}
return 0;
}
3.34 假設在如教科書3.4.1節中圖3.9所示的鐵道轉軌網的輸入端有n節車廂:硬座、硬臥和軟臥(分別以P,H和S表示)等待調度,要求這三種車廂在輸出端鐵道上的排列次序爲:硬座在前,軟臥在中,硬臥在後。試利用輸出受限的雙端隊列對這n節車廂進行調度,編寫算法輸出調度的操作序列:分別以字符‘E’和‘D’表示對雙端隊列的頭端進行入隊列和出隊列的操作;以字符A表示對雙端隊列的尾端進行入隊列的操作。
解:
int main()
{
ElemType e;
DQueue dq;
InitDQueue(dq,20);
char ch[20];
cout<<“請輸入待調度的車廂字符序列(僅限PHS):”;
cin>>ch;
int i=0;
while(ch[i]){
if(ch[i]‘P’) cout<<ch[i];
if(ch[i]
‘S’) EnDQueue(dq,ch[i],0);// 從隊頭入隊
if(ch[i]==‘H’) EnDQueue(dq,ch[i],1);// 從隊尾入隊
i++;
}
while(dq.front!=dq.rear||dq.tag){
DeDQueue(dq,e);
cout<<e;
}
cout<<endl;
return 0;
}

第4章 串
4.1 解:空格串是指一個或多個空格字符(ASCII碼爲20H)組成的串,而空串中沒有任何字符。
4.2 解:串賦值(StrAssign)、串比較(StrCompare)、求串長(StrLength)、串連接(Concat)、求子串(SubString)這五種基本操作構成串類型的最小操作子集。
4.6 解:
s1=SubString(s,3,1)
s2=SubString(s,6,1)
Replace(s,s1,s2)
Concat(s3,s,s1)
Concat(t,SubString(s3,1,5),SubString(s3,7,2))
算法設計題:
// Filename: String.h
#include <stdlib.h>
#include <string.h>
#define MaxSize 128
class String{
char ch;
int curlen;
public:
String(const String& ob);
String(const char
init);
String();
~String();
void StrAssign(String t);
int StrCompare(String t);
int StrLength();
void Concat(String t);
String SubString(int start,int len);
void show();
};
String::String(const String& ob)
{
ch=new char[MaxSize+1];
if(!ch) exit(1);
curlen=ob.curlen;
strcpy(ch,ob.ch);
}
String::String(const char* init)
{
ch=new char[MaxSize+1];
if(!ch) exit(1);
curlen=strlen(init);
strcpy(ch,init);
}
String::String()
{
ch=new char[MaxSize+1];
if(!ch) exit(1);
curlen=0;
ch[0]=’\0’;
}
String::~String()
{
delete ch;
curlen=0;
}
void String::StrAssign(String t)
{
strcpy(ch,t.ch);
curlen=t.curlen;
}
int String::StrCompare(String t)
{
return strcmp(ch,t.ch);
}
int String::StrLength()
{
return curlen;
}
void String::Concat(String t)
{
strcat(ch,t.ch);
curlen=curlen+t.curlen;
}
String String::SubString(int start,int len)
{
String temp;
int i,j;
if(start>=0 && start+len<=curlen && len>0){
temp.curlen=len;
for(i=0,j=start;i<len;i++,j++)
temp.ch[i]=ch[j];
temp.ch[len]=’\0’;
}
return temp;
}
void String::show()
{
cout<<ch<<endl;
}
4.10 解:
void StrReverse(String& s)
{
String t;
int i,j;
j=s.StrLength();
for(i=j-1;i>=0;i–)
t.Concat(s.SubString(i,1));
s.StrAssign(t);
}
4.11 解:

第5章 數組與廣義表
5.1 解:
(1) 6×8×6=288 Byte
(2) LOC(5,7)=1000+(5×8+7)×6=1282
(3) LOC(1,4)=1000+(1×8+4)×6=1072
(4) LOC(4,7)=1000+(7×6+4)×6=1276
5.2 解:
(1) LOC(0,0,0,0)=100
(2) LOC(1,1,1,1)=100+(1×3×5×8 + 1×5×8 + 1×8 + 1)×4=776
(3) LOC(3,1,2,5)=100+(3×3×5×8 + 1×5×8 + 2×8 + 5)×4=1784
(4) LOC(8,2,4,7)=100+(8×3×5×8 + 2×5×8 + 4×8 + 7)×4=4416
5.3 解:
(0,0,0,0) (1,0,0,0) (0,1,0,0) (1,1,0,0) (0,0,1,0) (1,0,1,0) (0,1,1,0) (1,1,1,0)
(0,0,2,0) (1,0,2,0) (0,1,2,0) (1,1,2,0) (0,0,0,1) (1,0,0,1) (0,1,0,1) (1,1,0,1)
(0,0,1,1) (1,0,1,1) (0,1,1,1) (1,1,1,1) (0,0,2,1) (1,0,2,1) (0,1,2,1) (1,1,2,1)
(0,0,0,2) (1,0,0,2) (0,1,0,2) (1,1,0,2) (0,0,1,2) (1,0,1,2) (0,1,1,2) (1,1,1,2)
(0,0,2,2) (1,0,2,2) (0,1,2,2) (1,1,2,2)
5.4 解:
其中,a=Max(i,j),b=Min(i,j)
5.5 解: ( , , )

5.6 解:u=i-j+1 v=j-1
5.7 解:(1) k=2(i-1)+j-1 ( )
(2) i=(k+1) DIV 3 + 1 ( )
j=k+1-2(k DIV 3)
5.8 解:i爲奇數時,k=i+j-2
j爲偶數時,k=i+j-1
k=2(i DIV 2)+j-1
5.9 解:設稀疏矩陣爲n行n列,其中的非零元爲m個,m遠小於 。從時間上來說,採用二維數組存儲稀疏矩陣需要 -1次加法運算,而用三元組只需m-1次加法運算。從空間上來說,用二維數組需要 個基本存儲單元,而三元組需要m個基本存儲單元外加2m個整型存儲單元。由於 遠遠大於m,故實際存儲空間也較大。
5.10 解:
(1) GetHead【(p,h,w)】= p
(2) GetTail【(b,k,p,h)】= (k,p,h)
(3) GetHead【((a,b),(c,d))】= (a,b)
(4) GetTail【((a,b),(c,d))】= ((c,d))
(5) GetHead【GetTail【((a,b),(c,d))】】= GetHead【((c,d))】= (c,d)
(6) GetTail【GetHead【((a,b),(c,d))】】= GetTail【(a,b)】= (b)
(7) GetHead【GetTail【GetHead【((a,b),(c,d))】】】= GetHead【(b)】= b
(8) GetTail【GetHead【GetTail【((a,b),(c,d))】】】= GetTail【(c,d)】= (d)
5.11 解:
(1) GetHead【GetTail【GetTail【L1】】】
(2) GetHead【GetHead【GetTail【L2】】】
(3) GetHead【GetHead【GetTail【GetTail【GetHead【L3】】】】】
(4) GetHead【GetHead【GetHead【GetTail【GetTail【L4】】】】】
(5) GetHead【GetHead【GetTail【GetTail【L5】】】】
(6) GetHead【GetTail【GetHead【L6】】】
(7) GetHead【GetHead【GetTail【GetHead【GetTail【L7】】】】】
5.12 解:

5.13 解:
(1) List=((x,(y)),(((())),(()),(z)))
(2) List=(((a,b,()),()),(a,(b)),())
5.14 解: (n>=1)
ElemType s(int i)
{
if(i>1)
return s(i-1)+a1+(i-1)*d;
else
return a1;
}
5.16 解:
5.17 解:
int Max(SqList &L,int k)
{
if(k<L.length-1)
if(L.elem[k]<Max(L,k+1))
return Max(L,k+1);
else
return L.elem[k];
else
return L.elem[k];
}
int Min(SqList &L,int k)
{
if(k<L.length-1)
if(L.elem[k]>Min(L,k+1))
return Min(L,k+1);
else
return L.elem[k];
else
return L.elem[k];
}
int Sum(SqList &L,int k)
{
if(k0)
return L.elem[0];
else
return L.elem[k]+Sum(a,k-1);
}
int Product(SqList &L,int k)
{
if(k
0)
return L.elem[0];
else
return L.elem[k]Sum(a,k-1);
}
double Avg(SqList &L,int k)
{
if(k==0)
return L.elem[0];
else
return (Avg(a,k-1)k+L.elem[k])/(k+1);
}
5.18 解:算法的基本思想是將數組分成k組,將第一組與第二組進行兩兩交換,再將第一組與第三組進行兩兩交換,…,總共需進行n-k次交換。注意最後一組可能出現不足k個元素的情況,此時最後一組爲剩餘元素加第一組的前幾個元素共k個構成最後一組。
void RRMove(ElemType A[],int k,int n)
{
ElemType e;
int i=0,j,p;
while(i<n-k){
p=i/k+1;
for(j=0;j<k;j++){
e=A[j];
A[j]=A[(p
k+j)%n];
A[(p
k+j)%n]=e;
i++;
}
}
}
5.19 解:
#include <iostream.h>
#define RS 4
#define CS 4

typedef int ElemType;
typedef struct{
ElemType e;
int i,j;
int Flags;
}NodeType;

void Initialize(NodeType a[RS][CS],ElemType A[RS][CS]);
void SaddlePoint(NodeType a[RS][CS]);
ElemType RowMin(NodeType a[RS][CS],int k);
ElemType ColMax(NodeType a[RS][CS],int k);
void Show(NodeType a[RS][CS]);

int main()
{
ElemType A[RS][CS]={
{2,1,3,4},
{1,3,1,2},
{2,7,1,3},
{3,2,4,1}
};
NodeType a[RS][CS];
Initialize(a,A);
SaddlePoint(a);
Show(a);
return 0;
}

void Initialize(NodeType a[RS][CS],ElemType A[RS][CS])
{
int i,j;
for(i=0;i<RS;i++){
for(j=0;j<CS;j++){
a[i][j].e=A[i][j];
a[i][j].i=i;
a[i][j].j=j;
a[i][j].Flags=0;
}
}
}

void SaddlePoint(NodeType a[RS][CS])
{
int i,j;
ElemType x,y;
for(i=0;i<RS;i++){
x=RowMin(a,i);
for(j=0;j<CS;j++){
y=ColMax(a,j);
if(a[i][j].ex&&a[i][j].ey)
a[i][j].Flags=1;
}
}
}

ElemType RowMin(NodeType a[RS][CS],int k)
{
ElemType x;
x=a[k][0].e;
int i;
for(i=1;i<CS;i++)
if(x>a[k][i].e){
x=a[k][i].e;
}
return x;
}

ElemType ColMax(NodeType a[RS][CS],int k)
{
ElemType x;
x=a[0][k].e;
int i;
for(i=1;i<RS;i++)
if(x<a[i][k].e){
x=a[i][k].e;
}
return x;
}

void Show(NodeType a[RS][CS])
{
for(int i=0;i<RS;i++)
for(int j=0;j<CS;j++)
if(a[i][j].Flags)
cout<<i<<" “<<j<<” is a saddle point"<<endl;
}
5.21 解:
typedef int ElemType;
class Triple{
public:
int row;
int col;
ElemType e;

Triple(){}
virtual ~Triple(){}
BOOL operator<(Triple b);
BOOL operator==(Triple b);

};

BOOL Triple::operator<(Triple b)
{
if(row<b.row) return TRUE;
if(rowb.row&&col<b.col) return TRUE;
return FALSE;
}
BOOL Triple::operator
(Triple b)
{
if(rowb.row && colb.col)
return TRUE;
else
return FALSE;
}

class CSparseMat
{
public:
CSparseMat(){}
virtual ~CSparseMat(){}
CSparseMat(int r,int c,int n);
CSparseMat operator+(CSparseMat B);
void ShowSparse(CDC* pDC);

Triple *m_pt;	// 指向非零元的指針
int m_nCol;		// 矩陣列數
int m_nRow;		// 矩陣行數
int m_nTrs;		// 非零元個數

};

CSparseMat::CSparseMat(int r, int c, int n)
{
m_nRow=r;
m_nCol=c;
m_nTrs=n;
m_pt=new Triple[m_nTrs];

// 輸入矩陣的所有三元組
int i;
for(i=0;i<m_nTrs;i++){
	CInputDlg dlg1;
	if(dlg1.DoModal()==IDOK){
		m_pt[i].row=dlg1.m_nRow;
		m_pt[i].col=dlg1.m_nCol;
		m_pt[i].e=dlg1.m_nElem;
	}
}

}

void CSparseMat::ShowSparse(CDC pDC)
{
char str[10];
int k=0;
for(int i=0;i<m_nRow;i++){
for(int j=0;j<m_nCol;j++){
if(m_pt[k].rowi && m_pt[k].colj){
itoa(m_pt[k].e,str,10);
k++;
}
else itoa(0,str,10);
pDC->TextOut(0+j
20,0+i*20,str,strlen(str));
}
}
}

// 矩陣相加的運算符重載函數
CSparseMat CSparseMat::operator+(CSparseMat B)
{
CSparseMat temp(m_nRow,m_nCol,0);
if(m_nRow!=B.m_nRow || m_nCol!=B.m_nCol)
return temp;

temp.m_pt=new Triple[m_nTrs+B.m_nTrs];
if(!temp.m_pt) return temp;
temp.m_nTrs=m_nTrs+B.m_nTrs;

int i=0;
int j=0;
int k=0;
while(i<m_nTrs && j<B.m_nTrs){
	if(m_pt[i]<B.m_pt[j]){
		temp.m_pt[k].row=m_pt[i].row;
		temp.m_pt[k].col=m_pt[i].col;
		temp.m_pt[k].e=m_pt[i].e;
		i++;
	}
	else{
		if(m_pt[i]==B.m_pt[j]){
			temp.m_pt[k].row=m_pt[i].row;
			temp.m_pt[k].col=m_pt[i].col;
			temp.m_pt[k].e=m_pt[i].e+B.m_pt[j].e;
			i++;	j++;
		}
		else{
			temp.m_pt[k].row=B.m_pt[j].row;
			temp.m_pt[k].col=B.m_pt[j].col;
			temp.m_pt[k].e=B.m_pt[j].e;
			j++;
		}
	}
	k++;
}
while(i<m_nTrs){
	temp.m_pt[k].row=m_pt[i].row;
	temp.m_pt[k].col=m_pt[i].col;
	temp.m_pt[k].e=m_pt[i].e;
	i++;
	k++;
}
while(j<B.m_nTrs){
	temp.m_pt[k].row=B.m_pt[j].row;
	temp.m_pt[k].col=B.m_pt[j].col;
	temp.m_pt[k].e=B.m_pt[j].e;
	j++;
	k++;
}
temp.m_nTrs=k;
return temp;

}
5.23 解:
#include<iostream.h>
#include<stdlib.h>
#define Max 128

typedef int ElemType;
typedef struct{
int col;
ElemType e;
}Twin;

class CSparseMat
{
public:
CSparseMat(){}
CSparseMat(int r,int c,int n);
virtual ~CSparseMat(){}
void ShowSparse(int i,int j);

Twin* m_pt;	// 指向非零元的指針
int rpos[Max];
int m_nCol;		// 矩陣列數
int m_nRow;		// 矩陣行數
int m_nTws;		// 非零元個數

};

CSparseMat::CSparseMat(int r, int c, int n)
{
m_nRow=r;
m_nCol=c;
m_nTws=n;
m_pt=new Twin[m_nTws];
if(!m_pt) return;

// 輸入矩陣的所有二元組
int i;
for(i=0;i<m_nTws;i++){
	cout<<"請輸入非零元二元組的列標和值:";
	cin>>m_pt[i].col>>m_pt[i].e;
}
for(i=0;i<m_nRow;i++){
	cout<<"請輸入每行第一個非零元在二元組中的序號(沒有輸入-1):";
	cin>>rpos[i]; // 該行沒有非零元輸入-1
}

}

void CSparseMat::ShowSparse(int i,int j)
{
if(i>m_nRow||j>m_nCol) return;

ElemType x=0;
int s,d;
if(i==m_nRow){
	s=rpos[i];
	d=m_nTws;
}
else{
	s=rpos[i];
	int m=1;
	d=rpos[i+m];
	while(d<0){
		if(i+m<m_nRow){
			m++;
			d=rpos[i+m];
		}
		else
			d=m_nTws;
	}
}
if(s>=0){
	int k=s;
	while(k<d){
		if(m_pt[k].col==j)
			x=m_pt[k].e;
		k++;
	}
}
cout<<x<<endl;

}

int main()
{
CSparseMat A(3,3,5);
A.ShowSparse(2,1);
return 0;
}
5.26 解:
typedef int ElemType;
typedef struct OLNode{
int row;
int col;
ElemType e;
struct OLNode *right,*down;
}OLNode,*OLink;

class CCrossListMat
{
public:
OLink *RHead,*CHead; // 行與列指針向量的頭指針
int m_nCol; // 矩陣列數
int m_nRow; // 矩陣行數
int m_nNum; // 非零元個數
public:
CCrossListMat(){}
CCrossListMat(int r,int c,int n);
virtual ~CCrossListMat(){}
void ShowMat(int i,int j);
};

CCrossListMat::CCrossListMat(int r, int c, int n)
{
m_nRow=r;
m_nCol=c;
m_nNum=n;
int i;

RHead=new OLink[m_nRow];
if(!RHead) exit(-2);
CHead=new OLink[m_nCol];
if(!CHead) exit(-2);
for(i=0;i<m_nRow;i++)
	RHead[i]=NULL;
for(i=0;i<m_nCol;i++)
	CHead[i]=NULL;

OLink p,q,qf;
for(i=0;i<m_nNum;i++){
	p=new OLNode;
	if(!p) exit(-2);
	cout<<"請輸入非零元的行標、列標和值:";
	cin>>p->row>>p->col>>p->e;
	q=RHead[p->row];
	if(!q){
		RHead[p->row]=p;
		p->right=NULL;
	}
	else{
		qf=q;
		while(q && q->col<p->col){
			qf=q;
			q=q->right;
		}
		p->right=qf->right;
		qf->right=p;
	}
	q=CHead[p->col];
	if(!q){
		CHead[p->col]=p;
		p->down=NULL;
	}
	else{
		qf=q;
		while(q && q->row<p->row){
			qf=q;
			q=q->down;
		}
		p->down=qf->down;
		qf->down=p;
	}
}

}

void CCrossListMat::ShowMat(int i,int j)
{
ElemType x=0;
OLink p;
p=RHead[i];
while(p && p->col!=j)
p=p->right;
if§
x=p->e;
cout<<x<<endl;
}
5.27 解:
#include<iostream.h>
#include<stdlib.h>

typedef int ElemType;
typedef struct OLNode{
int row;
int col;
ElemType e;
struct OLNode *right,*down;
}OLNode,*OLink;

class CCrossListMat
{
public:
OLink *RHead,*CHead; // 行與列指針向量的頭指針
int m_nCol; // 矩陣列數
int m_nRow; // 矩陣行數
int m_nNum; // 非零元個數
public:
CCrossListMat(){}
virtual ~CCrossListMat(){}
CCrossListMat(int r,int c,int n);
void Add(CCrossListMat B);
void ShowMat();
};

CCrossListMat::CCrossListMat(int r, int c, int n)
{
m_nRow=r;
m_nCol=c;
m_nNum=n;
int i;

RHead=new OLink[m_nRow];
if(!RHead) exit(-2);
CHead=new OLink[m_nCol];
if(!CHead) exit(-2);
for(i=0;i<m_nRow;i++)
	RHead[i]=NULL;
for(i=0;i<m_nCol;i++)
	CHead[i]=NULL;

OLink p,q,qf;
for(i=0;i<m_nNum;i++){
	p=new OLNode;
	if(!p) exit(-2);
	cout<<"請輸入非零元的行標、列標和值:";
	cin>>p->row>>p->col>>p->e;
	q=RHead[p->row];
	if(!q){
		RHead[p->row]=p;
		p->right=NULL;
	}
	else{
		qf=q;
		while(q && q->col<p->col){
			qf=q;
			q=q->right;
		}
		p->right=qf->right;
		qf->right=p;
	}
	q=CHead[p->col];
	if(!q){
		CHead[p->col]=p;
		p->down=NULL;
	}
	else{
		qf=q;
		while(q && q->row<p->row){
			qf=q;
			q=q->down;
		}
		p->down=qf->down;
		qf->down=p;
	}
}

}

void CCrossListMat::Add(CCrossListMat B)
{
int i,k=0;
OLink pa,pb;
OLink pre,p; // 按行插入
OLink qpre,q; // 按列插入

for(i=0;i<m_nRow;i++){
	pa=RHead[i];
	pb=B.RHead[i];
	pre=NULL;

	while(pb){
		while(pa&&pa->col<pb->col){
			pre=pa;
			pa=pa->right;
		}
		if(pa&&pa->col==pb->col){
			pa->e=pa->e+pb->e;
			pb=pb->right;
			pre=pa;
			pa=pa->right;
		}
		else{
			// 在A中插入一個新結點
			p=new OLNode;
			p->row=pb->row;
			p->col=pb->col;
			p->e=pb->e;
			pb=pb->right;
			if(!pre){
				p->right=pa;
				RHead[i]=p;
			}
			else{
				p->right=pre;
				pre->right=p;
			}
			// 處理列指針
			qpre=NULL;
			q=CHead[p->col];
			while(q&&q->row<i){
				qpre=q;
				q=q->down;
			}
			if(!qpre){
				p->down=q;
				CHead[p->col]=p;
			}
			else{
				p->down=pre;
				pre->down;
			}
			k++;
		}
	} // end while(pb)
} // end for
m_nNum=m_nNum+k;

}

void CCrossListMat::ShowMat()
{
int i,j;
OLink p;
for(i=0;i<m_nRow;i++){
p=RHead[i];
for(j=0;j<m_nCol;j++){
if(p && p->rowi && p->colj){
cout<e<<" “;
p=p->right;
}
else
cout<<0<<” ";
}
cout<<endl;
}
}

int main()
{
CCrossListMat A(3,3,4),B(3,3,2);
A.Add(B);
A.ShowMat();
return 0;
}
以下是關於廣義表算法涉及的描述及方法
#include “DSConst.h” // 常量定義頭文件
#include “StrStat.h” // 字符串定義頭文件

// 廣義表數據結構聲明
typedef char AtomType;
typedef enum{ATOM,LIST} ElemTag;
typedef struct GLNode{
ElemTag tag;
union{
AtomType atom;
struct GLNode *hp;
};
struct GLNode *tp;
}*GList;

// 將非空串Str分割成兩部分,HStr爲第一個,TStr爲之後的子串
int StrDistrict(CString& Str,CString& HStr,CString& TStr)
{
int n,i,k;
CString s1;
CString s2(","), s3("("), s4(")"); // 定義常量串
n=Str.StrLength();
i=1;
k=0;
while(i<=n && s1.StrCompare(s2) || k!=0){
s1=Str.SubString(i,1);
if(!s1.StrCompare(s3)) k++;
else if(!s1.StrCompare(s4)) k–;
i++;
}
if(i<=n){
HStr=Str.SubString(1,i-2);
TStr=Str.SubString(i,n-i+1);
}
else{
HStr=Str;
TStr.StrClear();
}
return OK;
}

// 用串s建立廣義表L
int CreateGList(GList& L,CString& s)
{
CString Sub,HSub,TSub; // 子串,表頭串,表尾串
if(s.StrEmpty()) L=NULL;
else{ // 非空串,建立廣義表
L=new GLNode; // 開闢一個結點
if(!L) exit(OVERFLOW);

	if(s.StrLength()>1){	// 如果串長大於1,說明是表結點
		L->tag=LIST;
		Sub=s.SubString(2,s.StrLength()-2);	// 取括號內子串
		if(!Sub.StrEmpty()){	// 建立子表
			StrDistrict(Sub,HSub,TSub);
			if(!HSub.StrEmpty())	// 表頭不空
				CreateGList(L->hp,HSub);
			else L->hp=NULL;
			if(!TSub.StrEmpty())	// 表尾不空
				CreateGList(L->tp,TSub);
			else L->tp=NULL;
		}
		else{	// 空表
			L->hp=NULL;
			L->tp=NULL;
		}
	}
	else{	// 建立原子結點
		L->tag=ATOM;
		L->atom=s.GetStr()[0];
		L->tp=NULL;
	}
}
return OK;

}

// 顯示廣義表串
void ShowGList(GList &L)
{
if(L){
if(L->tag==LIST){
cout<<"(";
if(L->hp)
ShowGList(L->hp);
if(L->tp){
cout<<",";
ShowGList(L->tp);
}
cout<<")";
}
else cout<atom;
}
}

5.30 解:
// 求廣義表深度的遞歸算法
int GListDepth(GList& L)
{
int Depth=0;
int HDepth,TDepth; // 表頭深度,表尾深度
if(!L) return Depth; // 廣義表不存在
if(L->tagATOM) return Depth; // 原子結點深度爲0
else{
Depth++; // 表結點深度爲1
HDepth=Depth+GListDepth(L->hp);
TDepth=Depth+GListDepth(L->tp);
return HDepth>TDepth?HDepth:TDepth;
}
}
5.31 解:
// 由廣義表L複製廣義表T
int CopyGList(GList& T,GList& L)
{
if(!L) T=NULL;
else{
T=new GLNode;
if(!T) exit(OVERFLOW);
T->tag=L->tag;
if(L->tag
ATOM) T->atom=L->atom;
else{
CopyGList(T->hp,L->hp);
CopyGList(T->tp,L->tp);
}
}
return OK;
}
5.32 解:
// 判兩廣義表是否相等,相等返回OK,否則返回FALSE
Status GListCompare(GList& L1,GList& L2)
{
if(!L1 && !L2) return OK; // L1和L2均爲空表
if((!L1 && L2) || (L1 && !L2)) return FALSE;
else{ // L1和L2均非空表
if(L1->tagL2->tag){ // 表屬性相同
if(L1->tag
ATOM){ // 均爲原子結點
if(L1->atom==L2->atom) return OK;
else return FALSE;
}
else{ // 均爲表結點
if(GListCompare(L1->hp,L2->hp) &&
GListCompare(L1->tp,L2->tp))
return OK; // 表頭、表尾均相同
else return FALSE;
}
}
else return FALSE; // 表屬性不同
}
}
5.33 解:
6章 樹和二叉樹
6.1 已知一棵樹邊的集合爲{<I,M>, <I,N>, <E,I>, <B,E>, <B,D>, <A,B>, <G,J>, <G,K>, <C,G>, <C,F>, <A,C>},請畫出這棵樹,並回答下列問題:
(1) 哪個是根結點?
(2) 哪些是葉子結點?
(3) 哪個是結點G的雙親?
(4) 哪些是結點G的祖先?
(5) 哪些是結點G的孩子?
(6) 哪些是結點E的子孫?
(7) 那些是結點E的子孫?
(8) 結點B和N的層次號分別是什麼?
(9) 樹的深度是多少?
(10) 以結點C爲根的子樹的深度是多少?
6.2 一棵度爲2的樹與一棵二叉樹有何區別?
解:二叉樹是顆有序樹,但度爲2的樹則未必有序。
6.3 試分別畫出具有3個結點的樹和3個結點的二叉樹的所有不同形態。
6.4 一棵深度爲H的滿k叉樹有如下性質:第H層上的結點都是葉子結點,其餘各層上每個結點都有k棵非空子樹。如果按層次順序從1開始對全部結點編號,問:
(1) 各層的結點數目是多少?
(2) 編號爲p的結點的父結點(若存在)的編號是多少?
(3) 編號爲p的結點的第i個兒子結點(若存在)的編號是多少?
(4) 編號爲p的結點有右兄弟的條件是什麼?其右兄弟的編號是多少?
解:(1)
(2)如果p是其雙親的最小的孩子(右孩子),則p減去根結點的一個結點,應是k的整數倍,該整數即爲所在的組數,每一組爲一棵滿k叉樹,正好應爲雙親結點的編號。如果p是其雙親的最大的孩子(左孩子),則p+k-1爲其最小的弟弟,再減去一個根結點,除以k,即爲其雙親結點的編號。
綜合來說,對於p是左孩子的情況,i=(p+k-2)/k;對於p是右孩子的情況,i=(p-1)/k
如果左孩子的編號爲p,則其右孩子編號必爲p+k-1,所以,其雙親結點的編號爲
向下取整,如1.5向下取整爲1
(3)結點p的右孩子的編號爲kp+1,左孩子的編號爲kp+1-k+1=k(p-1)+2,第i個孩子的編號爲k(p-1)+2+i-1=kp-k+i+1。
(4)當(p-1)%k != 0時,結點p有右兄弟,其右兄弟的編號爲p+1。
6.5 已知一棵度爲k的樹中有 個度爲1的結點, 個度爲2的結點,…, 個度爲k的結點,問該樹中有多少個葉子結點?
解:根據樹的定義,在一顆樹中,除樹根結點外,每個結點有且僅有一個前驅結點,也就是說,每個結點與指向它的一個分支一一對應,所以除樹根結點之外的結點樹等於所有結點的分支數,即度數,從而可得樹中的結點數等於所有結點的度數加1。總結點數爲

而度爲0的結點數就應爲總結點數減去度不爲0的結點數的總和,即

6.6 已知在一棵含有n個結點的樹中,只有度爲k的分支結點和度爲0的葉子結點。試求該樹含有的葉子節點數目。
解:利用上題結論易得結果。設度爲k的結點個數爲 ,則總結點數爲 。葉子結點的數目應等於總結點數減去度不爲0的結點的數目,即

6.7 一棵含有n個結點的k叉樹,可能達到的最大深度和最小深度各爲多少?
解:能達到最大深度的樹是單支樹,其深度爲n。滿k叉樹的深度最小,其深度爲
(證明見徐孝凱著數據結構實用教程P166)
6.8 證明:一棵滿k叉樹上的葉子結點數 和非葉子結點數 之間滿足以下關係:

解:一棵滿k叉樹的最後一層(深度爲h)的結點數(葉子結點數)爲 ,其總結點數爲 ,則非葉子結點數 ,從而得

6.9 試分別推導含有n個結點和含 個葉子結點的完全三叉樹的深度H。
解:(1) 根據完全三叉樹的定義

(2) 設總的結點數爲n,非葉子結點數爲 注意到每個非葉子結點的度均爲3,則
	 		由  

6.10 對於那些所有非葉子結點均含有左右子數的二叉樹:
(1) 試問:有n個葉子結點的樹中共有多少個結點?
(2) 試證明: ,其中n爲葉子結點的個數, 表示第i個葉子結點所在的層次(設根節點所在層次爲1)。
解:(1)總結點數爲 ,其中 爲非葉子結點數,則葉子結點數爲 ,所以總結點數爲 。
(2)用歸納法證明。
i=1,說明二叉樹只有一個葉子結點,則整棵樹只有一個根結點, ,
,結論成立。
設有n個葉子結點時也成立,即 ,現假設增加一個葉子結點,這意味着在某葉子結點p上新生兩個葉子結點,而結點p則成爲非葉子結點,可見,總結點數增2,葉子結點數增1。此時,所有葉子結點是原結點除去p,然後加上兩個深度爲 的新葉子結點,由此,

則當i=n+1時,也成立,由此即得到證明。
6.11 在二叉樹的順序存儲結構中,實際上隱含着雙親的信息,因此可和三叉鏈表對應。假設每個指針域佔4個字節,每個信息域佔k個字節。試問:對於一棵有n個結點的二叉樹,且在順序存儲結構中最後一個節點的下標爲m,在什麼條件下順序存儲結構比三叉鏈表更節省空間?
解:採用三叉鏈表結構,需要n(k+12)個字節的存儲空間。採用順序存儲結構,需要mk個字節的存儲空間,則當mk<n(k+12)時,即 時,採用順序存儲比採用三叉鏈表更節省空間。
6.12 對題6.3所得各種形態的二叉樹,分別寫出前序、中序和後序遍歷的序列。
6.13 假設n和m爲二叉樹中兩結點,用1、0或#(分別表示肯定、恰恰相反或不一定)填寫下表:

已知 前序遍歷時
n在m前? 中序遍歷時
n在m前? 後序遍歷時
n在m前?
n在m左方      
n在m右左方      
n是m祖先      
n是m子孫      
注:如果(1)離a和b最近的共同祖先p存在,且(2)a在p的左子樹中,b在p的右子樹中,則稱a在b的左方(即b在a的右方)。
6.14 找出所有滿足下列條件的二叉樹:
(a) 它們在先序遍歷和中序遍歷時,得到的節點訪問序列相同;
(b) 它們在後序遍歷和中序遍歷時,得到的結點訪問序列相同;
© 它們在先序遍歷和後序遍歷時,得到的節點訪問序列相同。
解:(a) 不含左子樹的二叉樹。
(b) 不含右子樹的二叉樹。
© 即不含左子樹,也不含右子樹的二叉樹。
6.15 解:

6.16 解:1 2 3 4 5 6 7 8 9 10 11 12 13 14
Info A B C D E F G H I J K L M N
Ltag 0 0 0 1 0 1 0 1 0 0 1 1 1 1
Lchild 2 4 6 2 7 3 10 14 12 13 13 9 10 11
Rtag 0 0 1 1 0 0 0 1 1 1 0 1 1 1
Rchild 3 5 6 5 8 9 11 3 12 13 14 0 11 8
6.17 解:其錯誤在於中序遍歷應先訪問其左子樹,可做如下修改:
BiTree InSucc(BiTree q){
// 一直q是指向中序線索二叉樹上某個結點的指針,
// 本函數返回指向*q的後繼的指針。
r=q->rchild;
if(!r->ltag)
while(!r->ltag) r=r->lchild;
return r;
} // InSucc
6.18 解:如果p是根結點,則其後繼爲空。否則需查找p的雙親結點。從p結點開始中序線索遍歷,如果某結點的左指針域等於p,說明該結點是p的雙親結點,且p是它的左孩子;如果某結點的右指針域等於p,說明該結點是p的雙親結點,且p是它的右孩子;如此即可確定訪問次序。若是右孩子,其後繼是雙親結點;若是左孩子,其後繼是其兄弟最左下的子孫,如果兄弟不存在,其後繼是其雙親結點。
6.19 分別畫出和下列樹對應的各個二叉樹:
解:

6.20 解:

(1) 先序:1 2 3 4 5 6 8 7 9 10 11 12 13 15 14
(2) 中序:3 4 8 6 7 5 2 1 10 9 11 15 14 13 12
(3) 後序:8 7 6 5 4 3 2 10 15 14 13 12 11 9 1

6.23 解:樹的先根序列爲GFKDAIEBCHJ,後根序列爲DIAEKFCJHBG,可以先轉化成二叉樹,再通過二叉樹轉換成樹。注意二叉樹的先根序列與等價樹的先根序列相同,二叉樹的中序序列對應着樹的後根序列。
GFKDAIEBCHJ爲所求二叉樹的先序序列,DIAEKFCJHBG爲二叉樹的中序序列。通過觀察先序序列,G爲二叉樹的根結點,再由中序序列,G的左子樹序列爲DIAEKFCJHB,右子爲空。可以表示成如下形式:
G(DIAEKFCJHB,NULL)
對於子樹先序序列爲FKDAIEBCHJ,中序序列爲DIAEKFCJHB,顯然子樹根爲F。再由中序序列可以看到,F的左子樹是DIAEK,右子樹爲CJHB。進一步表示成:
G(F(DIAEK,CJHB),NULL)
對於DIAEK(中序表示),先序爲KDAIE,K爲根,左子爲DIAE,右子爲空;對於CJHB,B爲根,左子爲CJH,右子爲空。進一步表示成:
G(F(K(DIAE,NULL),B(CJH,NULL)),NULL)
G(F(K(D(NULL,IAE),NULL),B(C(NULL,JH),NULL)),NULL)
G(F(K(D(NULL,A(I,E)),NULL),B(C(NULL,H(J,NULL)),NULL)),NULL)
由此畫出二叉樹,進而畫出樹。

6.24 解:本題應注意下列轉換:
樹 森林 二叉樹
先根 先序 先序
後根 中序 中序

6.25 解;用歸納法證明。
當n=2時,要使其成爲最優二叉樹,必須使兩個結點都成爲葉子結點。
設n=k時成立,則當n=k+1時,要使其成爲最優,必須用k個結點的哈夫曼樹與第k+1個結點組成一個新的最優二叉樹,所以n=k+1時也成立。
6.26 解:不妨設這8個結點爲A、B、C、D、E、F、G、H,其相應的權爲7、19、2、6、32、3、21、10。

A:1101 B:01 C:11111 D:1110 E:10 F:11110 G:00 H:1100
採用這種方式編碼,電文最短。
6.27 解:

6.33 解:
int Visit(int u,int v)
{
if(u==v) return 1;
if(L[v]==0){//左子樹不存在
if(R[v]==0)//右子樹也不存在
return 0;
else{// 右子樹存在,繼續訪問右子樹
if(Visit(u,R[v])) return 1;
else return 0;
}
}
else{// 左子樹存在
if(Visit(u,L[v]))// 左子樹中存在目標
return 1;
else{// 左子樹中沒有目標,需訪問右子樹
if(R[v]0)// 沒有右子樹
return 0;
else{// 右子樹存在,繼續訪問右子樹
if(Visit(u,R[v])) return 1;
else return 0;
}
}
}
}
6.34 解:
int Visit(int u,int v)
{
int Nv;
Nv=NumElem(v); // 返回結點v的下標
if(Nv
-1) exit(-2); // 結點v不存在,退出

if(u==v) return 1;
if(L[Nv]==0){//左子樹不存在
	if(R[Nv]==0)//右子樹也不存在
		return 0;
	else{// 右子樹存在,繼續訪問右子樹
		if(Visit(u,R[Nv])) return 1;
		else return 0;
	}
}
else{// 左子樹存在
	if(Visit(u,L[Nv]))// 左子樹中存在目標
		return 1;
	else{// 左子樹中沒有目標,需訪問右子樹
		if(R[Nv]==0)// 沒有右子樹
			return 0;
		else{// 右子樹存在,繼續訪問右子樹
			if(Visit(u,R[Nv])) return 1;
			else return 0;
		}
	}
}

}
// 返回結點在數組T中的下標
int NumElem(int x)
{
int i=0;
while(i<N&&T[i]!=x) i++;
if(T[i]==x) return i;
else return -1;
}
6.35 解:
#define N 8
char T[N]={‘0’,‘a’,‘b’,‘c’,‘d’,‘e’,‘f’,‘g’};
// 用順序數組存儲,0爲頭結點,
// a爲根結點,b爲左子樹,c爲右子樹,…

// 若某結點編號爲奇數,
// 則它必爲其雙親的右孩子,否則爲左孩子
// 編號爲k的結點的雙親結點的編號爲k/2

int NumTree(char c);// 求結點在樹中的編號
int Exp(int a,int b);// 求a的b次方
int NumElem(char c);// 返回元素在數組中的下標

int main()
{
char c;
cout<<“請輸入結點的值:”;
cin>>c;
cout<<NumTree©<<endl;
return 0;
}

int NumTree(char c)// 求結點在樹中的編號
{
int k,i=0;
int Code[N];
k=NumElem©; // 返回結點c的下標
if(k==-1) exit(-2); // 結點c不存在,退出

while(k){
	// 如果k爲偶數,記錄一個代碼1
	if(k%2==1) Code[i]=1;
	else Code[i]=0;
	k=k/2;
	i++;
}
int x=0,j;
for(j=0;j<i;j++)
	x=x+Code[j]*Exp(2,j);
return x;

}

int Exp(int a,int b)
{
int i;
int x=1;
for(i=0;i<b;i++)
x=x*a;
return x;
}

// 返回結點在數組T中的下標
int NumElem(char c)
{
int i=0;
while(i<N&&T[i]!=c) i++;
if(T[i]c) return i;
else return -1;
}
6.36 解:
Status SimilarTree(BiTree& T1,BiTree& T2)
{
if(!T1){// T1是空樹
if(!T2) return TRUE;// T2是空樹
else return FALSE;
}
else{// T1是非空樹
if(!T2) return FALSE;
else{// T2是非空樹
if(SimilarTree(T1->lchild,T2->lchild)
&& SimilarTree(T1->rchild,T2->rchild))
return TRUE;
else return FALSE;
}
}
}
6.37 解:
// 先序遍歷的非遞歸算法
Status POTraverse(BiTree& T,Status (*Visit)(TElemType e))
{
BiTree p;
Stack s;
InitStack(s);
p=T;
while(p||!StackEmpty(s)){
if§{
// 如果根指針不空,訪問根結點,
// 右指針域壓棧,繼續遍歷左子樹
if(!Visit(p->data)) return ERROR;
Push(s,p->rchild);
p=p->lchild;
} // 根指針已空,本子樹已遍歷完畢,
// 退棧返回上一層,繼續遍歷未曾訪問的結點
else Pop(s,p);
}
return OK;
}
6.41 解:
// 求位於先序序列中第k個位置的結點的值,
// e中存放結點的返回值,i爲計數器
Status PONodeK(TElemType& e,int& i,int k,BiTree& T)
{
if(T){
i++;
if(i
k) e=T->data;
else{
PONodeK(e,i,k,T->lchild);
PONodeK(e,i,k,T->rchild);
}
}
return OK;
}
6.42 解:
// 求二叉樹中葉子結點的數目
Status POLeafNodeNum(int& i,BiTree& T)
{
if(T){
if(!T->lchild && !T->rchild) i++;
POLeafNodeNum(i,T->lchild);
POLeafNodeNum(i,T->rchild);
}
return OK;
}
6.43 解:
// 按先序交換二叉樹的左右子樹
Status ExchangeBiTree(BiTree& T)
{
BiTree p;
if(T){
p=T->lchild;
T->lchild=T->rchild;
T->rchild=p;
ExchangeBiTree(T->lchild);
ExchangeBiTree(T->rchild);
}
return OK;
}
6.44 解:
// 求二叉樹中以元素值爲x的結點爲根的子樹的深度
Status ChildTreeDepth(BiTree& T,TElemType x,int& depth)
{
BiTree T1;
if(PreOrderLocate(T,x,T1)){
depth=BiTDepth(T1);
return OK;
}
else return ERROR;
}

// 按先序在樹中查找根爲x的子樹,T1爲指向子樹根的指針
Status PreOrderLocate(BiTree& T,TElemType x,BiTree& T1)
{
if(T){
if(T->data==x){
T1=T;
return OK;
}
else{
if(PreOrderLocate(T->lchild,x,T1))
return OK;
else{
if(PreOrderLocate(T->rchild,x,T1))
return OK;
else return ERROR;
}
}
}
else return ERROR;
}

// 求二叉樹的深度
int BiTDepth(BiTree& T)
{
int ldep,rdep;
if(!T) return 0;
else{
ldep=BiTDepth(T->lchild)+1;
rdep=BiTDepth(T->rchild)+1;
return ldep>rdep?ldep:rdep;
}
}
6.45 解:
// 刪除以元素值爲x的結點爲根的子樹
Status DelChildTree(BiTree& T,TElemType x)
{
if(T){
if(T->data==x){
DelBTree(T);
T=NULL;
return OK;
}
else{
if(DelChildTree(T->lchild,x))
return OK;
else{
if(DelChildTree(T->rchild,x))
return OK;
else return ERROR;
}
}
}
else return ERROR;
}
// 刪除二叉樹
Status DelBTree(BiTree& T)
{
if(T){
DelBTree(T->lchild);
DelBTree(T->rchild);
delete T;
return OK;
}
else return ERROR;
}
6.46 解:
// 複製一棵二叉樹
Status CopyBiTree(BiTree& T,BiTree& T1)
{
BiTree p;
if(T){
p=new BiTNode;
if(!p) return ERROR;
p->data=T->data;
T1=p;
CopyBiTree(T->lchild,T1->lchild);
CopyBiTree(T->rchild,T1->rchild);
}
else{
T1=T;
}
return OK;
}
6.47 解:
typedef BiTree QElemType;
#include “c:\Yin\include\Queue.h”
Status LevelOrderTraverse(BiTree& T,Status (*Visit)(TElemType e))
{
QElemType p;
Queue q;
InitQueue(q);

if(T) EnQueue(q,T);
while(!QueueEmpty(q)){
	DeQueue(q,p);
	Visit(p->data);
	if(p->lchild) EnQueue(q,p->lchild);
	if(p->rchild) EnQueue(q,p->rchild);
}
return OK;

}
6.48 解:
// 在二叉樹T中求結點p和q的共同最小祖先e
Status MinComAncst(BiTree& T,TElemType& e,TElemType *p,TElemType *q)
{
if(!T) return ERROR;
BiTree T1,T2,pt=NULL;
if(!CopyBiTree(T,T1)) return ERROR;
if(!CopyBiTree(T,T2)) return ERROR;
if(!PathTree(T1,p))
return ERROR;// 求根結點到結點p的路徑樹T1
else ShowBiTree(T1);
cout<<endl;
if(!PathTree(T2,q))
return ERROR;// 求根結點到結點q的路徑樹T2
else ShowBiTree(T2);
cout<<endl;
while(T1 && T2 && T1->dataT2->data){
pt=T1;
if(T1->lchild){
T1=T1->lchild;
T2=T2->lchild;
}
else{
if(T1->rchild){
T1=T1->rchild;
T2=T2->rchild;
}
}
}
if(!pt) return ERROR;
else{
e=pt->data;
return OK;
}
}
// 在二叉樹T中求根到結點p的路徑樹,該操作將剪去除路徑之外的所有分支
Status PathTree(BiTree& T,TElemType *p)
{
if(!T || !p) return ERROR;
if(T->data
*p){// 找到目標,刪除目標的左右子樹
if(T->lchild) DelBiTree(T->lchild);
if(T->rchild) DelBiTree(T->rchild);
return OK;
}
else{// 沒找到目標,繼續遞歸查找
if(PathTree(T->lchild,p)){// 目標在左子樹中,刪除右子樹
if(T->rchild) DelBiTree(T->rchild);
return OK;
}
else
if(PathTree(T->rchild,p)){// 目標在右子樹中,刪除左子樹
if(T->lchild) DelBiTree(T->lchild);
return OK;
}
else return ERROR;// 找不到目標
}
}
6.49 解:
Status CompleteBiTree(BiTree& T)
{
int d;
if(T){
d=BiTDepth(T->lchild)-BiTDepth(T->rchild);
if(d<0 || d>1) return ERROR;
else{
if(CompleteBiTree(T->lchild) &&
CompleteBiTree(T->rchild)) return OK;
else return ERROR;
}
}
else return OK;
}
6.51 解:
Status ShowBiTExpress(BiTree& T)
{
if(T){
if(T->lchild){
if(Low(T->lchild->data,T->data)){
cout<<’(’;
ShowBiTExpress(T->lchild);
cout<<’)’;
}
else ShowBiTExpress(T->lchild);
}
cout<data;
if(T->rchild){
if(Low(T->rchild->data,T->data)){
cout<<’(’;
ShowBiTExpress(T->rchild);
cout<<’)’;
}
else ShowBiTExpress(T->rchild);
}
}
return OK;
}

Status Low(char a,char b)
{
if((a==’+’ || a==’-’) && (b==’*’ || b==’/’)) return TRUE;
else return FALSE;
}
6.52 解:
int BiTreeThrive(BiTree& T)
{
int i,d,nn[20];
d=BiTDepth(T);
BiTree p=T;
Stack s1,s2;
InitStack(s1); InitStack(s2);
for(i=0;i<20;i++){
nn[i]=0; // 每層結點個數
}

if(p) Push(s1,p);
else return 0;
for(i=0;i<d;i++){
	if(!StackEmpty(s1) && StackEmpty(s2)){
		while(!StackEmpty(s1)){
			Pop(s1,p);	nn[i]++;// s1中存放第i層的結點
			if(p->lchild) Push(s2,p->lchild);//s2中存放第i+1層結點
			if(p->rchild) Push(s2,p->rchild);
		}
	}
	else{
		if(StackEmpty(s1) && !StackEmpty(s2)){
			while(!StackEmpty(s2)){
				Pop(s2,p);	nn[i]++;
				if(p->lchild) Push(s1,p->lchild);
				if(p->rchild) Push(s1,p->rchild);
			}
		}
	}
}
int max=nn[0];
for(i=0;i<d;i++)
	if(max<nn[i]) max=nn[i];
return max*d;

}
6.53 解:
// 所有從根到葉子最長路徑樹
Status MaxPathBiTree(BiTree& T)
{
if(T){
if(BiTDepth(T)-BiTDepth(T->lchild)!=1)
DelBiTree(T->lchild);
else
MaxPathBiTree(T->lchild);
if(BiTDepth(T)-BiTDepth(T->rchild)!=1)
DelBiTree(T->rchild);
else
MaxPathBiTree(T->rchild);
}
return OK;
}
// 從根到葉子最長路徑中最左方的路徑樹
Status LMaxPathBiTree(BiTree& T)
{
if(T){
if(BiTDepth(T)-BiTDepth(T->lchild)==1){
DelBiTree(T->rchild);
LMaxPathBiTree(T->lchild);
}
else{
DelBiTree(T->lchild);
if(BiTDepth(T)-BiTDepth(T->rchild)1)
LMaxPathBiTree(T->rchild);
else
DelBiTree(T->rchild);
}
}
return OK;
}
6.54 解:
// 根據完全二叉順序樹創建完全二叉鏈表樹
Status CreateCompleteBiTree(SqList& ST,BiTree& LT)
{
BiTree p;
int i=0,len;
if(ST.Length
0) return OK;
p=new BiTNode;
if(!p) return ERROR;
p->data=ST.Get(i);
p->lchild=NULL;
p->rchild=NULL;
LT=p;

Queue q;
InitQueue(q);
EnQueue(q,p);
len=ST.Length();

while(!QueueEmpty(q)&&i<len-1){
	DeQueue(q,p);
	if(i<len-1 && i%2==0){
		p->lchild=new BiTNode;
		if(!p->lchild) return ERROR;
		p->lchild->data=ST.Get(++i);
		p->lchild->lchild=NULL;
		p->lchild->rchild=NULL;
		EnQueue(q,p->lchild);
	}
	if(i<len-1 && i%2==1){
		p->rchild=new BiTNode;
		if(!p->rchild) return ERROR;
		p->rchild->data=ST.Get(++i);
		p->rchild->lchild=NULL;
		p->rchild->rchild=NULL;
		EnQueue(q,p->rchild);
	}
}
return OK;

}
6.55 解:
Status PreOrderTraverse(BiTree& T)
{
if(T){
T->DescNum=DescendNum(T);
PreOrderTraverse(T->lchild);
PreOrderTraverse(T->rchild);
}
return OK;
}

int DescendNum(BiTree& T)
{
if(!T) return 0;
if(!T->lchild){
if(!T->rchild) return 0;
else return DescendNum(T->rchild)+1;
}
else{
if(!T->rchild) return DescendNum(T->lchild)+1;
else return DescendNum(T->rchild)+DescendNum(T->lchild)+2;
}
}
6.56 解:
先對二叉樹T進行先序線索,得到先序線索二叉樹Thrt。然後再進行查找。
// 先序線索二叉樹算法
Status PreOrderThreading(BiThrTree& Thrt,BiThrTree& T)
{
BiThrTree pre;
Thrt=new BiThrNode; // 爲線索二叉樹建立頭結點
if(!Thrt) exit(OVERFLOW);
Thrt->LTag=Thread;
Thrt->RTag=Link;
Thrt->lchild=Thrt;// 左子樹回指
if(!T) Thrt->rchild=Thrt;// 若二叉樹空,右子樹回指
else{
Thrt->rchild=T;
pre=Thrt;
PreThreading(T,pre); // 先序遍歷進行先序線索化
pre->rchild=Thrt; // 最後一個結點線索化
pre->RTag=Thread;
Thrt->lchild=pre;
}
return OK;
}

Status PreThreading(BiThrTree& T,BiThrTree& pre)
{
if(T){
if(!T->lchild){
T->LTag=Thread;
T->lchild=pre;
}
if(pre && !pre->rchild){
pre->RTag=Thread;
pre->rchild=T;
}
pre=T;
if(T->LTagLink) PreThreading(T->lchild,pre);
if(T->RTag
Link) PreThreading(T->rchild,pre);
}
return OK;
}

// 從二叉線索樹上任一結點q開始查找結點p。
// 如果找到,將
p的後繼結點指針存於q中,返回TRUE;否則返回FALSE
Status FindNextInBiThrTree(BiThrTree& q,TElemType *p)
{
BiThrTree pt=q;
if(!pt) return FALSE;
if(pt->data==*p){
if(pt->LTagLink) q=pt->lchild;
else q=pt->rchild;
return OK;
}
pt=q->rchild;
while(pt!=q && pt->data!=*p){
if(pt->LTag
Link) pt=pt->lchild;
else pt=pt->rchild;
}
if(ptq) return FALSE;
if(pt->data
*p){
if(pt->LTag==Link) q=pt->lchild;
else q=pt->rchild;
}
return OK;
}
6.57 解:
Status PostOrderThreading(BiThrTree& T,BiThrTree& pre);//首先建立後序線索樹
Status FindNextInBiThrTree(BiThrTree& q,TElemType *p);//再進行查找

// 後序線索二叉樹的算法
Status PostOrderThreading(BiThrTree& Thrt,BiThrTree& T)
{
BiThrTree pre;
Thrt=new BiThrNode; // 爲線索二叉樹建立頭結點
if(!Thrt) exit(OVERFLOW);
Thrt->LTag=Link;
Thrt->RTag=Thread;
Thrt->rchild=Thrt;// 右子樹回指
if(!T) Thrt->lchild=Thrt;// 若二叉樹空,左子樹回指
else{
Thrt->lchild=T;
pre=Thrt;
PostThreading(T,pre); // 後序遍歷進行後序線索化
pre->rchild=Thrt;// 最後一個結點線索化
pre->RTag=Thread;
Thrt->rchild=pre;
}
return OK;
}

Status PostThreading(BiThrTree& T,BiThrTree& pre)
{
if(T){
if(T->LTagLink) PostThreading(T->lchild,pre);
if(T->RTag
Link) PostThreading(T->rchild,pre);
if(!T->lchild){
T->LTag=Thread;
T->lchild=pre;
}
if(pre && !pre->rchild){
pre->RTag=Thread;
pre->rchild=T;
}
pre=T;
}
return OK;
}
6.58 解:
typedef char TElemType;
typedef struct CSNode{
TElemType data;
struct CSNode *firstchild,*nextsibling;
}CSNode,*CSTree;
// 建立樹的二叉鏈表表示
Status CreateTree(CSTree& T)
{
char ch;
cout<<“輸入結點的值(一個字符,’@'表示空樹)”;
cin>>ch;
if(ch==’@’){
T=NULL;
}
else{
T=new CSNode;
if(!T) return ERROR;
T->data=ch;
CreateTree(T->firstchild);
CreateTree(T->nextsibling);
}
return OK;
}
// 輸出樹的各邊
Status ShowTree(CSTree& T,CSTree& Father)
{
if(T && Father)
cout<<"("<data<<","<data<<")";
if(T->firstchild) ShowTree(T->firstchild,T);
if(T->nextsibling) ShowTree(T->nextsibling,Father);
return OK;
}
6.60 解:
int LeafNum(CSTree& T)
{
if(T){
if(!T->firstchild)
return 1+LeafNum(T->nextsibling);
else
return LeafNum(T->firstchild)+LeafNum(T->nextsibling);
}
else return 0;
}
6.61 解:
int DegreeNum(CSTree& T)
{
int d,dl,dr;
if(T){
if(!T->firstchild) d=0;
else d=1+RSiblingNum(T->firstchild);
dl=DegreeNum(T->firstchild);
dr=DegreeNum(T->nextsibling);
return Max(d,dl,dr); // 三數中求最大者
}
else return 0;
}
// 返回當前結點的兄弟數
int RSiblingNum(CSTree& T)
{
int i=0;
while(T->nextsibling){
i++;
T=T->nextsibling;
}
return i;
}
6.62 解:
// 樹的深度
int Depth(CSTree& T)
{
int d1,d2;
if(T){
d1=1+Depth(T->firstchild);
d2=Depth(T->nextsibling);
return d1>d2?d1:d2;
}
else return 0;
}
第7章 圖
7.1 解:(1) ID(1)=3 OD(1)=0
ID(2)=2 OD(2)=2
ID(3)=1 OD(3)=2
ID(4)=1 OD(4)=3
ID(5)=2 OD(5)=1
ID(6)=2 OD(6)=3
(2) 0 0 0 0 0 0
1 0 0 1 0 0
0 1 0 0 0 1
0 0 1 0 1 1
1 0 0 0 0 0
1 1 0 0 1 0
(3)

(4)
	      
(5) 有三個連通分量1、5、2346

7.2 解:k=1,說明了各結點之間的相互連通關係;k=2說明了結點之間按路徑長度爲2的相互連通關係;…。
7.3 解:鄰接表:

鄰接多重表:

深度優先搜索的順序爲 1 5 6 4 3 2
廣度優先搜索的順序爲 1 5 6 3 2 4,		15 16 13 12 24

7.14 解:
Status CreateAG(ALGraph &G)
{
int n,e,k,i,j;
cout<<“請輸入頂點數:”;
cin>>n;
cout<<“請輸入邊數:”;
cin>>e;
G.vernum=n;
G.arcnum=e;

// 建立頂點數組
for(k=0;k<G.vernum;k++){
	cout<<"請輸入頂點信息:";
	cin>>G.vertices[k].data;
	G.vertices[k].firstarc=NULL;
}
// 建立鄰接表
VertexType v1,v2;
ArcNode *p,*q;
for(k=0;k<G.arcnum;k++){
	cout<<"請輸入弧的始點和終點信息,中間用空格分開:";
	cin>>v1>>v2;
	i=LocateVex(G,v1);
	if(i<0 || i>G.vernum-1) return ERROR;
	j=LocateVex(G,v2);
	if(j<0 || j>G.vernum-1) return ERROR;
	if(i==j) return ERROR;
	p=new ArcNode;
	if(!p) return ERROR;
	p->adjvex=j;
	p->nextarc=NULL;
	q=G.vertices[i].firstarc;
	if(!q) G.vertices[i].firstarc=p;
	else{
		while(q->nextarc) q=q->nextarc;	// 指針定位於鄰接表的尾結點
		q->nextarc=p;
	}
}
return OK;

}
int LocateVex(ALGraph& G,VertexType v)
{
int i=0;
while(G.vertices[i].data!=v&&i<G.vernum) i++;
if(G.vertices[i].data==v) return i;
else return -1;
}
7.15 解
第9章 查找
9.1 解:(1)相同,平均查找長度爲
(2)相同,平均查找長度爲
(3)對於有序順序表,平均查找長度爲 ,對於無序順序表,則爲 。
9.2 解:查找e的過程如下:

9.3 解:

9.4 解:

9.5 解:

9.7 解:

9.9 解:(1)

(3) 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章