線性表學習(4)

王小東大將軍發表於2017-06-18

1.     對稱矩陣,因此壓縮儲存可以認為是隻要儲存下三角矩陣。

 

2.     廣義表中的元素或者是一個不可分割的原子,或者是一個非空的廣義表。(錯誤。廣義表的元素可以為空。)

 

3.     既希望較快查詢又便於線性表動態變化的查詢方法是(C)

A 順序查詢 B折半查詢 C 索引查詢 D 雜湊查詢

分析:索引查詢是在索引表和主表(即線性表的索引儲存結構)上進行的查詢。索引查詢的過程是:首先根據給定的索引值K1,在索引表上查詢出索引值等於K1的索引項,以確定K1對應的子表在主表中的開始位置和長度,然後再根據給定的關鍵字K2,在對應的子表中查詢出關鍵字等於K2的元素(結點)。

 

對索引表或子表進行查詢時,若表是順序儲存的有序表,則既可進行順序查詢,也可進行二分查詢。否則只能進行順序查詢。

 

索引順序查詢又稱為分塊查詢,是介於順序查詢和二分查詢之間的一種查詢方法。

分析二:希望較快而不是很快,並且希望便於動態變化,這個用C而不是D,如果雜湊法的儲存不是鏈式,一般的情況下隨著關鍵字的增多,衝突頻繁發生,查詢效能會急劇下降,其實並不是太利於動態變化,索引順序由於一般塊內可以無序,因此塊內可以方便地減少增加。

 

4.     分析下列程式,哪個不可以表示a[1]地址:A

#include<iostream>
#include<string.h>
using namespace std;
int main() {
	int a[5]={1,2,3,4,5};
	cout<<a+sizeof(int)<<endl; // 不正確, 在32位機器上相當於指標運算 a + 4
	cout<<&a+1<<endl; // 正確,陣列首元素地址加1,根據指標運算就是a[1]的地址
	cout<<(int *)&a+1<<endl; // 正確,陣列地址被強制型別轉換為int*,然後加1,這樣和B表示的一個意思
	cout<<(int*)((char*)&a+sizeof(int));	// 正確,資料地址先被轉換為char*,然後加4,根據指標運算公式,向前移動4 * sizeof(char),之後被轉換						//為int*,顯然是a[1]的地址
	return 0;	
}

A a+sizeof(int) B &a+1 C (int *)&a+1 D (int*)((char*)&a+sizeof(int))

 

5. Name -> ID 就是 string -> int,字串最好用 Hash 或 Tree 來索引,不過由於 Name 不是唯一的,一個 Name 可能對應多個 ID,要用 Linked List 做對應過來的結構。所以可以是 Hash + Linked List 或 Tree + Linked List。(Tree 可以用 Trie,複雜度是 O(length(name)),其實 Hash 會更好,理論上覆雜度是 O(1))。
ID -> Name 就是 int(7 digit) -> string,7 位數也就是 1000 w,可以開得下(人家機子好),所以就可以隨機查了。

 

6. 1).矩陣的下三角形i>=j ;a[i][j]是第1+2+...+i-1+j=(i-1)*i/2+j個元素應該選A

2).矩陣的上三角形i<=j ;第一列存了1個,第二列存了2個,第j-1列存了j-1個,最後一列存了i個。因此,a[i][j]是第1+2+...+j-1+i=(j-1)*j/2+i個元素。

 

7.用陣列r儲存靜態連結串列,結點的next域指向後繼,工作指標j指向鏈中結點,使j沿鏈移動的操作為(A)

A j=r[j].next Bj=j+1 C j=j->nextDj=r[j]->next

分析:注意,靜態連結串列,所以j是陣列的下標。

 

用陣列r儲存靜態連結串列,結點的next域指向後繼。實際上是說,節點的next才是下一個元素,節點不是鏈中元素,節點的next才是後繼。

 

分析二:首先連結串列的下一個結點應該用next指標訪問,排除B;

當前結點是r[j],可以排除C;

 

r[j]是當前節點而不是指向當前節點的指標,因此用r[j].next;

 

8.若要刪除book表中的所有資料,以下哪些語法是錯誤的(AD)

A drop table book Btruncate table book Cdelete from book Ddelete *from book

分析:A: drop table book 是刪除整個表,題目的潛在意思是刪除表中的資料而並非刪除整個表。因此A錯。
B: truncate table book 是刪除表中的資料,刪除速度比delete更快,無法撤回(回退)。
C: delete from book  刪除資料表中的資料,可以回退,可新增where 子句。
D:語法錯誤

 

