1. 程式人生 > 實用技巧 >《趣學演算法》第三章 分治法原始碼

《趣學演算法》第三章 分治法原始碼

分治法程式碼實現

目錄

1、猜數遊戲——二分搜尋技術

//program 3-1
#include<iostream>
#include<cstdlib>
#include<algorithm>
using namespace std;
const int M=10000;
int x,n,i;
int s[M];

int BinarySearch(int n,int s[],int x)
{
   int low=0,high=n-1;  //low指向有序陣列的第一個元素,high指向有序陣列的最後一個元素
   while(low<=high)
   {
       int middle=(low+high)/2;  //middle為查詢範圍的中間值
       if(x==s[middle])  //x等於查詢範圍的中間值,演算法結束
          return middle;
       else if(x>s[middle])   //x大於查詢範圍的中間元素,則從左半部分查詢
              low=middle+1;
            else   //x小於查詢範圍的中間元素,則從右半部分查詢
              high=middle-1;
    }
    return -1;
}

int main()
{
    cout<<"該數列中的元素個數n為:";
    while(cin>>n)
    {
        cout<<"請依次輸入數列中的元素:";
        for(i=0;i<n;i++)
           cin>>s[i];
        sort(s,s+n);
        cout<<"排序後的陣列為:";
        for(i=0;i<n;i++)
        {
           cout<<s[i]<<" ";
        }
        cout<<endl;
        cout<<"請輸入要查詢的元素:";
        cin>>x;
        i=BinarySearch(n,s,x);
        if(i==-1)
          cout<<"該數列中沒有要查詢的元素"<<endl;
        else
          cout<<"要查詢的元素在第"<<i+1<<"位"<<endl;
    }
    return 0;
}


2、合久必分,分久必合——合併排序

//program 3-2
#include <iostream>
#include <cstdlib>
using namespace std;
void Merge(int A[], int low, int mid, int high)
{
    int *B=new int[high-low+1];//申請一個輔助陣列
    int i=low, j=mid+1, k=0;
    while(i<=mid && j<=high) {//按從小到大存放到輔助陣列B[]中
        if(A[i]<=A[j])
            B[k++]=A[i++];
        else
            B[k++]=A[j++];
    }
    while(i<=mid) B[k++]=A[i++];//將陣列中剩下的元素放置B中
    while(j<=high) B[k++]=A[j++];
    for(i=low, k=0; i<=high; i++)
        A[i]=B[k++];
}
void MergeSort(int A[], int low, int high)
{
    if(low<high)
    {
        int mid=(low+high)/2;//取中點
        MergeSort(A, low, mid);//對A[low:mid]中的元素合併排序
        MergeSort(A, mid+1, high);//對A[mid+1:high]中的元素合併排序
        Merge(A, low, mid, high);//合併
    }
}
int main()
{
    int n, A[100];
    cout<<"請輸入數列中的元素個數n為:"<<endl;
    cin>>n;
    cout<<"請依次輸入數列中的元素:"<<endl;
    for(int i=0; i<n; i++)
       cin>>A[i];
    MergeSort(A,0,n-1);
    cout<<"合併排序結果:"<<endl;
    for(int i=0;i<n;i++)
       cout<<A[i]<<" ";
    cout<<endl;
    return 0;
}

3、兵貴神速——快速排序

//program 3-3
#include <iostream>
using namespace std;
int Partition(int r[],int low,int high)//劃分函式
{
    int i=low,j=high,pivot=r[low];//基準元素
    while(i<j)
    {
        while(i<j&&r[j]>pivot) j--;//向左掃描
        if(i<j)
        {
            swap(r[i++],r[j]);//r[i]和r[j]交換後i+1右移一位
        }
        while(i<j&&r[i]<=pivot) i++;//向右掃描
        if(i<j)
        {
            swap(r[i],r[j--]);//r[i]和r[j]交換 後j-1左移一位
        }
    }

    return i;//返回最終劃分完成後基準元素所在的位置
}

