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

来源:互联网 发布:c语言区间内素数 编辑:程序博客网 时间:2024/05/02 00:40

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

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

0 0