算法学习(四)寻找满足条件的两个数或者多个数
来源:互联网 发布:云计算 网络强国 编辑:程序博客网 时间:2024/04/30 09:53
寻找和为定值的两个数
编程之美2.12节(P176)
题目描述:能否快速找出一个数组中的两个数字,让这两个数字之和等于一个给定的值,为了简化,假设这个数组肯定存在这两个数。
例如数组1,2,4,7,11,15。给定值为15,4+11 = 15,输出4,11。
分析:
1,判断条件就是两数之和为sum,对于A[i] ,需要找sum-A[i]是否也在数组A中,这样就变成了一个查找算法,那么提高查找效率就能提高整个算法的效率,通常可以先将数组排序,然后用二分查找等方法进行查找,查找时间为O(logN),排序需要O(N * logN),遍历需要O(N),总的时间为O(N* logN + N* logN) = O (N *logN)。
2,如果能再缩短查找时间,算法的效率就会再次提高,我们可以使用hash表,它是可以在常数时间执行插入,删除,查找。
这样可以在时间O(N)完成,不过空间复杂度变为O(N),某些情况下,空间换时间也不失为一个好的方法。
3,感觉排序和查找有时候就是并存的,看到问题尤其是数的时候,先想一想,如果这些数有序是不是对于解决问题有帮助,对于这个问题,先排序,从小到大排列,然后用两个首尾指针,i 从0,J从n-1开始,逐次判断a[i]+a[j]?= sum,如果a[i]+a[j] > sum,想办法让两者之和减小,尾端的是大数,所以就j–,i不动,如果a[i]+a[j] < sum,想办法让两者之和增大,左边为小数,j不动,i++。这样查找可以在O(N)完成,而且空间复杂度为O(1),加上排序的时间O(N*logN),总的时间为O(N*logN),两个指针两端扫描法。
实现:
#include<iostream>#include <string.h>#include <algorithm>#define SIZE 10#define TABLESIZE 16using namespace std;typedef struct Listnode * Position;Position HashTable[TABLESIZE]; //这个地方直接用的数组,没有用结构体,显得不够专业,不过方便哟struct Listnode{ int num; Position next;};/*专业的再构建一个结构体,里面有hashtable大小和指向指向结构体Listnode的指针的指针struct Hash{ int Tablesize; Position * TheLists;};*///二分查找法int binary_search(int * a,int len,int goal){ int low = 0; int high = len-1; //这个地方要注意,如果high= len,后面的也要更改 while(low <= high) { int middle = (high-low)/2 + low; if(a[middle] == goal) return 1; else if(a[middle] > goal) high = middle-1; //如果high= len,此处high = middle else low = middle+1; } return 0;}//简单的hash函数int hash_function(int num){ int n ; n = num % TABLESIZE; return n;}//在hashtable查找函数Position Find(int num){ if(num <0) //如果值为负,sum-a[i]的结果,说明肯定不是 return NULL; else { int index = hash_function(num); Position p = HashTable[index]; while(p != NULL && p->num != num) p = p->next; return p; }}//hashtable插入函数void Insert(int num){ int index = 0; Position pos; index = hash_function(num); pos = Find(num); if(pos == NULL) //如果在hashtable中不存在这个值,新插入一个 { Position newcell = new Listnode; newcell->num = num; newcell->next = HashTable[index]; HashTable[index] = newcell; }}//思路3,双指针扫描法void Find_num(int * a,int len,int sum){ int i,j; for(i = 0,j = len-1;i<j;) { if(a[i]+a[j] == sum) { cout << "find num " << a[i] << " " << a[j] << endl; return; } else if(a[i]+a[j] > sum) { j--; } else { i++; } } cout << "not find" << endl;}int main(){ int a[SIZE] = {2,4,1,23,5,76,0,43,24,65}; int len ; int sum = 24; sort(a,a+SIZE); //直接使用库函数排序,排序不是重点 int i;//二分查找法 for(i = 0;i< SIZE;i++) { cout << a[i] << endl; if(binary_search(a,SIZE,sum-a[i])) { cout << "find num :" << a[i] << " " << sum-a[i] << endl; break; } }//hashtable for(i = 0;i< SIZE;i++) { Insert(a[i]); } for(i = 0;i<SIZE;i++) { if(Find(sum-a[i]) != NULL) { cout << "find num :" << a[i] << " " << sum-a[i] << endl; break; } }//双指针法 Find_num(a,SIZE,sum); return 0;}
想了想还是按标准的c风格把hashtable的解法写了一遍,初始化的时候忘记返回了,一直段错误找不到,找了半个小时,哎,写的时候一定要注意返回值问题。
/************************************************************************* > File Name: find_twonum_hash.cpp > Author: zxl > mail: 857317335@qq.com > Created Time: 2016年04月15日 星期五 09时42分14秒 ************************************************************************/#include <iostream>#include <algorithm>#include <stdlib.h> //支持mallocusing namespace std;const int size = 10;typedef struct Listnode * Position;typedef struct Hash * HashTable;typedef Position List;struct Listnode{ int num; Position next;};struct Hash{ int Tablesize; List * TheLists;};//比较Low逼的hash函数int Hash_function(int key,int size){ int n; n = key % size; return n;}bool isprime(int n){ int i; if(n< 2) return false; if(n== 2) return true; for(i = 3;i*i <=n;i+=2) //如果不是素数,存在一个因子d,1< d < sqrt(n) { if(n%i == 0) return false; } return true;}//找到比n大的下一个素数int NextPrime(int n){ int i; while(n++) { if(isprime(n)) return n; }}//hashtable的初始化HashTable init(int Tablesize){ HashTable H; int i; H = (Hash *)malloc(sizeof(Hash)); if(H == NULL) cout << "out of space" << endl; H->Tablesize = NextPrime(Tablesize); H->TheLists = (List *)malloc(sizeof(List) * H->Tablesize); if(H->TheLists == NULL) cout << "out of space"<< endl; for(i = 0;i< H->Tablesize;i++) { H->TheLists[i] =(Listnode *)malloc(sizeof(struct Listnode)); if(H->TheLists[i] == NULL) cout << "out of space" << endl; else H->TheLists[i]->next = NULL; } return H; //尼玛,这个地方忘记返回了}//在hash中查找keyPosition Find(HashTable H,int key){ List L; Position P; int index; index = Hash_function(key,H->Tablesize); L = H->TheLists[index]; P = L->next; while(P != NULL && P->num != key) P = P->next; return P;}//插入keyvoid Insert(HashTable H,int key){ Position pos,newcell; List L; pos = Find(H,key); int index; index = Hash_function(key,H->Tablesize); if(pos == NULL) //如果不存在 { newcell = (Listnode *)malloc(sizeof(struct Listnode)); L = H->TheLists[index]; newcell->num = key; newcell->next = L->next; L->next = newcell; }}int main(){ int a[size] = {2,4,1,23,5,76,0,43,24,65}; const int sum = 24; int Tablesize = 16; sort(a,a+size); int i; HashTable H; H = init(Tablesize); for(i = 0;i<size;i++) Insert(H,a[i]); for(i = 0;i<size;i++) { if(sum-a[i] < 0) continue; else { if(Find(H,sum-a[i]) != NULL) { cout << "find num:" << a[i] << " " << sum-a[i] << endl; break; } } } return 0;}
寻找和为定值的多个数
题目描述:
输入两个整数n和m,从数列1,2,3…n中随意取几个数,使其和等于m,要求将其中所有的可能组合列出来。
分析:
有点类似0-1背包问题:在选择每个数的时候,对每个数只用两种选择,要么装入到序列中,要不装,不能将一个数装入多次。
可以使用递归的思想:放入数n,问题就变为了n-1个数的和为m -n,依次递归,不放入数n,问题就变为了n-1个数和和为m。
解法一:
/************************************************************************* > File Name: find_manynum.cpp > Author: zxl > mail: 857317335@qq.com > Created Time: 2016年04月18日 星期一 10时38分47秒 ************************************************************************/#include <iostream>#include <list>using namespace std;list<int > List;void find_factor(int sum,int n){ if(n<=0 || sum<= 0) return; if(sum == n) { List.reverse(); //由大到小输出 list<int>::iterator iter; for(iter = List.begin();iter !=List.end();iter++) { cout << *iter << "+"; } cout << n << endl; List.reverse(); } List.push_front(n); //将n放入到序列中 find_factor(sum-n,n-1); List.pop_front(); //n不放入到序列中 find_factor(sum,n-1);}int main(){ int sum ,n; cout << "input the sum" <<endl; cin>>sum; cout << "input the n" << endl; cin>> n; find_factor(sum,n); return 0;}
回溯法解问题,定义问题的解空间。确定了解空间的组织结构后,从开始结点出发,以深度优先方式搜索整个解空间,通常采用两种策略避免无效搜索,提高回溯法的搜索效率。其一是用约束函数在扩展结点处剪去不满足约束的子树;其二是用限界函数剪去得不到最优解的子树,这两类函数统称为剪枝函数。
X数组为解向量,t = ∑(1,…k-1)Wi*Xi,r = ∑(k,…n)Wi
Wi表示对应结点的权重,在此题就是i,Xi为对应的向量值(0或者1),
若t+Wk+W(k+1)<=M,则Xk = 1,递归k结点的左儿子(X1,X2…X(k-1),1);否则剪枝;
若t+r-Wk>=M && t+W(k+1) <= M,则置Xk = 0;递归k结点的右儿子(X1,X2,…,X(k-1),0);否则剪枝;
/************************************************************************* > File Name: find_manysum_hui.cpp > Author: zxl > mail: 857317335@qq.com > Created Time: 2016年04月18日 星期一 15时32分47秒 ************************************************************************/#include <iostream>#include <stdlib.h>#include <string.h>using namespace std;void sumofsub(int t,int r,int k,int M,bool&flag,bool *X){ X[k] = true; if(t+k == M) { int i; flag = true; for(i = 0;i<k;i++) { if(X[i] == true) cout << i << "+" ; } cout <<k << endl; } else { if(t+k+k+1 <= M) sumofsub(t+k,r-k,k+1,M,flag,X); //递归左子树 if((t+r-k>= M) && (t+k+1)<=M) { X[k] = false; sumofsub(t,r-k,k+1,M,flag,X); //递归右子树 } }}void search(int n,int m){ bool * X = (bool *)malloc(sizeof(bool)*(n+1)); memset(X,false,sizeof(bool)*(n+1)); //将x中的值设置为false int sum = (n+1)*n/2; //前n项和 if(1>m || sum < m) { cout << "m is error" << endl; return; } bool f = false; sumofsub(0,sum,1,m,f,X); if(!f) { cout << "not found" << endl; } free(X);}int main(){ int n,m; cout << "input N" << endl; cin >> n; cout << "input M" << endl; cin >> m; search(n,m); return 0;}
- 算法学习(四)寻找满足条件的两个数或者多个数
- 编程之美2.12——快速寻找满足条件的两个数。(拓展满足条件的多个数)
- (5)寻找满足条件的两个或多个数
- 寻找满足条件的两个或多个数
- 寻找满足条件的两个或多个数
- 第五章、寻找满足条件的两个或多个数
- 寻找两个满足条件的数
- 快速寻找满足条件的两个数
- 快速寻找满足条件的两个数
- 快速寻找满足条件的两个数
- 快速寻找满足条件的两个数
- 寻找满足条件的两个数
- 寻找满足条件的两个数
- 快速寻找满足条件的两个数
- 快速寻找满足条件的两个数
- 快速寻找满足条件的两个数
- 快速寻找满足条件的两个数
- 快速寻找满足条件的两个数
- javascript中apply、call和bind的区别
- Android合并两个APP的具体做法(掌握)
- driver: Linux设备模型之input子系统详解
- Spring <context:annotation-config/>详解
- ie8 background背景图片没有显示
- 算法学习(四)寻找满足条件的两个数或者多个数
- Android支付宝
- eclipse最有用快捷键整理
- php字符串处理函数大全
- python下配置matplotlib开发环境
- document.createElement()用法中appendChild()、 insertBefore()的区别
- Javascript模块化编程(三):require.js的用法
- 写在前面 把自己的学习之路记录下来
- 保存密保在手机内存