int Partition2(int r[],int low,int high)//劃分函式
{
    int i=low,j=high,pivot=r[low];//基準元素
    while(i<j)
    {
        while(i<j&&r[j]>pivot) j--;//向左掃描
        while(i<j&&r[i]<=pivot) i++;//向右掃描
        if(i<j)
        {
            swap(r[i++],r[j--]);//r[i]和r[j]交換
        }
    }
    if(r[i]>pivot)
    {
        swap(r[i-1],r[low]);//r[i-1]和r[low]交換
        return i-1;//返回最終劃分完成後基準元素所在的位置
    }
    swap(r[i],r[low]);//r[i]和r[low]交換
    return i;//返回最終劃分完成後基準元素所在的位置
}

void QuickSort(int R[],int low,int high)//實現快排演算法
{
    int mid;
    if(low<high)
    {
        mid=Partition2(R,low,high); //基準位置
        QuickSort(R,low,mid-1);//左區間遞迴快排
        QuickSort(R,mid+1,high);//右區間遞迴快排
    }
}
int main()
{
    int a[100];
    int i,N;
    cout<<"請先輸入要排序的資料的個數:";
    cin>>N;
    cout<<"請輸入要排序的資料:";
    for(i=0;i<N;i++)
        cin>>a[i];
    cout<<endl;
    QuickSort(a,0,N-1);
    cout<<"排序後的序列為:"<<endl;
    for(i=0;i<N;i++)
        cout<<a[i]<<" " ;
    cout<<endl;
    return 0;
}

4、效率至上——大整數乘法

//program 3-4
#include <stdlib.h>
#include <cstring>
#include <iostream>
using namespace std;

#define M 100

char sa[1000];
char sb[1000];

typedef struct _Node
{
    int s[M];
    int l;            //代表字串的長度
    int c;
} Node,*pNode;

void cp(pNode src, pNode des, int st, int l)
{
    int i, j;
    for(i=st, j=0; i<st+l; i++, j++)
    {
        des->s[j] = src->s[i];
    }
    des->l = l;
    des->c = st + src->c;  //次冪

}

/*
分治法 大數乘法
X = A*10^n + B
Y = C*10^m + D
X*Y = A*C*10^(n+m) + A*D*10^n + B*C*10^m + B*D
*/

void add(pNode pa, pNode pb, pNode ans)
{
    int i,cc,k,palen,pblen,len;
    int ta, tb;
    pNode temp;
    if((pa->c<pb->c))   //保證Pa的次冪大
    {
        temp = pa;
        pa = pb;
        pb = temp;
    }
    ans->c = pb->c;
    cc = 0;
    palen=pa->l + pa->c;
    pblen=pb->l + pb->c;
    if(palen>pblen)
        len=palen;
    else
        len=pblen;
    k=pa->c - pb->c;
    for(i=0; i<len-ans->c; i++) //結果的長度最長為pa,pb之中的最大長度減去最低次冪
    {
        if(i<k)
            ta = 0;
        else
            ta = pa->s[i-k];//次冪高的補0,大於低的長度後與0進行計算
        if(i<pb->l)
            tb = pb->s[i];
        else
            tb = 0;
        if(i>=pa->l+k)
            ta = 0;
        ans->s[i] = (ta + tb + cc)%10;
        cc = (ta + tb + cc)/10;
    }
    if(cc)
        ans->s[i++] = cc;
    ans->l = i;
}

void mul(pNode pa, pNode pb, pNode ans)
{
    int i, cc, w;
    int ma = pa->l>>1, mb = pb->l>>1; //長度除2
    Node ah, al, bh, bl;
    Node t1, t2, t3, t4, z;
    pNode temp;

    if(!ma || !mb) //如果其中個數為1
    {
        if(!ma)   //如果a串的長度為1,pa,pb交換,pa的長度大於等於pb的長度
        {
            temp = pa;
            pa = pb;
            pb = temp;
        }
        ans->c = pa->c + pb->c;
        w = pb->s[0];
        cc = 0;     //此時的進位為c
        for(i=0; i < pa->l; i++)
        {
            ans->s[i] = (w*pa->s[i] + cc)%10;
            cc= (w*pa->s[i] + cc)/10;
        }
        if(cc)
            ans->s[i++] = cc; //如果到最後還有進位,則存入結果
        ans->l = i;          //記錄結果的長度
        return;
    }
    //分治的核心
    cp(pa, &ah, ma, pa->l-ma);  //先分成4部分al,ah,bl,bh
    cp(pa, &al, 0, ma);
    cp(pb, &bh, mb, pb->l-mb);
    cp(pb, &bl, 0, mb);

    mul(&ah, &bh, &t1);     //分成4部分相乘
    mul(&ah, &bl, &t2);
    mul(&al, &bh, &t3);
    mul(&al, &bl, &t4);

    add(&t3, &t4, ans);
    add(&t2, ans, &z);
    add(&t1, &z, ans);
}