9.當用一個大小為6的陣列來實現迴圈佇列,且用rear和front的值分別為0和3.當從佇列中刪除一個元素,再加入兩個元素後,rear和front分別為: 2 和 4.

分析:1)、佇列新增元素是在對尾,刪除元素是在對頭;

2)、新增元素,尾指標rear+1;刪除元素,頭指標front+1;

3)、本題中,刪除一個元素,front+1,也就是3+1=4;新增2個元素,rear+2,也就是0+2=2;

 

10.對於class java.util.ArrayList:

元素在集合中有序,指的是元素插入過程中記錄了元素的插入順序。

 

ArrayList就是動態陣列,用MSDN中的說法,就是Array的複雜版本,它提供瞭如下一些好處: 

   1> 動態的增加和減少元素 

   2> 實現了ICollection和IList介面 

   3> 靈活的設定陣列的大小

 

Synchronized屬性指示當前的ArrayList例項是否支援執行緒同步,而ArrayList.Synchronized靜態方法則會返回一個ArrayList的執行緒同步的封裝。

 

如果使用非執行緒同步的例項,那麼在多執行緒訪問的時候,需要自己手動呼叫lock來保持執行緒同步.

 

1).陣列大小指定之後是不可變的,集合是可變的; 
2).List中元素可以重複,Set不可重複; 
3).ArrayList的儲存結構是線性的,動態分配記憶體,有序的,可以重複,優點是可以隨機訪問快; 
4).ArrayList是執行緒不安全的,不需要執行緒同步;

 

 

ArrayList和Vector是類似的: 
(1)兩者都是基於索引的,內部有一個陣列支援 
(2)兩者都維護插入的順序 
(3)兩者都允許null值 
不同之處在於: 
(1)ArrayList是不同步的,而Vector是同步的,所以ArrayList更高效,不會過載 
(2)ArrayList更加通用,因為我們可以使用Collections工具類輕易地獲取同步列表和只讀列表,當需要在迭代的時候對列表進行改變,你應該使用CopyOnWriteArrayList 

 

 

11.對長度為n的線性表進行順序查詢,在最壞情況下所需要的比較次數為:n

分析:最壞情況就是需要查詢的數在最後一個或者不存在,那麼比較次數就是n。

 

12. 若在記憶體中,則資料可以”隨機存取”,但記憶體資料被讀取或寫入時,所需要的時間與這段資訊所在的位置無關.但是在讀取和寫入磁碟時,其所需要的時間與位置就會有關係.因為在BASIC,PASCAL和C/C++語言中,陣列的存放是按照行優先來存放的,按行號第一行第二行…以此類推.本體關鍵是考察記憶體抖動的問題,如果按列訪問則需要跳過一大串記憶體地址,這樣可能需求的記憶體地址不在當前頁中則需要進行頁置換,這樣便需要硬碟IO,減低速度。

當陣列過大的時候CPU無法一次性讀取整個記憶體頁面,這時候訪問資料的時候就會發生頁面置換,如果以列序來訪問就會發生頻繁的置換,這時候就會消耗大量的時間。(跨列訪問虛存可能發生頁分裂,頁表頻繁切換導致效率降低)

 

如果陣列很大的話應該是行優先快,因為陣列在記憶體中是按行優先儲存的,在虛存環境下,如果整個陣列沒有在記憶體中的話可以比列優先減少記憶體換進換出的次數。就算整個陣列都在記憶體中,列優先訪問a[i][j]還得計算一次乘法,行優先只需加一就可以了,這個可以忽略。

 

13. 陣列指標(也稱行指標) 

定義 int (*p)[n];

()優先順序高,首先說明p是一個指標,指向一個整型的一維陣列,這個一維陣列的長度是n,也可以說是p的步長。也就是說執行p+1時,p要跨過n個整型資料的長度。

 

如要將二維陣列賦給一指標,應這樣賦值:

int a[3][4];

int (*p)[4]; //該語句是定義一個陣列指標,指向含4個元素的一維陣列。

 p=a;//將該二維陣列的首地址賦給p,也就是a[0]或&a[0][0]

 p++;//該語句執行過後,也就是p=p+1;p跨過行a[0][]指向了行a[1][]。

所以陣列指標也稱指向一維陣列的指標,亦稱行指標。

 

指標陣列 

定義 int *p[n];

