经典算法面试题(二)

1 . 大整数乘法

下面先介绍“列表法”:
例如当计算8765*234时,把乘数和被乘数照如下列出,见表:

8 7 6 5 *
16 14 12 10 2
24 21 18 15 3
32 28 24 20 4
16 14 12 10
24 21 18 15
32 28 24 20
16 38 65 56 39 20
16 38 65 56 39 20
2 16+4=20 38+7=45 65+6=71 56+4=60 39+2=41
留2 留0进2 留5进4 留1进7 留0进6 留1进4 留0进2
2 0 5 1 0 1 0

根据以上思路 就可以编写C++程序了,再经分析可得:

1,一个m位的整数与一个n位的整数相乘,乘积为m+n-1位或m+n位。
2,程序中,用三个字符数组分别存储乘数,被乘数与乘积。由第1点分析知,存放乘积的字符数组饿长度应不小于存放乘数与被乘数的两个数组的长度之和。
3,可以把第二步“计算填表”与第三四步“累加进位”放在一起完成,可以节省存储表格2所需的空间。
4,程序关键部分是两层循环,内层循环累计一数组的和,外层循环处理保留的数字和进位。
#include <iostream>
#include <string>

using namespace std;

int  resualt[2048] = { 0 };
string num1, num2;

void multiply(string n1,string n2)
{
    int n1len = n1.length();
    int n2len = n2.length();
    int sum = 0;
    int carry = 0;
    int i, j;
    for (i = n1len + n2len-2; i >=0; i--)
    {
        sum = carry;
        j = i - n1len+1;
        if (j < 0)
            j = 0;
        for (; j <= i&&j <= n2len-1; j++)
            sum += (n1[i - j] - '0')*(n2[j] - '0');
        resualt[i+1] = sum % 10;
        carry = sum / 10;
    }
    if (carry>0)
        resualt[0] = carry ;
}

int main()
{
    cin >> num1 >> num2;
    multiply(num1, num2);
    for (int y = 0; y <num1.length() + num2.length(); y++)
        cout << resualt[y];
    return 0;
}

算法改进:
8216547*96785 将两数从个位起,每3位分为节,列出乘法表,将斜线间的数字相加:

8 216 547
96 785
8 216 547 *
768 20736 52512 96
6250 169560 429395 785
768 20736 52512
6250 169560 429395
768 27016 222072 429395

将表中最后一行进行如下处理:从个位数开始,每一个方格里只保留三个数字,超出1000的部分进位到前一个方格里:

768 27016 222072 429395
768+27=795 27016+222=27238 222072+429=222501 留395进429
795 238 501 395

所以8216547*96785 = 795238501395

也就是说我们在计算生成这个二维表时,不必一位一位的乘,而可以三位三位的乘;在累加时也是满1000进位。这样,我们计算m位整数乘以n位整数,只需要进行m*n/9次乘法运算,再进行约(m+n)/3次加法运算和(m+n)/3次去摸运算。总体看来,效率是前一种算法的9倍。

2 . 哈夫曼树

定义哈夫曼树之前先说明几个与哈夫曼树有关的概念:

路径: 树中一个结点到另一个结点之间的分支构成这两个结点之间的路径。
路径长度:路径上的分枝数目称作路径长度。
树的路径长度:从树根到每一个结点的路径长度之和。
结点的带权路径长度:在一棵树中,如果其结点上附带有一个权值,通常把该结点的路径长度与该结点上的权值 之积称为该结点的带权路径长度(weighted path length)
树的带权路径长度:如果树中每个叶子上都带有一个权值,则把树中所有叶子的带权路径长度之和称为树的带
                               权路径长度。

设某二叉树有n个带权值的叶子结点,则该二叉树的带权路径长度记为:
经典算法面试题(二)_第1张图片
公式中,Wk为第k个叶子结点的权值;Lk为该结点的路径长度。
示例:

根据哈弗曼树的定义,一棵二叉树要使其WPL值最小,必须使权值越大的叶子结点越靠近根结点,而权值越小的叶子结点
越远离根结点。
哈弗曼依据这一特点提出了一种构造最优二叉树的方法,其基本思想如下:
经典算法面试题(二)_第2张图片
下面演示了用Huffman算法构造一棵Huffman树的过程:
经典算法面试题(二)_第3张图片

3 . 由一个等概率的随机函数生成另一个等概率的随机函数

知有个rand7()的函数,可以生成等概率的[1,7]范围内的随机整数,让利用这个rand7()构造rand10()函数,生成等概率的[1,10]范围内的随机整数。

已知有个rand7()的函数,可以生成等概率的[1,7]范围内的随机整数,让利用这个rand7()构造rand10()函数,生成等概率的[1,10]范围内的随机整数。

分析:要保证rand10()在整数1-10的均匀分布,可以构造一个1-10*n的均匀分布的随机整数区间(n为任何正整数)。假设x是这个1-10*n区间上的一个随机整数,那么x%10+1就是均匀分布在1-10区间上的整数。由于(rand7()-1)*7+rand7()可以构造出均匀分布在1-49的随机数(原因见下面的说明),可以将41~49这样的随机数剔除掉,得到的数1-40仍然是均匀分布在1-40的,这是因为每个数都可以看成一个独立事件。
下面说明为什么(rand7()-1)*7+rand7()可以构造出均匀分布在1-49的随机数:
首先rand7()-1得到一个离散整数集合{0,1,2,3,4,5,6},其中每个整数的出现概率都是1/7。那么(rand7()-1)*7得到一个离散整数集合A={0,7,14,21,28,35,42},其中每个整数的出现概率也都是1/7。而rand7()得到的集合B={1,2,3,4,5,6,7}中每个整数出现的概率也是1/7。显然集合A和B中任何两个元素组合可以与1-49之间的一个整数一一对应,也就是说1-49之间的任何一个数,可以唯一确定A和B中两个元素的一种组合方式,反过来也成立。由于A和B中元素可以看成是独立事件,根据独立事件的概率公式P(AB)=P(A)P(B),得到每个组合的概率是1/7*1/7=1/49。因此(rand7()-1)*7+rand7()生成的整数均匀分布在1-49之间,每个数的概率都是1/49。

int rand_10()    
{    
    int x = 0;    
    do    
    {    
        x = 7 * (rand7() - 1) + rand7();    
    }while(x > 40);    
    return x % 10 + 1;    
}

另一种思路:第一个数由rand7()产生,第二个由rand7()+1产生,,,第10个由(rand7()+9)%10产生,由于是等概率的,以此循环,也生成的等概率的1-10之间的随机数。

你可能感兴趣的:(算法)