分治法求眾數和重數(含檔案輸入輸出)
【mie haha的部落格】轉載請註明出處(萬分感謝!):
https://blog.csdn.net/qq_40315080/article/details/88580999
(可以列出所有眾數)
程式碼:
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
int mode=0,multiplicate=0;
vector<int> allmode;
void get_mode_and_multiplicate(int a[],int start,int end)
{
if(end==start)return;//遞迴結束條件:當待考察序列只有一個元素,終止檢視
int mid=(start+end)/2;
int newmode=0,newmultiplicate=0,left=mid,right=mid,leftflag=1,rightflag=1;//次輪發現的眾數個重數
for(int i=mid-1;i>=start;i--)
{
if(a[i]!=a[mid])
{
left=i;leftflag=0;break;//找到從中間數向左邊數第一個不相同的數
}
}
for(int i=mid+1;i<=end;i++)
{
if(a[i]!=a[mid])
{
right=i;rightflag=0;break;//找到從中間數向右邊數第一個不相同的數
}
}
if(leftflag==1)left=start;
if(rightflag==1)right=end;
//cout<<"left:"<<left<<" "<<"right:"<<right<<" ";
newmode=a[mid];newmultiplicate=right-left-1;//次輪得到的眾數候選重數候選
if(leftflag==1&&rightflag==1)newmultiplicate+=2;
else if(leftflag==1||rightflag==1)newmultiplicate+=1;
//cout<<"nowmode:"<<newmode<<" "<<"nowmultiplicate:"<<newmultiplicate<<endl;
if(newmultiplicate>multiplicate)//如果得到的新眾數重數更大,更新眾數和重數
{
multiplicate=newmultiplicate;mode=newmode;allmode.push_back(mode);
}
else if(newmultiplicate==multiplicate)allmode.push_back(newmode);
get_mode_and_multiplicate(a,start,left);//繼續在左邊的待查陣列裡遞迴
get_mode_and_multiplicate(a,right,end);//繼續在右邊遞迴
}
int main()
{
int n;
cin>>n;
int a[n];
for(int i=0;i<n;i++)cin>>a[i];
sort(a,a+n);
get_mode_and_multiplicate(a,0,n-1);
cout<<"最終結果:"<<endl;
if(multiplicate<=1)
{
cout<<"沒有眾數,所有元素都只出現了1次"<<endl;
vector<int>().swap(allmode);//儲存的數並不是眾數已經沒有用了,當資料量過大,釋放vector記憶體
}
else if(multiplicate>1)
{
cout<<"共有"<<allmode.size()<<"個眾數"<<endl;
cout<<"為:";
for(int i=0;i<allmode.size();i++)cout<<allmode[i]<<" ";
}
return 0;
}
執行結果:
思路:
首先對陣列進行排序,這樣相同值的元素就挨在了一起,通過對某個元素進行向左向右檢視是否有元素相同,就可判斷當前元素共出現了多少次。不斷地劃分陣列,每次都找到陣列中的1個元素作為候選眾數,以該元素出現次數為候選重數,把候選重數與當前儲存的重數比較,如果更大,則更新當前儲存的眾數和重數,如果一樣大說明目前有出現次數一樣的眾數,把它存入vector中儲存起來。每次劃分陣列長度都減半,要處理的陣列越來越短,直至陣列裡只有1個元素就結束排查了。(其實就是用了遞迴的方法)
(每次對半分讓子問題規模儘量相等時間複雜度最小,也體現了分治法的思想)
步驟:
1.使用sort函式平均情況下時間複雜度只有nlogn(最差情況就比較差了,為n²)
2.首先判斷一下當前待處理的陣列需不需要處理,如果只有1個元素,肯定不是眾數,不用再操作了直接結束。如果元素個數>1,則開始處理:
首先,找到當前待處理陣列的中間值a[(start+end)/2]作為此次排查中的候選眾數,該值出現的次數為候選重數。
左右兩個迴圈來找到兩邊最近的與它不同的元素,標記一下兩側的不同元素的位置,則從標記處分成了左右新的兩個短陣列。
然後,此時兩側標記相減(不是單純相減,有一點誤差,自己舉個例子算一下就可以調整了,不同情況下+1/-1/不變,程式碼中有註釋)就是此輪的候選重數。
最後,比較候選重數和當前儲存的重數(全域性變數)進行判斷:
(1)如果候選重數與其一樣大,把當前這個數儲存到vector中;
(2)如果候選重數更大,說明之前儲存的眾數都無效了,因為它們不是出現最多的數,∴清空vector,再將當前這個數儲存到vector中;
(3)如果候選重數小,不用管,當前儲存的數仍是目前出現最多的數;
3.對分出的左右兩個陣列繼續2的操作,即判斷陣列中間位置的數是不是眾數的操作,用遞迴來實現。
(檔案型輸入輸出)程式碼:
#include<iostream>
#include<algorithm>
#include<vector>
#include<fstream>//用於檔案輸入輸出
using namespace std;
int mode=0,multiplicate=0;
vector<int> allmode;
void get_mode_and_multiplicate(int a[],int start,int end)
{
if(end==start)return;//遞迴結束條件:當待考察序列只有一個元素,終止檢視
int mid=(start+end)/2;
int newmode=0,newmultiplicate=0,left=mid,right=mid,leftflag=1,rightflag=1;//次輪發現的眾數個重數
//找到從中間數向左邊數第一個不相同的數
for(int i=mid-1;i>=start;i--)
{
if(a[i]!=a[mid])
{
left=i;leftflag=0;break;
}
}
//找到從中間數向右邊數第一個不相同的數
for(int i=mid+1;i<=end;i++)
{
if(a[i]!=a[mid])
{
right=i;rightflag=0;break;
}
}
if(leftflag==1)left=start;//如果左邊找不到不相同的數,就只好賦值為當前陣列左起點
if(rightflag==1)right=end;//如果右邊找不到不相同的數,就只好賦值為當前陣列右終點
newmode=a[mid];newmultiplicate=right-left-1;//次輪得到的眾數候選重數候選
if(leftflag==1&&rightflag==1)newmultiplicate+=2;
else if(leftflag==1||rightflag==1)newmultiplicate+=1;
if(newmultiplicate>multiplicate)//如果得到的新眾數重數更大
{
multiplicate=newmultiplicate;mode=newmode; //更新重數和眾數
allmode.clear();allmode.push_back(mode);//原來vector中儲存的眾數失效,清空存入新值
}
else if(newmultiplicate==multiplicate)allmode.push_back(newmode);
get_mode_and_multiplicate(a,start,left);//繼續在左邊的待查陣列裡遞迴
get_mode_and_multiplicate(a,right,end);//繼續在右邊遞迴
}
int main()
{
ifstream infile("input.txt",ios::in);
//讀取元素個數n
int n;
infile>>n;
//讀取元素
int a[n];
for(int i=0;i<n;i++)infile>>a[i];
sort(a,a+n);
get_mode_and_multiplicate(a,0,n-1);
ofstream outfile("output.txt",ios::out);
if(multiplicate<=1)
{
outfile<<"沒有眾數,所有元素都只出現了1次"<<endl;
vector<int>().swap(allmode);//儲存的數並不是眾數已經沒有用了,當資料量過大,釋放vector記憶體
}
else if(multiplicate>1)
{
outfile<<"共有"<<allmode.size()<<"個眾數:";
for(int i=0;i<allmode.size();i++)outfile<<allmode[i]<<" ";
outfile<<endl<<"出現次數為:"<<multiplicate;
cout<<"結果請在輸出檔案output中檢視";
}
return 0;
}
輸入:
執行結果:
注:
1.如果題目有說保證輸入的資料只有1個眾數,就不需要vector陣列來儲存了,可以把程式碼中有關的部分都刪掉,每次只要判斷一下候選重數是不是比儲存的重數大,如果是的話,更新一下重數和眾數就可以了,程式碼會短很多~
2.另外輸出部分當重數為1即沒有眾數的情況下我為了防止輸入的資料量太大,把vector的記憶體釋放了(swap()實現),也可以刪掉,程式碼又會短很多~
3.總體來說分治法和遞迴很相似,可以說分治是策略,遞迴是程式碼方法,分治用到遞迴
如有錯誤或不完善的地方,歡迎指出
相關文章
- 演算法設計--眾數和重數問題(分治法)演算法
- 笨辦法學C 練習24:輸入輸出和檔案
- 排序,檔案輸入輸出排序
- 檔案操作-輸入輸出
- python ----輸入輸出 變數Python變數
- 1.變數and輸入輸出變數
- C輸入輸出與檔案
- matlab輸出複數到檔案Matlab
- 輸入和輸出基礎語法
- java_檔案輸入與輸出Java
- C++中的檔案輸入/輸出(3):掌握輸入/輸出流 (轉)C++
- python:檔案的輸入與輸出Python
- LeetCode169求眾數——分治LeetCode
- UVA 11462 Age Sort(計數排序法 優化輸入輸出)排序優化
- C# 輸入一個整數,求質因數C#
- 第10章 對檔案的輸入輸出
- 【C++】標準檔案的輸入輸出!!!C++
- 求大家幫助,Jmeter 變數為 null 時,輸出的是變數名,如何不輸出變數名原樣輸出 null?JMeter變數Null
- C++筆記:輸入輸出、變數、變數加減乘除C++筆記變數
- 使用Java NIO 和 NIO2實現檔案輸入/輸出Java
- 第13周-專案1-小玩檔案-用鍵盤輸入檔名,統計輸出檔案中每個字母、數字字元出現的次數字元
- 檔案輸入和輸入出處理(六)-序列化和反序列化
- 檔案輸入輸出處理(二)-位元組流
- 瞭解下C# 檔案的輸入與輸出C#
- 輸入一個三位數,輸出它各個數位之和
- c++實現求眾數及其重數C++
- input 輸入框只能輸入數字
- C++中的檔案輸入/輸出(2):讀取檔案 (轉)C++
- C++中的檔案輸入/輸出(4):檢測輸入/輸出的狀態標誌 (轉)C++
- ios OC 輸入框禁止輸入空格/去掉空格/只能輸入字母和數字iOS
- 有關日期手工輸入驗證、日期彈出框、數值輸入驗證、必須輸入驗證等(共三個檔案)
- Python 輸入和輸出Python
- 重學java中的輸入輸出流Java
- Oracle帶輸入輸出引數的儲存過程Oracle儲存過程
- Python進階02 文字檔案的輸入輸出Python
- Python輸入和輸出(IO)Python
- 格式化輸入和輸出
- C語言中輸入輸出重定,freopen()妙用。C語言