int main()
{
    Node ans,a,b;
    cout << "輸入大整數 a:"<<endl;
    cin >> sa;
    cout << "輸入大整數 b:"<<endl;
    cin >> sb;
    a.l=strlen(sa);//sa,sb以字串進行處理
    b.l=strlen(sb);
    int z=0,i;
    for(i = a.l-1; i >= 0; i--)
        a.s[z++]=sa[i]-'0';             //倒向儲存
    a.c=0;
    z=0;
    for(i = b.l-1; i >= 0; i--)
        b.s[z++] = sb[i]-'0';
    b.c = 0;
    mul(&a, &b, &ans);
    cout << "最終結果為:";
    for(i = ans.l-1; i >= 0; i--)
        cout << ans.s[i];         //ans用來儲存結果,倒向儲存
    cout << endl;
    return 0;
}

//program 3-4-1
#include <stdlib.h>
#include <cstring>
#include <iostream>
using namespace std;

#define M 100

char sa[1000];
char sb[1000];

typedef struct _Node
{
    int s[M];
    int l;            //代表字串的長度
    int c;
} Node,*pNode;


void add(pNode pa, pNode pb, pNode ans)
{
    int i,cc,k,palen,pblen,len;
    int ta, tb;
    pNode temp;
    if((pa->c<pb->c))   //保證Pa的次冪大
    {
        temp = pa;
        pa = pb;
        pb = temp;
    }
    ans->c = pb->c;
    cc = 0;
    k=pa->c - pb->c;
    palen=pa->l + pa->c;
    pblen=pb->l + pb->c;
    if(palen>pblen)
        len=palen;
    else
        len=pblen;
    len=len-ans->c;
    for(i=0; i<len; i++) //結果的長度最長為pa,pb之中的最大長度減去最低次冪
    {
        if(i<k)
            ta = 0;
        else
            ta = pa->s[i-k];//次冪高的補0,大於低的長度後與0進行計算
        if(i<pb->l)
            tb = pb->s[i];
        else
            tb = 0;
        if(i>=pa->l+k)
            ta = 0;
        ans->s[i] = (ta + tb + cc)%10;
        cc = (ta + tb + cc)/10;
    }
    if(cc)
        ans->s[i++] = cc;
    ans->l = i;
}

int main()
{
    Node ans,a,b;//ans用來儲存結果,倒向儲存
    cout << "輸入大整數 a:"<<endl;
    cin >> sa;
    cout << "輸入大整數 a的次冪:"<<endl;
    cin >> a.c;
    cout << "輸入大整數 b:"<<endl;
    cin >> sb;
    cout << "輸入大整數 b的次冪:"<<endl;
    cin >> b.c;
    a.l=strlen(sa);//sa,sb以字串進行處理
    b.l=strlen(sb);
    int z=0,i;
    for(i = a.l-1; i >= 0; i--)
        a.s[z++]=sa[i]-'0';             //倒向儲存
    z=0;
    for(i = b.l-1; i >= 0; i--)
        b.s[z++] = sb[i]-'0';
    cout << "a=";
    for(i = a.l-1; i >= 0; i--)
        cout << a.s[i];
    cout << "*10^"<<a.c;
    cout << endl;
    cout << "b=";
    for(i = b.l-1; i >= 0; i--)
        cout << b.s[i];
    cout << "*10^"<<b.c;
    cout << endl;
    add(&a, &b, &ans);
    cout << "最終結果為:";
    for(i = ans.l-1; i>=0; i--)
        cout << ans.s[i];  //ans用來儲存結果,倒向儲存
    cout << "*10^"<<ans.c;
    cout<<endl;
    return 0;
}