算法学习(四)寻找满足条件的两个数或者多个数

来源:互联网 发布:云计算 网络强国 编辑:程序博客网 时间: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;}
0 0
原创粉丝点击