[]優先順序高,先與p結合成為一個陣列,再由int*說明這是一個整型指標陣列,它有n個指標型別的陣列元素。這裡執行p+1是錯誤的,這樣賦值也是錯誤的:p=a;因為p是個不可知的表示,只存在p[0]、p[1]、p[2]...p[n-1],而且它們分別是指標變數可以用來存放變數地址。但可以這樣*p=a; 這裡*p表示指標陣列第一個元素的值,a的首地址的值。

如要將二維陣列賦給一指標陣列:

int *p[3];

int a[3][4];

for(i=0;i<3;i++)

p[i]=a[i];

這裡int *p[3] 表示一個一維陣列記憶體放著三個指標變數,分別是p[0]、p[1]、p[2]

所以要分別賦值。

 

這樣兩者的區別就豁然開朗了,陣列指標只是一個指標變數,似乎是C語言裡專門用來指向二維陣列的,它佔有記憶體中一個指標的儲存空間。指標陣列是多個指標變數,以陣列形式存在記憶體當中,佔有多個指標的儲存空間

還需要說明的一點就是,同時用來指向二維陣列時,其引用和用陣列名引用都是一樣的。

比如要表示陣列中i行j列一個元素:

*(p[i]+j)、*(*(p+i)+j)、(*(p+i))[j]、p[i][j]

 

14. 在java 中,宣告一個陣列時,不能直接限定陣列長度,只有在建立例項化物件時,才能對給定陣列長度.。

如下,1,2,3可以通過編譯,4,5不行。而String是Object的子類,所以上述BCF均可定義一個存放50個String型別物件的陣列。

1. String a[]=new String[50];

2. String b[];

3. char c[];

4. String d[50];

5. char e[50];

 

A:char[][] 定義了二位字元陣列。在Java中,使用字串對char陣列賦值,必須使用toCharArray()方法進行轉換。所以A錯誤。

B、C:在Java中定義String陣列,有兩種定義方式:String a[]和String[] a。所以B、C正確。

D、E:陣列是一個引用型別變數,因此使用它定義一個變數時,僅僅定義了一個變數 ,這個引用變數還未指向任何有效的記憶體 ,因此定義陣列不能指定陣列的長度。所以D、E錯誤。

F:Object類是所有類的父類。子類其實是一種特殊的父類,因此子類物件可以直接賦值給父類引用變數,無須強制轉換,這也被稱為向上轉型。這體現了多型的思想。所以F正確。

 

15.下列說法正確的是(ABD)

A 二維以上的陣列是一種特殊的廣義表

B 陣列一旦建立,結構的元素個數和元素間的關係就不再變

C 陣列是一種線性結構,因此只能用來儲存線性表

D 陣列採用順序儲存方式表示

分析:需要區分資料的物理結構與邏輯結構。物理結構主要是指儲存方式,包含線性儲存與鏈式儲存,它是從計算機儲存的角度去考慮。邏輯結構指的是資料之間的關係,有線性關係和鏈式關係等,主要是從人為定義角度去考慮。陣列是一種被人們定義為線性關係的表,至於其儲存結構,可以用線性儲存也可以是鏈式儲存去儲存。

 

16.順序儲存結構的儲存一定是連續的;鏈式儲存結構的儲存空間不一定是連續的,可以是連續的。

鏈式儲存結構中每個結點都由資料域與指標域兩部分組成,增加了儲存空間。

 

17.給定一個m行n列的整數矩陣,每行從左到右和每列從上到下都是有序的。判斷一個整數k是否在矩陣中出現的最優演算法,在最壞情況下的時間複雜度為:o(m+n)

分析:首先,按題中要求所得矩陣的左上角和右下角元素分別為整個矩陣的最小值和最大值,這倆個點是矩陣的鞍點

 

下面是最優演算法:

記矩陣的右上角(左下角也可以)元素為a,搜尋起點設定為a,要查詢的元素為k:

若a>k,則a所在列的所有元素均大於k,搜尋位置左移1位,然後刪除該列構成新的矩陣;

若a<k,則a所在行的所有元素均小於k,搜尋位置下移1位,然後刪除該行構成新的矩陣;

若相等,結束查詢;

由新構成的矩陣利用上述方式繼續查詢(遞迴呼叫)。

本題中,該最優演算法的最壞情況也就是說從右上角開始搜尋直到左下角結束,每次向左或向下一步,共需要m+n步到達左下角。

 

18.在順序表中訪問任意一個結點的時間複雜度均為O(1),因此順序表也稱為隨機存取的資料結構。

相關文章