1. 程式人生 > >《程式設計之法》2.2尋找和為定值的兩個數

《程式設計之法》2.2尋找和為定值的兩個數

題目描述:輸入一個整數陣列和一個整數,在陣列中查詢一對數,滿足它們的和正好等於輸入的那個整數,並輸出任一一對值。

解法一:直接窮舉:
雙層迴圈,複雜度為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)。最好分配一個標記陣列來數本身去掉重複的情況。