重新系統的過一遍數據結構
現在先回顧下它的定義
#define MaxSize 50 //定義線性表的最大長度
typedef struct{
ElemType data[MaxSize]; //順序表的元素
int length; //順序表的當前長度
}SqList; //順序表的類型定義
#define InitSize = 100 //表長度的初始定義
typedef struct{
ElemType *data; //只是動態分配數組的指針
int MaxSize,length; //數組的最大容量和當前個數
}SqList; //動態分配數組順序表的類型定義
注意,題中如果沒有強調有序順序表,那麼默認無序 比如下面的第四題
一維數組可以是靜態分配的,也可以是動態分配的。在靜態分配時,由於數組的大小和空間實現已經固定,一旦空間佔滿,再加入新的數據將產生溢出,就會導致程序崩潰。
而動態分配時,儲存數組的空間實在程序執行過程中通過動態存儲分配語句分配的,一旦數據空間佔滿,可以另外開闢一塊更大的存儲空間,用以替換原來的存儲空間,從而達到擴充存儲數組空間的目的,而不需要一次性地劃分所有所需空間給線性表。
1. 從順序表中刪除具有最小值的元素(假設唯一)並由函數返回被刪元素的值。空出的位置由最後一個元素填補,若順序表爲空則顯示出錯誤信息並退出運行。
2. 設計一個高效算法,將順序表的所有元素你只,要求算法的空間複雜度爲O(1)。
3. 長度爲n的順序表L,編寫一個時間複雜度爲O(n)、空間複雜度爲O(1)的算法,該算法刪除線性表中所有值爲x的數據元素。
4. 從有序順序表中刪除其值在給定值s和t之間(要求s < t)的所有元素,如果s或t不合理或者順序表爲空則顯示出錯信息並退出運行。
5. 從順序表中刪除其值在給定值s與t之間(包含s和t,要求s < t)的所有元素,如果s或t不合理或者順序表爲空則顯示出錯誤信息並退出運行。
6. 從有序順序表中刪除所有其值重複的元素,使表中所有元素的值均不同。
7. 將兩個有序順序表合併成一個新的有序順序表,並由函數返回結果順序表。
8. 已知在一維數組A[m+n]中一次存放着兩個線性表(a1,a2,a3,…,am)和(b1,b2,b3,…,bn)。試編寫一個函數,將數組中兩個順序表的位置互換,即將(b1,b2,b3,…,bn)放在(a1,a2,a3,…,am)的前面。
9. 線性表(a1,a2,a3,…,an)中元素遞增有序且按順序存儲於計算機內。要求設計一算法完成用最少時間在表中查找數值爲x的元素,若找到將其與後繼元素位置相交換,若找不到將其插入表中並使表中元素扔遞增有序。
10. 設將n(n>1)個整數存放到一維數組R中,試設計一個在時間和空間兩方面都儘可能搞笑的算法。將R中保存的序列循環左移p(0 < p < n)個位置,即將R中的數據由(X0,X1,…,Xn-1`)變換爲(Xp,Xp+1,…,Xn-1,X0,X1,…,Xp-1)。要求:
1)給出算法的基本設計思想
2)根據設計思想,採用C或++或Java語言描述算法,關鍵之處給出註釋。
3)說明你所設計算法的時間複雜度和空間複雜度
11. 一個長度爲L(L≥1)的升序序列S,處在第[L/2]個位置的數稱爲S的中位數。例如,若序列S1=(11,13,15,17,19),則S1的中位數是15,兩個序列的中位數是含它們所有元素的升序序列的中位數。例如,若S2=(2,4,6,8,20),則S1和S2的中位數是11。現在又兩個等長升序序列A和B,試設計一個在時間和空間兩方面都儘可能高效的算法,找出兩個序列A和B的中位數,要求:
1)給出算法的基本設計思想
2)根據設計思想,採用C或++或Java語言描述算法,關鍵之處給出註釋。
3)說明你所設計算法的時間複雜度和空間複雜度
12.已知一個整數序列A=(a0,a1,…,an-1),其中0≤ai≤n(0 ≤ i< n)。若存在ap1=ap2=…=apm=x且m > n / 2 ( 0 ≤ pk < n,1 ≤ k ≤ m),則稱x爲A的主元素。例如A = (0,5,5,3,5,7,5,5),則5爲主元素;又如A=(0,5,5,3,5,1,5,7),則A中沒有主元素。假設A中的n個元素保存在一個一維數組中,請設計一個儘可能高效的算法,找出A的主元素。若存在主元素,則輸出鈣元素;否則輸出-1。要求:
1)給出算法的基本設計思想
2)根據設計思想,採用C或++或Java語言描述算法,關鍵之處給出註釋。
3)說明你所設計算法的時間複雜度和空間複雜度
解答部分
第一題
思想:搜索整個順序表,查找最小值元素並記住其位置,搜索結束後用最後一個元素填補空出的原最小值元素位置。
bool Del_Min(sqList &L,int &value) {
//刪除順序表L中最小值元素結點,並通過引用性參數value返回其值
//如果刪除成功,返回true;否則,返回false
if(L.length == 0)
return false; //表空
value = L.data[0];
int pos = 0; //假設第一個元素是最小的
for(int i =1;i<L.length;i++) //循環,尋找具有最小值的元素
if(L.data[i]<value){ //讓value記憶當前具有最小值的元素
value = L.data[i];
pos = i;
}
L.data[pos] = L.data[L.length-1];//空出的位置由最後一個元素填補
L.length--;
return ture; //辭職時,value即爲最小值
}
第二題
算法思想:掃描順序表L的前半部分元素,對於元素L.data[i] (0 ≤ i < L.length/2),將其餘後半部分對應元素L.data[L.length-i-1]進行交換。
void Reverse(SqList &L) {
Elemtype temp; //輔助變量
for(i=0;i<L.length/2;i++) {
temp = L.data[i]; //交換L.dta[i]與L.data[L.length-i-1]
L.data[i] = L.data[L.length-i-1];
L.data[L.length-i-1]=temp;
}
}
其實經過測試,表內元素是奇數或者偶數時都符合條件,所以可以當做一個典型題來記
第三題
有兩種方法:
第一種:
用k記錄順序表L中不等於x的元素個數(即需要保存的元素個數),邊掃描L邊統計k,並將不等於x的元素向前放置k位置上,最後修改L的長度
void del_x_1(SqList &L,Elemtype x){
//本算法實現刪除順序表L中所有值爲x的數據元素
int k = 0 ; //記錄值不等於x的元素個數
for(i=0;i<L.length;i++){
if(L.data[i]! = x){
L.data[k]=L.data[i];
k++; //不等於x的元素增1
}
}
L.length=k; //順序表L的長度等於k
}
第二種:
用k記錄順序表L中等於x的元素個數,邊掃描L邊統計k,並將不等於x的元素前移k個位置,最後修改L的長度。
void del_x_2(SqList &L,Elemtype x) {
int k =0,i=0; //k記錄值等於x的元素個數
while(i<L.length) {
if(L.data[i]==x)
k++;
else
L.data[i-k]=L.data[i]; //當前元素前移k個位置
i++;
}
L.length = L.length-k; //順序表L的長度遞減
}
第四題
注意:和上面不同,因爲是有序表,所以刪除的元素必然是相連的整體
思想:先尋找大於等於s的第一個元素(第一個刪除的元素),然後尋找值大於t的第一個元素(最後一個刪除的元素的下一個元素),要將這段元素刪除,則只需直接將後面的元素前移即可
boo Del_s_t2(sqList &L,ElemTyep s, ElemType t){
//刪除有序順序表L中值在給定值s與t之間的所有元素
int i,j;
if((s>=t)||L.length==0)
return false;
for(i=0;i<L.length&&L.data[i]<s;i++); //尋找值>=s的第一個元素
if(i>=L.length)
retrun false; //所有元素值均小於s,則返回
for(j=i;j<L.length&&L.data[j]<=t;j++); //尋找值>t的第一個元素
for(;j<L.length;i++,j++)
L.data[i]=L.data[j]; //前移,填補被刪元素位置
L.length=i;
return true;
}
第五題
思想:從前向後掃描順序表L,用k記錄下元素值在s到t之間的元素的個數(初始值k=0)。對於當前掃描的元素,若其值不在s到t之間,則前移k個位置;否則執行k++。由於這樣每個不在s到t之間的元素僅移動一次,所以算法效率高
bool Del_s_t(sqList &L,ElemType s, ElemType t){
//刪除順序表L中值在給定值s與t之間(要求s<t)的所有元素
int i,k=0;
if(L.length == 0 || s>= t)
return false; //線性表爲空或者s、t不合法,返回
for(i=0;i<L.lenght;i++){
if(L.data[i]>=s&&L.data[i]<=t)
k++;
else
L.data[i-k]=L.data[i]; //當前元素前移k個位置
}//for
L.length-=k; //長度減小
return true;
}
本題也可以從後向前掃描順序表,每遇到一個值在s到t之間的元素,則刪除該元素,其後的所有元素全部遷移。但異動次數遠大於前者,效率不夠高
第六題
思想:注意是有序順序表,值相同的元素一定在連續的位置上,用類似於直接插入排序的思想,初始時將第一個元素看做非重複的有序表。之後以此判斷後面的元素是否與前面非重複有序表的最後一個元素相同,如果相同則繼續向後判斷,如果不同則插入到前面的非重複有序表的最後,直至判斷到表尾爲止。
bool Delete_Same(SeqList& L){
if(L.length ==0)
return false;
int i,j; //i存儲第一個不相同的元素,j工作指針
for(i=0,j=1;j<L.length;j++)
if(L.data[i]!=L.data[j]) //查找下一個與上個元素值不同的元素
L.data[++i]=L.data[j]; //找到後,則將元素前移
L.length = i+1;
}
書上說,對於本題算法,可以以序列1,2,2,2,2,3,3,3,4,4,5來手動模擬算法的執行過程,在模擬的過程中要標註i和j所指示的元素
思考:如果將本題的有序表改爲無需表,你能想到時間複雜度爲O(n)的方法嗎?
(提示:使用hash表)
第七題
思想:首先,按照順序不斷取下兩個順序表表頭較小的節點存入新的順序表中。然後,看哪個表還有剩餘,將剩下的部分加到新的順序表後面。
bool Merge(SeqList A,SeqList B,SeqList &C){
if(A.length + B.length > C.maxSize) //大於順序表的最大長度
return false;
int i = 0,j= 0,k=0;
while(i<A.length&&j<B.length){ //循環,兩兩比較,小者存入結果表
if(A.data[i]<=B.data[i++]);
C.data[k++]=A.data[i++];
else
C.data[k++]=B.data[j++];
}
while(i<A.length) //還剩一個沒有比較完的順序表
C.data[k++]=A.data[i++];
while(i<B.length)
C.data[k++]=B.data[j++];
C.length=k;
return ture;
}
第八題
思想:
首先將數組A[m+n] 中的全部元素(a1,a2,a3,…,am,b1,b2,b3,…,bn)原地逆置爲(bn,bn-1,bn-2,…,b1,am,am-1,am-2,…,a1)再對前n個元素和後m個元素分別使用逆置算法,就可以得到(b1,b2,b3,…,bn,a1,a2,a3,…,am)從而實現順序表的位置互換
typedef int DataTyep;
void Reverse(DataType A[], int left, int right,int arrySize){
//逆轉(aleft,aleft+1,aleft+2,...,aright)爲(aright,aright-1,....,aleft)
if(left >= right || right>=arraySize)
return;
int mid=(left+right)/2;
for(int i=0;i<=mid-left;i++){
DataType temp=A[left+i];
A[left+i]=A[right-i];
A[right-i]=temp;
}
void Exchange(DataType A[],int m, int n ,int arraySize){
/*數組A[m+n]中,從0到m-1存放順序表(a1,a2,a3,...,am),從m到m+n-1存放順序表(b1,b2,b3,...,bn)
* ,算法將這兩個表的位置互換*/
Reverse(A,0,m+n-1,arryaSize);
Reverse(A,0,n-1,arryaSize);
Reverse(A,0,m+n-1,arryaSize);
}
}
第九題
思想:順序存儲的線性表遞增有序,可以順序查找,也可以折半查找。題目要求“用最少的時間在表中查找數值爲x的元素”,這裏鷹使用折半查找法
void SearchExchanageInsert(ElemType A[],ElemType x ){
int low = 0,high = n-1,mid; //low和high指向順序表上界和下界的下標
while(low <= high){
mid=(low+high)/2; //找中間位置
if(A[mid]==x) break; //找到x,退出while循環
else if (A[mid]<x) low = mid+1; //到中點mid的右半部去查
else high =mid-1; //到中點mid的左半部去查
}
if(A[mid]==x&&mid!=n-1){ //若最後一個元素與x相等,則不存在與其後繼交換的操作
t=A[mid];A[mid]=a[mid+1];A[mid+1]=t;
}
if(low > high){ //查找失敗,插入數據元素x
for(i=n-1; i>high;i--) A[i+1] =A[i];//後移元素
A[i+1]=x; //插入x
}
}
第十題
1)思想:可以將這個問題看作是把數組ab轉換成數組ba(a代表數組的前p個元素,b代表數組中餘下的n-p個元素),先將a逆置得到
Reverse(0,p-1)得到cbadefgh;
Reverse(p,n-1)得到cbahgfed;
Reverse(0,n-1)得到defghabc;
注:Reverse中,兩個參數分別表示數組中待轉換元素的始末位置。
2)使用C語言描述算法如下
void Reverse(int R[],int from,int to){
int i,temp;
for(i=0;i<(to-from+1)/2;i++)
{
temp=R[from+i];R[from+i]=R[to-i];R[to-i]=temp;
}//Reverse
void Converse(int R[],int n ,int p){
Reverse(R,0,p-1);
Reverse(R,p,n-1);
Reverse(R,0,n-1);
}
}
3)上述算法中三個Reverse函數的時間複雜度分別爲O(p/2)、O((n-p)/2)和O(n/2),故所涉及的算法的時間複雜度爲O(n),空間複雜度爲O(1).
另解,藉助輔助數組來實現:
思想:創建大小爲p的輔助數組S,將R中前p個整數一次暫存在S中,同事將R中後n-p個整數左移,然後將S中暫存的p個數依次放回到R中的後續單元。
時間複雜度爲O(n),空間複雜度爲O(p)
第十一題
1)算法的基本涉及思想如下:
分別求兩個升序序列A、B的中位數,設爲a和b,求序列A、B的中位數過程如下:
①若a=b,則a或b即爲所求中位數,算法結束。
②若a
int M_Search(int A[],int B[],int n){
int s1=0,d1=n-1,m1,s2=0,d2=n-1,m2;
//分別表示序列A和B的首位數、末尾數和中位數
while(s1!d1||s2!=d2){
m1=(s1+d1)/2;
m2=(s2+d2)/2;
if(A[m1]==B[m2])
return A[m1]; //滿足條件1
if(A[m1]<B[m2]){ //滿足條件2
if((s1+d1)%2==0){ //若元素個數爲奇數
s1=m1; //捨棄A中間點以前的部分且保留中間點
d2=m2; //捨棄B中間點以後的部分且保留中間點
}
else{ //元素個數爲偶數
s1=m1+1; //捨棄A中間點及中間點以前部分
d2=m2; //捨棄B中間點以後部分且保留中間點
}
}
else { //滿足條件3
if((s2+d2)%2==0){ //若元素個數爲奇數
d1=m1; //捨棄A中間點以後的部分且保留中間點
s2=m2; //捨棄B中間點以前的部分且保留中間點
}
else{ //元素個數爲偶數
d1=m1; //捨棄A中間點以後部分且保留中間點
s2=m2+1; //捨棄B中間點及中間點以前部分
}
}
}
return A[s1]<B[s2]?A[s1]:B[s2];
}
3)算法的時間複雜度爲O(log2n),空間複雜度爲O(1)
第十二題
1)思想:
算法的策略是從前向後掃描數組元素,標記處一個可能成爲主元素的元素Num,然後重新計數,確認Num是否是主元素。
算法可分爲以下兩部
①選取候選的主元素:一次掃描所給數組中的每個整數,將第一個遇到的整數Num保存到c中,記錄Num的出現次數爲1;若遇到的下一個整數扔等於Num,則計數加1,否則計數減1;當計數減到0時,將遇到的下一個整數保存到c中,計數重新記爲1,開始新一輪計數,即從當前位置開始重複上述過程,知道掃描完全部數組元素
②判斷c中元素是否是真正的主元素:在此掃描該數組,統計c中元素出現的次數,若大於n/2,則爲主元素;否則,序列中不存在主元素
2)
int Majority(int A[],int n){
int i,c,count=1; //c用來保存候選主元素,count用來計數
c = A[0]; //設置A[0]位候選主元素
for(i=1;i<n;i++) //查找候選主元素
if(A[i]==c)
count++; //對A中的候選主元素計數
else
if(count>0) //處理不是候選主元素的情況
count--;
else{ //更換候選主元素,重新計數
c=A[i];
count=1;
}
if(count>0)
for(i=count=0;i<n;i++) //統計候選主元素的實際出險次數
if(A[i]==c)
count++;
if(count>n/2) return c; //確認候選主元素
else return -1; //不存在主元素
}
3)算法的時間複雜度爲O(n),空間複雜度爲O(1)