算法學習(四)尋找滿足條件的兩個數或者多個數

尋找和爲定值的兩個數

編程之美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 16
using 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: [email protected]
    > Created Time: 2016年04月15日 星期五 09時42分14秒
 ************************************************************************/

#include <iostream>
#include <algorithm>
#include <stdlib.h>        //支持malloc
using 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中查找key
Position 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;

}
//插入key
void 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: [email protected]
    > 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: [email protected]
    > 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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章