《编程之法》2.2寻找和为定值的两个数

本文介绍了如何在整数数组中寻找和为定值的两个数,探讨了从直接穷举到高效算法的优化过程,包括双层循环法、哈希表辅助的线性搜索等,分析了不同方法的时间复杂度,如O(n^2)到O(n^2logn)的改进。
摘要由CSDN通过智能技术生成

题目描述:输入一个整数数组和一个整数,在数组中查找一对数,满足它们的和正好等于输入的那个整数,并输出任一一对值。

解法一:直接穷举:
双层循环,复杂度为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)。最好分配一个标记数组来数本身去掉重复的情况。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值