《程式設計之法》2.2尋找和為定值的兩個數
阿新 • • 發佈:2018-12-31
題目描述:輸入一個整數陣列和一個整數,在陣列中查詢一對數,滿足它們的和正好等於輸入的那個整數,並輸出任一一對值。
解法一:直接窮舉:
雙層迴圈,複雜度為O(n^2);
#include <iostream> using namespace std; void DirectEnum(int nums[], int n, int len){ int i, j, flag = 0; for(i = 0; i < len; ++i){ for(j = i+1; j < len; ++j){ if(nums[i] + nums[j] == n){ cout << nums[i] << " " << nums[j] << endl; flag = 1; break; } } if(flag) break; } } int main(){ int n, nums[20] = {4, 5, 23, 12, 29, 20, 14, 24, 34, 36, 39, 28, 33, 7, 9, 17, 27, 30, 8, 11}; while(cin >> n) DirectEnum(nums, n, 20); return 0; }
解法二:排序後查詢:
先對陣列sort排序,之後遍歷陣列,對於每一個遍歷的數x,二分查詢n-x是否在該陣列中,時間複雜度為O(nlogn)+O(n*logn)=O(nlogn);
#include <iostream> #include <algorithm> using namespace std; bool Division(int nums[], int pos, int x, int low, int high){ int mid = (low + high)/2, tag = 0; while(low <= high){ if(nums[mid] < x){ low = mid + 1; mid = (low + high)/2; } else if(nums[mid] > x){ high = mid - 1; mid = (low + high)/2; } else{ tag = 1; break; } } if(tag == 1 && mid != pos)//需要是一對數,故不能為它本身 return true; return false; } void SortEnum(int nums[], int n, int len){ int i, j; sort(nums, nums+len); for(i = 0; i < len; ++i){ if(Division(nums, i, n-nums[i], 0, len-1)){ cout << nums[i] << " " << n-nums[i] << endl; break; } } } int main(){ int n, nums[20] = {4, 5, 23, 12, 29, 20, 14, 24, 34, 36, 39, 28, 33, 7, 9, 17, 27, 30, 8, 11}; while(cin >> n) SortEnum(nums, n, 20); return 0; }
解法三:雜湊排序:
假設所有的整數介於0~100間,以時間換空間,設定hash[n],每輸入一個數x,就令hash[x]=1。判斷時只需遍歷一遍陣列,看是否對應的hash[n-s[i]]為1即可,時間複雜度為O(n),針對空間複雜度,假設陣列中有些數大於n,此時沒必要給這些數設定hash空間了,因為它們不可能與另外的數累加得到n,故空間複雜度也為O(n);
#include <iostream> using namespace std; void HashEnum(int nums[], int hash[], int n, int len){ int i; for(i = 0; i < len; ++i){ if((n-nums[i] >= 0) && hash[n-nums[i]]){ if((n-nums[i] == nums[i]) && (hash[nums[i]] == 1)) //去掉nums[i]為其本身的情況 continue; cout << nums[i] << " " << n-nums[i] << endl; break; } } } int main(){ int hash[200], n, nums[20] = {4, 5, 23, 12, 100, 22, 14, 24, 34, 36, 39, 28, 33, 7, 9, 17, 27, 30, 8, 11}; while(cin >> n){ memset(hash, 0, sizeof(hash)); int i; for(i = 0; i < 20; ++i){ ++hash[nums[i]]; } HashEnum(nums, hash, n, 20); } return 0; }//此時輸入200,無輸出
解法四:排序夾逼:
若此時陣列是有序的,則可令時間複雜度為O(n),若是無序的,時間複雜度為O(nlogn) + O(n),空間複雜度都為O(1)。設定兩個指標begin和end分別指向陣列尾端,分別向右向左移動,每次依據a[begin]+a[end]的值與n進行比較,若該值小於n,說明需要a[begin]太小,begin指標需右移;若該值大於n,說明a[end]較大,end指標需左移。
#include <iostream>
#include <algorithm>
using namespace std;
void TwoSum(int nums[], int n, int len){
sort(nums, nums+len);
int begin = 0, end = len - 1;
while(begin < end){
if(nums[begin] + nums[end] == n){
cout << nums[begin] << " " << nums[end] << endl;
break;
}
else{
if(nums[begin] + nums[end] < n)
++begin;
else
--end;
}
}
}
int main(){
int n, nums[20] = {4, 5, 23, 12, 100, 22, 14, 24, 34, 36, 39, 28, 33, 7, 9, 17, 27, 30, 8, 11};
while(cin >> n)
TwoSum(nums, n, 20);
return 0;
}
舉一反三:
尋找樹中和為定值的所有路徑
輸入一棵二叉樹和一個整數,從樹的根結點開始向下訪問,一直到葉結點。如下,輸入整數22和該二叉樹,則列印兩條路徑:10-->12和10-->5-->7。
演算法分析:二叉樹可以用陣列進行儲存,可以先分配一個數組,如下的二叉樹,從下標1開始存放根結點,將不存在的結點在陣列中用-1存放。根據二叉樹的性質,結點編號為i的左右孩子結點編號為2i, 2i+1,故可以利用圖的深度遍歷方式從根結點一直往下訪問,遇到-1則比較並輸出,如下:
#include <iostream>
#include <iomanip>
using namespace std;
int n, t, path[22], Tree[110], loc;//path[]存放路徑
void dfs(int cur){
int i;
//臨界條件,dfs中必須寫在dfs呼叫前面,否則出現無窮遞迴。遞迴條件是當前結點為葉子結點
if((Tree[2*cur] == -1) && (Tree[2*cur+1] == -1)){
int sum = 0;
for(i = 0; path[i]; ++i)
sum += path[i];
if(sum == t){
for(i = 0; path[i]; ++i){
if(i == 0) cout << path[i];
else
cout << setw(3) << path[i];
}
cout << endl;
}
return;
}
//深度遞迴
for(i = 0; i <= 1; i++){
if(Tree[2*cur+i] != -1){ //孩子結點未被訪問
path[loc++] = Tree[2*cur+i];
dfs(2*cur+i);
path[--loc] = 0;
}
}
}
int main(){
while(cin >> n >> t && n != 0){ //輸入樹高n,和判斷為和的整數t
//輸入二叉樹的資訊
int i;
for(i = 0; i <= (int)pow(2.0, n+1)-1; ++i)
Tree[i] = -1;
for(i = 1; i <= (int)pow(2.0, n)-1; ++i)
cin >> Tree[i];
//開始深度遍歷
loc = 0;
path[loc++] = Tree[1];
dfs(1);
}
return 0;
}
3個數和為定值問題:以解法三為例,對於每個數a,則對應另外兩個數的和為-a,之後再進行遍歷,對於遍歷到的每個數b,看對應hash[-a-b+offset]是否為1,時間複雜度為O(n^2);
用解法一效仿,時間複雜度為O(n^3);用解法二做,為O(nlogn)+O(n*n*logn)=O(n^2logn);用解法四做,為O(nlogn)+O(n*n)=O(n^2)。最好分配一個標記陣列來數本身去掉重複的情況。