データ構造 (C 言語) 第 2 版 第 4 章 解答
1~5 BBCAB
6~10 BBCBB
11~15 ABD (C,B) C
1. 複数の選択肢
(1) 文字列は特別な線形テーブルであり、その特殊性は (B)。
A.連続して保存できるB. データ要素は単一の文字
Cです。連鎖ストレージが可能 D. データ要素は複数の文字にすることができます。
String (string の略) は、データ要素が 1 文字のみで構成される特別な線形テーブルです。
(2) 文字列 以下の文字列の説明では、(B) 間違っています。
A.
文字列は文字Bの有限シーケンスです。空文字列はスペース
Cで構成される文字列です。
パターン マッチングは文字列Dに対する重要な操作です。文字列は連続して保存することも、連鎖的に保存することもできます
ゼロ文字の文字列は空文字列と呼ばれ、長さはゼロです。
スペースは、文字列の文字セットの要素です。
(3) 文字列「ababaaababaa」の次の配列は (C)
。012345678999 B.012121111212
C.011234223456 D.0123012322345
文字列の次の関数値を計算するには、「KMP パターン マッチング アルゴリズム」を参照してください。
計算プロセス:
添字 j: 1 2 3 4 5 6 7 8 9 10 11 12
文字列: ababaaababaa
next[ j ] 0 1 1 2 3 4 2 2 3 4 5 6
1) j=1 の場合、next[1]=0 に固定;
2) j=2 の場合、1 から j-1 までの文字列は他の文字列に属する "a" next[2]=1;
3) j=3 のとき、1 から j-1 までの文字列は "ab" であり、接頭辞文字 "a" と接尾辞文字 "b" は等しくありません。他の場合に属するので、 next[3]=1;
4) j=4 の場合、1 から j-1 までの文字列は "aba" であり、接頭辞文字 "a" は接尾辞文字 "a" に等しい"、つまり 1 文字が等しい、したがって、next[4]=1+1=2;
5) j=5 の場合、1 から j-1 までの文字列は "abab"、接頭辞文字 "ab" は接尾辞文字「ab」と等しい、つまり等しい文字が 2 つあるので、next[5]=2+1=3; 6
) j=6 の場合、1 から j-1 までの文字列は「ababa」、接頭文字「aba」と接尾文字「aba」が等しい、つまり等しい文字が 3 つあるので、 next[6]=3+1=4; 7) j=7 のとき、1 から j までの文字
列-1 は「ababaa」で、接頭辞文字「a」は接尾辞文字「a」と等しい、つまり 1 文字が等しいため、
next[7]=1+1=2; 8) j=8 のとき、1 から j-1 までの文字列は "ababaaa" であり、接頭辞文字 "a" は接尾辞文字 "a" に等しい、つまり、等しい文字は 1 つあるため、next[8]=1+1=2;
9) j=9 の場合、1 から j-1 までの文字列は「ababaaab」で、接頭文字「ab」と接尾文字「ab」は等しい、つまり等しい文字が 2 つあるため、next[9 ]= 2+1=3;
10) j=10のとき、1からj-1までの文字列は「ababaaaba」で、接頭文字「aba」と接尾文字「aba」は等しい、つまり3つあります。文字が等しいので、 next[10]=3+1=4;
11) j=11 の場合、1 から j-1 までの文字列は "ababaaaabab" であり、接頭辞文字 "abab" は接尾辞文字 "abab" に等しい"、つまり等しい文字が 4 つあるので、next[11]=4+1=5;
12) j=12 の場合、1 から j-1 までの文字列は "ababaaababa"、接頭辞文字 "ababa" " と接尾辞文字 "ababa "は等しい。つまり、等しい文字が 5 つあるため、next[12]=5+1=6;
(4) 文字列「ababaabab」の nextval は (あ)
。010104101 B.010102101C
.010100011 D .010101011
添字 i: 1 2 3 4 5 6 7 8 9
文字列 s: ababaabab
next [ i ]: 0 1 1 2 3 4 2 3 4
nextval[i]: 0 1 0 1 0 4 1 0 1
まず次のプレフィックスを計算します。 [i] の値:
文字列の次の関数値を計算します。「KMP パターン マッチング アルゴリズム」を参照してください。
next [ i ]: 0 1 1 2 3 4 2 3 4
次に、nextval[i] の値を計算します。
最初の桁 nextval の値は 0 でなければなりません。2 番目のビットは、最初のビットと同じ場合は 0、異なる場合は 1 になります。
3 番目のビットの次の値が 1 で、3 番目のビットと最初のビットを比較し、両方とも a で同じである場合、3 番目のビットの nextval 値は 0 になります。
4 番目のビットの次の値は 2 で、4 番目のビットと 2 番目のビットを比較します。それらが同じである場合、2 番目のビットの次の値が 1 である場合、2 番目のビットと最初のビットの比較を続けます。それらが異なる場合、4 番目のビットです。ビットの nextval 値は 2 番目のビットの次の値、つまり 1 です。
5 桁目の次の値は 3 で、5 桁と 3 桁を比較すると同じです。3 桁目の次の値は 1 で、一致する場合は 3 桁と最初の桁の比較を続けます。ビットの nextval 値は、最初のビットの次の値 (0) です。
6 番目のビットの次の値は 4 で、6 番目のビットと 4 番目のビットを比較し、異なる場合は、6 番目のビットの次の値が 4 になります。
7 番目のビットの次の値が 2 で、7 番目のビットと 2 番目のビットを比較し、それらが同じである場合、2 番目のビットの次の値が 1 である場合、2 番目のビットと最初のビットの比較を続けます。それらは異なり、次に 7 番目のビットの nextval 値は 2 番目のビットの次の値、つまり 1 です。
8 番目のビットの次の値は 3 で、8 番目のビットと 3 番目のビットを比較すると同じです。次の 3 番目のビットの値は 1 で、同じであれば 3 番目のビットと最初のビットの比較を続けます。 8 番目のビットの nextval 値は、最初のビットの次の値 (0) です。
9 番目のビットの次の値は 4 で、9 番目のビットと 4 番目のビットを比較します。それらは同じです。4 番目のビットの次の値は 2 で、それらが同じであれば、4 番目のビットと 2 番目のビットの比較を続けます。同じ、次に 8 番目 ビットの nextval 値は、2 番目のビットの次の値、つまり 1 です。
nextval は 0,1,0,1,0,4,1,0,1 です
(5) 文字列の長さは (B)。
A.
文字列B内の異なる文字の数。
文字列Cの文字数。
文字列Dに含まれる異なる文字の数。文字列内の空白以外の文字の数
文字列内の文字数を文字列の長さと呼びます。
(6) 2 次元配列 A=array[1…100,1…100] が主シーケンスとして行順に格納され、各データ要素が 2 つの記憶ユニットを占有し、ベース アドレスが 10 であると仮定すると、LOC [5,5] =(B
)。808 B.818C.1010D .1020
LOC ( i , j ) = ベース アドレス + ( ( i - 1 ) * m + j ) * L (L は各要素が占有する記憶単位) は行順序に基づき、LOC[5,5]=[
( 5-1)*100+(5-1)]*2+10=818。
(7) 配列 A[i,j] があり、配列の各要素の長さは 3 バイト、i の値は 1 ~ 8、j の値は 1 ~ 10 で、配列が格納されますメモリの先頭アドレス BA から順に列ベースのストレージを使用する場合、要素 A[5,8] のストレージ先頭アドレスは (B).A
.BA+141 B .BA+180C
.BA+222 D.BA+225
LOC ( i , j ) = ベース アドレス + ( ( j - 1 ) * m + i ) * L (L は各要素が占有する記憶単位) は列順序に基づき、LOC[5,8]=[
( 8-1)*8+(5-1)]*3+BA=BA+180。
(8) 次数 10 の対称行列 A があります。これは、圧縮記憶方式を採用し、主記憶として行順序を使用します。a11 が最初の要素で、その記憶アドレスは 1 で、各要素はアドレス空間を占有します。 a85 のアドレスは (C) 。
A.13 B.32 C.33 D.40
これはk=0,1...から計算され、質問に格納されている最初の要素a11のアドレスは1なので、得られた結果はプラス1
(9) n 次の対称行列 A の場合、下三角形の要素 (主対角上のすべての要素を含む) は 1 次元配列 B[1...(n(n+1) )/ に格納されます。 2] の場合、B における aij ( i<j ) の位置 k を決定する関係は (B
)。i*(i-1)/2+j B.j*(j-1)/2+i
C.i*(i+1)/2+j D.j*(j+1)/2+i
質問では、1 次元配列 B[1...(n(n+1))/2] に順番に格納します。k は 1 から始まることがわかっているため、次の式の結果に 1 を加えます。 i<j なので、 k = j * ( j - 1 ) / 2 + i となります。
(10) 2 次元配列 A の各要素は、行添字 i=0,1, ..., ,8、列添字 j=1,2, ...,10 の 10 文字で構成される文字列です。A が行から先に格納される場合、要素 A[8,5] の開始アドレスは、A が列から先に格納される場合の要素と同じになります (B)同じ開始アドレスを持つ。各文字が 1 バイトを占めるようにします。
A.A[8,5] B.A[3,10] C. A[5,8] D.A[0,9]
配列がメモリの最初のアドレス M から順に格納されると仮定します。配列が行から先に格納される場合、要素 A[8,5] の開始アドレスは次のようになります: M+[ ( 8-0 ) *10+ ( 5 -1 ) ]*1=M +84 ; 配列が最初に列ごとに格納される場合、要素 A[3,10] の開始アドレスは次のように簡単に計算できます: M+[ ( 10-1 ) *9+( 3 -0 ) ]*1=M+84 。
(11) 次のようにして、2 次元配列 A[1... m , 1... n] (つまり m 行 n 列) を配列 B[1... m*n] 行に格納します。行、次に 2 次元配列要素 A[i,j] 1 次元配列 B の添字は (あ)
。(i-1) n+j B.(i-1) n+j-1 C.i (j-1) D .jm +i-1
行ごとに保存すると、合計 (i - 1) 行と j 個の要素がいっぱいになります。
(12) 配列に含まれる要素の数 A[0…4,-1…-3,5…7] (B) 。
A.55 B .45C.36 D.16
合計 5 3 3=45 個の要素を持つ3 次元配列。
(13) 一般化テーブル A=(a,b,(c,d),(e,(f,g))) の場合、Head(Tail(Head(Tail(Tail(A))))) の値は次のようになります。 (D) 。
A.(g) B .(d) C.CD。d
テール(A)=(b,(c,d),(e,(f,g))) ; テール(テール(A))=( (c,d),(e,(f,g))) ; 頭(尾(尾(A)))=(c,d) ; 尾(頭(尾(尾(A))))=(d) ; 頭(尾(頭(尾(尾(A))) ))=d 。
(14) 一般化テーブル ((a,b,c,d)) の先頭は (C)、尾は(B) 。
A.a B .( )C .(a,b,c,d) D .(b、c、d)
ヘッダーは空ではない一般化テーブルの最初の要素であり、単一のアトムまたはサブリストにすることができます。((a,b,c,d)) のヘッダーはサブリスト (a,b,c ,d) です。 ; 末尾はヘッダーを除く他の要素で構成されるリストであり、テーブルは一般化されたリストでなければならず、((a,b,c,d)) の末尾は空のリスト ( ) です。
(15) 一般化テーブル L=((a,b,c)) とすると、L の長さと深さはそれぞれ (C)。
A.1と1B.1と3C。1 と 2D.2と3
一般化テーブルの深さは、拡張テーブルに含まれる括弧の層の数を指し、一般化テーブルの長さは、一般化テーブルに含まれる要素の数を指します。定義によれば、L の長さが 1、深さが 2 であることが簡単にわかります。
2. 応募に関する質問
(1) パターン文字列 t=' abcabbabcab ' が与えられた場合、KMP メソッドで得られた各文字に対応する next および nextval 関数の値を書き込みます。
/*
模式串t 的next 和nextval 值如下:
下标j 1 2 3 4 5 6 7 8 9 10 11 12
模式串t a b c a a b b a b c a b
next[j] 0 1 1 1 2 2 3 1 2 3 4 5
nextval[j] 0 1 1 0 2 1 3 0 1 1 0 5
*/
(2) ターゲットを t=“abcaabbabcabaacbacba”、モードを p=“abcabaa”とする
①モード p の naxtval 関数値を計算する;
②アルゴリズムは記述せず、KMP 使用時の各トリップのマッチング過程のみを描画するパターンマッチングのアルゴリズム。
/*
1.p 的nextval 函数值为0110132 (p 的next 函数值为0111232)
2.利用KMP(改进的nextval) 算法,每趟匹配过程如下
第一趟匹配: abcaabbabcabaacbacba
abcab(i=5,j=5)
第二趟匹配: abcaabbabcabaacbacba
abc(i=7,j=3)
第三趟匹配: abcaabbabcabaacbacba
a(i=7,j=1)
第四趟匹配: abcaabbabcabaac bacba
( 成功) abcabaa(i=15,j=8)
*/
(3) 配列 A では、各要素 A[i,j] の長さは 32 バイナリ ビットで、行の添字は -1 ~ 9、列の添字は 1 ~ 11、メモリ内のメインのメインメモリのワード長は 16 ビットです。質問:
① 配列を保存するには何ユニット必要ですか?
②配列の4列目の要素をすべて格納するにはセルは何個必要ですか?
③配列を行単位で格納する場合、要素A[7,4]の開始アドレスは何になりますか?
④ 配列を列に格納する場合、要素 A[4,7] の開始アドレスは何になりますか?
各要素は 32 バイナリ ビットを持ち、メイン メモリのワード長は 16 ビットであるため、各要素は 2 ワードを占有し、行の添字は 1 から 11 にシフトできます。
(1) 242 (2) 22 (3) s+182 (4) s+142
(4) H( ) ― Head( ), T( ) ― Tail( ) のツールを使って L からバナナを取り出してください。
L=(リンゴ,(オレンジ,(イチゴ,(バナナ)),桃),梨)
答え: H( H( T( H( T( H( T( L))))))
3. アルゴリズム設計に関する質問
(1) 入力文字列内のさまざまな文字の出現頻度をカウントし、結果をファイルに保存するアルゴリズムを作成します (文字列内の有効な文字は、A ~ Z の 26 文字と 0 ~ 9 の 10 個の数字です)。
[ トピック分析 ]
合計 26 個の文字と 10 個の数字があるため、合計 36 個の数字があるため、長さ 36 の整数配列を設定します。最初の 10 個のコンポーネントは数字の出現数を格納し、残りは数字を格納します。文字の出現。文字列から数字を読み取る場合、その文字の ASCII コード値が数字「0」の ASCII コード値から減算されてその値 (0...9) が得られ、文字の ASCII コード値が得られます。文字「A」の ASCII 値から減算されます。コード値に 10 を加算し、配列の対応する添字コンポーネントに格納します。他の記号が見つかった場合、入力文字列の終わりまで処理されません。
[ アルゴリズムの説明 ]
void Count (){
// 统计输入字符串中数字字符和字母字符的个数。
int i, num[36];
char ch ;
for ( i = 0; i<36 ; i++ ) num[i] =0; // 初始化
while ((ch = getchar()) != '#'){
// ‘ # ’表示输入字符串结束。
if ('0'<=ch<=' 9'){
i=ch - 48;num[i]++ ;} // 数字字符
else if ('A' <=ch<= 'Z'){
i=ch-65+10;num[i]++;} // 字母字符
}
for (i=0 ; i<10 ;i++ ) // 输出数字字符的个数
cout<<"数字" <<i<< "的个数="<<num[i]<<endl;
for (i = 10; i<36 ; i++ ) // 求出字母字符的个数
cout<<"字母字符" <<i+55<< "的个数="<<num[i]<<endl;
}
(2) 追加の文字列記憶領域を必要とせず、文字列を逆の順序で格納する再帰アルゴリズムを作成します。
[ トピック分析 ]
文字列の反転を実現することは難しくありませんが、この質問では、文字列の逆順の格納、つまり最初の入力文字が最後に格納され、最後の入力文字が最初に格納されることを実現するために「追加の文字列格納スペースは必要ありません」 、これは再帰到着を使用することで簡単に行うことができます。
[ アルゴリズムの説明 ]
void InvertStore( char A[]){
// 字符串逆序存储的递归算法。
char ch;
static int i = 0; // 需要使用静态变量
cin>>ch;
if (ch!= '.') {
// 规定'.' 是字符串输入结束标志
InvertStore(A);
A[i++] = ch; // 字符串逆序存储
}
A[i] = '\0'; // 字符串结尾标记
}
(3) 以下の関数の機能を実現するアルゴリズムを記述します。関数 void insert(char s,char t,int pos) は、文字列 t を文字列 s の pos に挿入します。文字列 s に割り当てられたスペースは、文字列 t を挿入するのに十分であると仮定します。(注: ライブラリ関数は使用しないでください)
[タイトル分析]
この問題は文字列挿入問題であり、文字列 s の位置に文字列 t を挿入する必要があります。まず、文字列 s の位置を検索し、部分文字列を文字列 t の長さ分だけ後方に移動し、文字列 t の位置にコピーします。文字列 s.
挿入位置 pos の正当性を検証するには、文字列 s の長さが 1 未満または s より大きい場合は不正です。タイトルでは文字列 s のスペースが十分に大きいことを前提としているため、挿入時にオーバーフローを判断する必要はありません。 。
[ アルゴリズムの説明 ]
void insert(char *s,char *t,int pos){
// 将字符串t 插入字符串s 的第pos 个位置。
int i=1,x=0;
//p , q 分别为字符串s 和t 的工作指针
char *p=s,*q=t;
if(pos<1) {
cout<< "pos 参数位置非法" <<endl;exit(0);}
while(*p!= '0'&&i<pos) {
p++;i++;} // 查pos 位置
// 若pos 小于串s 长度,则查到pos 位置时, i=pos 。
if(*p == '/0') {
cout<<pos<<" 位置大于字符串s 的长度";exit(0);}
else // 查找字符串的尾
while(*p!= '/0') {
p++; i++;} // 查到尾时, i 为字符‘ \0 ’的下标, p 也指向‘ \0 ’。
while(*q!= '\0') {
q++; x++; } // 查找字符串t 的长度x,循环结束时q 指向'\0' 。
for(j=i;j>=pos ;j--){
*(p+x)=*p; p--;}// 串s 的pos 后的子串右移,空出串t 的位置。
q--; // 指针q 回退到串t 的最后一个字符
for(j=1;j<=x;j++) *p--=*q--; // 将t 串插入到s 的pos 位置上
}
(4) 英語の一部が既知の文字列 S1 に格納され、アルゴリズム format(s1,s2,s3,n) が記述されて、指定された長さ n に従って両端が揃えられた文字列 S2 にフォーマットされます。余分な文字は S3 に送信されます。
[ トピック分析 ]
この質問では、文字列 s1 を文字列 s2 と文字列 s3 に分割する必要があり、文字列 s2 は「指定された長さ n に従って
両端が揃えられた文字列にフォーマットされる」必要があります。つまり、長さは n で、最初と最後の文字はスペース文字であってはなりません。このアルゴリズムは、文字列 s1 を左から右にスキャンし、最初の非スペース文字を見つけて n まで数えます。文字列 s2 にコピーされる n 番目の文字はスペースであってはなりません。その後、残りの文字を文字列 s3 にコピーします。
[ アルゴリズムの説明 ]
void format (char *s1,*s2,*s3){
// 将字符串s1 拆分成字符串s2 和字符串s3 ,要求字符串s2 是长n 且两端对齐
char *p=s1, *q=s2;
int i=0;
while(*p!= '\0' && *p== ' ') p++;// 滤掉s1 左端空格
if(*p== '\0') {
cout<<" 字符串s1 为空串或空格串"<<endl;exit(0); }
while( *p!='\0' && i<n){
*q=*p; q++; p++; i++;}
// 字符串s1 向字符串s2 中复制
if(*p =='\0'){
cout<<" 字符串s1 没有"<<n<<" 个有效字符"<<endl; exit(0);}
if(*(--q)==' ' ){
// 若最后一个字符为空格,则需向后找到第一个非空格字符
p-- ; //p 指针也后退
while(*p==' '&&*p!='\0') p++;// 往后查找一个非空格字符作串s2 的尾字符
if(*p=='\0'){
cout<<"s1 串没有"<<n<<" 个两端对齐的字符串"<<endl; exit(0);}
*q=*p; // 字符串s2 最后一个非空字符
*(++q)='\0'; // 置s2 字符串结束标记
}
*q=s3;p++; // 将s1 串其余部分送字符串s3 。
while (*p!= '\0') {
*q=*p; q++; p++;}
*q='\0'; // 置串s3 结束标记
}
(5) 2 次元配列 a[1…m, 1…n] に m*n 個の整数が含まれるとします。
① a のすべての要素が互いに異なるかどうかを判断するアルゴリズムを作成しますか? 関連情報を出力します (はい/いいえ);
② アルゴリズムの時間計算量を分析してみます。
[ トピック分析 ]
2 次元配列の要素が異なるかどうかを判断するには、要素を 1 つずつ比較し、等しい要素のペアを見つけるだけで、それらは互いに異なっていないと結論付けることができます。各要素を他の要素と一度だけ比較するにはどうすればよいでしょうか? 現在の行では、各要素がこの行の後ろの要素 (以下の最初のループ制御変数 p の for ループ) と一度比較され、その後、i+1 行目以降の要素と比較されます。ループ制御変数 k と p の 2 レベルの for ループ。
[ アルゴリズムの説明 ]
int JudgEqual(ing a[m][n],int m,n){
// 判断二维数组中所有元素是否互不相同,如是,返回1;否则,返回0。
for(i=0;i<m;i++){
for(j=0;j<n-1;j++){
for(p=j+1;p<n;p++) // 和同行其它元素比较
if(a[i][j]==a[i][p]) {
cout<< "no" ; return(0); }
// 只要有一个相同的,就结论不是互不相同
for(k=i+1;k<m;k++) // 和第i+1 行及以后元素比较
for(p=0;p<n;p++)
if(a[i][j]==a[k][p]) {
cout<<"no" ; return(0); }
}
}
cout<< "yes" ;
return(1); // 元素互不相同
}
2 次元配列の各要素は他の要素と 1 回比較されます。配列には m n 個の要素があり、最初の要素は他の m n-1 要素と比較され、2 番目の要素は他の m n-2 要素と比較されます。 、、、m n-1 番目の要素と最後の要素 (m n) が 1 回比較されるため、要素が互いに等しくない場合、合計の比較回数は (m n -1)+(m n-2)+となります。 、+2+ 1=(m n)(m n-1)/2。同じ要素がある場合、最初の比較で同じである場合もあれば、最後の比較で同じである場合もあり、(m n -1) 個の位置が同じであるとして、このときの平均比較回数合計時間は約 ( m n )( m n-1)/4 です。
複雑さは O(n 4) です。
(6) 任意の n 個の整数が配列 A(1:n) に格納されていると仮定して、すべての正の数をすべての負の数の前に配置するアルゴリズムを作成してみます (アルゴリズムの複雑さは 0(n) である必要があります) )。
[ トピック分析 ]
この質問は並べ替えの問題であり、正と負のみであり、サイズではありません。配列の先頭と最後に2つのポインタiとjを設定でき、iは小さい値から大きい値、負の数まで検索して停止し、jは大きい値から小さい値、正の数まで検索して停止します。次に、i と j が指すデータを交換し、i=j になるまで上記の処理を続けます。
[ アルゴリズムの説明 ]
void Arrange(int A[],int n){
//n 个整数存于数组A 中,本算法将数组中所有正数排在所有负数的前面
int i=0,j=n-1,x;
while(i<j){
while(i<j && A[i]>0) i++;
while(i<j && A[j]<0) j--;
if(i<j) {
x=A[i]; A[i++]=A[j]; A[j--]=x; } // 交换A[i] 与A[j]
}
}