東 華 大 學
《算法分析設計與綜合實踐》實驗報告
學生姓名:曹晨 學號:171310402 指導教師:章昭輝
實驗時間:2019-3-13 實驗地點:圖文信息樓三號機房
請勿轉載!!!
- 實驗名稱
眾數問題
-
實驗目的
對於給定的由n個自然數組成的多重集S,計算S的眾數及其重數。
-
實驗內容
給定含有n個元素的多重集合S,每個元素在S中出現的次數稱為該元素的重數。多重集合S中重數最大的元素稱為眾數。例如{1,2,2,2,3,5}。多重集合S的眾數為2,其重數為3。
數據輸入:
輸入第一行為多重集S中元素的個數n;在接下來的n行中,每行有一個自然數。
結果輸出:
輸出由兩行,第一行為眾數,第二行時重數
-
實驗過程
自己的思路:
創建一個數組b[][2],遍歷一遍數組,b中的結果如下
元素
個數
b[0]
1
1
b[1]
2
3
b[2]
3
1
b[3]
5
1
在數組b中找到個數最多的值為重數,對應的元素為眾數,輸出即可
參考答案及網上的思路:
1 |
2 |
2 |
2 |
3 |
5 |
l med r
👇
1 |
2 |
2 |
2 |
3 |
5 |
l l1 med r1 r
左子集 右子集
1 |
3 |
5 |
l,r l r
‧‧‧‧‧‧‧‧‧
每做完一次上述的操作,都要更新一下眾數和重數。當重數的大小大於左(或右)子集的大小時,就不用在左(或右)子集中再做一次上述操作。
-
結果分析
這個結果是我用數組的辦法編寫的。所用的時間為9.683s
這個結果是我用分治法的辦法編寫的。所用的的時間為5.352s
由此可見,用分治法解這道題效率更高
-
實驗總結
剛開始我沒有想到可以用分治法來做,用的是數組來做。做完以后發現算法太過復雜,而且處理較大的n時,時間復雜度和空間復雜度都比較大。於是我參考了算法答案那本書,學到了分治法來解決這道題。但是在完成了用分治法的代碼以后,我發現了問題:
輸出應該為:
2
4
分治法解決這道題有一個大前提:多重集中的元素是有序的(遞增或者遞減),所以很有必要給多重集加入一個排序的算法.
附錄:(要求代碼里各行要有注釋)
自己的代碼(性能不夠好,太過復雜)
int main()
{
int n;
cin>>n;
int a[n];
for(int i=0;i<n;i++)
{
cin>>a[i];
}
f(n,a);
return 0;
}
void f(int n,int *a)//n為多重集元素的個數,a是存放數組的元素
{
int b[n][2];//b[][0]存放元素的種類,B[][1]存放每種元素的個數
b[0][0]=a[0];
b[0][1]=1;
int h=0;//數組b中已初始化的行的標號
int flag=0;//判斷b中是否已經有該元素了
for(int i=1;i<n;i++)
{
for(int j=0;j<=h;j++)
{
if(a[i]==b[j][0])//判斷元素a[i]是否已經在數組b中存在
{
b[j][1]++;
flag=1;
}
}//存在的話,把b[j][1]加1,flag變為1,否則不做操作
if(flag==0)
{
h++;
b[h][0]=a[i];
b[h][1]=1;
}
flag=0;
}//不存在的話,flag為0,此時把a[i]加入到數組之中,並把個數變為1
int temp=b[0][1];
int t=0;
for(int i=0;i<=h;i++)//尋找b中數目最多的那個元素
{
if(temp<b[i][1])
{
temp=b[i][1];
t=i;
}
}
cout<<b[t][0]<<endl<<temp<<endl;//輸出眾數和重數
}
參照答案和csdn博客所改的代碼:
https://www.cnblogs.com/yangykaifa/p/7162192.html
https://blog.csdn.net/chencong3139/article/details/52863629
int main()
{
int n;
cin>>n;//元素個數
int a[n];
for(int i=0;i<n;i++)
{
cin>>a[i];
}//數組用於存放元素
int l=0;//數組的首個標號
int r=n-1;//數組的最后一個標號
int largest=0;//重數的初始化
int elem=-1;//眾數的初始化
mode(a,l,r,largest,elem);//調用函數
cout<<elem<<endl<<largest<<endl;//輸出眾數和重數
return 0;
}
//用於求數組的中位數的函數↓
int median(int *a,int l,int r)
{
return a[(l+r)/2];
}
//以med來划分數組↓ !!!關鍵函數
void split(int *a,int med,int l,int r,int &l1,int &r1)
{
for(l1=l;l1<=r;l1++)
{
if(a[l1]==med)
break;
}//l1表示首個等於med的數組下標
for(r1=l1+1;r1<=r;r1++)
{
if(a[r1]!=med)
break;
}
r1--;
}//r1表示最后一個等於med的數組下標
void mode(int *a,int l,int r,int &largest,int &elem)
{
int l1,r1;//分割后左邊子數組的右界和右邊子數組的左界
int med=median(a,l,r);//midian函數用於找到數組的中位數
split(a,med,l,r,l1,r1);//以med中位數來分割數組
if(largest<r1-l1+1)
{
largest=r1-l1+1;
elem=med;
}//每次遞歸更新眾數和重數的值
if(l1-l>largest)
mode(a,l,l1-1,largest,elem);//判斷左邊是否值得遞歸(只有l1-1大於largest才有必要搜尋)
if(r-r1>largest)
mode(a,r1+1,r,largest,elem);//判斷右邊是否值得遞歸
}