YY算法问题--活跃大脑

来源:互联网 发布:装配式结构优化设计 编辑:程序博客网 时间:2024/04/28 02:56

参考:非常感谢该筒子,几乎每个问题都给了代码实现

           这个也不错http://wenku.baidu.com/view/3ba6d522a5e9856a5612609b.html


1.判断单链表是否有环

定义两个指针: p1指向head, p2指向head->next

                        然后每次把p1后移一个节点、p2后移两个节点

                        当p1==p2时说明有环,当p2到链表末尾说明没有环

原理:A和B跳方格,A每次跳1格,B每次跳2格,这样A和B之间的距离一步步拉大,1、2、3、4、5...

            当距离达到格子环的周长(及倍数)时两人重合。


2.用一行代码的递归对数组求和

int MySum( int* array,  int  n){    return n>1?(MySum(array, n-1)+array[n-1]):array[0];}

原理:当数组元素个数n大于1时,用前n-1个元素的和加上array[n-1];

            当数组元素n==1时,直接返回元素值array[0]就是整个数组元素和。


3.将原串循环移位(比如ABCDEFG移位成FGABCDE)后如何判断原串中是否存在某子串(比如EFG)

在给定串str后面接上str,在新得到的串里面查找目标串,成功说明原串包含目标串,失败说明原串不含目标串。


4.某元素在整数数组A中出现的次数超过元素个数的一般,如何确定该元素是什么

1>排序,取中间的元素就是目标元素

2>int i = 0, count=0; int result=A[i]; i递增,当A[i]==result时count++,当A[i]!=result时count--,当count<0时result=A[i]

    原理(第二种解法的原理):相同元素的个数减去其他所有元素的个数结果大于零


5.如何快速确定某整数是否在一个被顺序移位的有序整数数组中


6.求两个有序整数数组的共同元素

定义两个指针pA、pB分别指向数组A、B第一个元素

当*pA>*pB时,pB += 1

当*pA<*pB时,pA += 1

当*pA==*pB时(找到一个相同元素),pA+=1,pB+=1

当pA和pB分别指向A、B末尾元素时退出


7.一个含n(n>1)个元素的数组,内部存储整数1..n-1,其中有一个整数重复,如何找到重复的整数

对数据求和,减去1...n-1的和,结果就是重复的整数


8.一个含有n个元素的整型数组中,只有一个元素出现的次数是奇数,如何确定该元素

int tmp = 0; 遍历数组,将所有元素跟tmp异或后赋值给tmp,遍历结束时tmp的值就是目标元素

原理:k^k=0  k^0=k  a^b^c=a^(b^c)


9.给定两个有序整型数组a和b,各有n个元素,求两个数组中满足给定和的数对,即对a中元素i和b中元素j,满足i + j = d(d已知)

定义两个指针分别pA、pB分别指向a开头和b结尾

当*pA+*pB>d,pB -=1

当*pA+*pB<d,pA+=1

当*pA+*pB==d(找到一对儿),pA+=1,pB-=1

原理:不解释了,列举两个数组看看,你懂的


10.给定一个整型数组a,求出最大连续子段之和,如果和为负数,则按0计算,比如1, 2, -5, 6, 8则输出6 + 8 = 14

// 子数组的最大和int Sum(int* a, int n){    int curSum = 0;    int maxSum = 0;    for (int i = 0; i < n; i++)    {        if (curSum + a[i] < 0)            curSum = 0;        else        {            curSum += a[i] ;            maxSum = max(maxSum, curSum);        }    }    return maxSum;}

原理:先定一个游戏规则 【从左起第一个元素开始组队,当队伍不能自负盈亏时(和为负)完成该队伍组队,从下一个元素开始重新组队】

            在该规则下我们发现,右边的队伍拉上左边队伍里面的子队伍是很不划算的,因为你拉上的子队伍是亏损的(如果该子队伍盈利,就说明其所在队伍里面靠左边的子队伍是亏损的,跟规则抵触)

            我们追求的是组队过程中出现的高潮


11.给定一个整型数组a,求出最大连续子段的乘积,比如 1, 2, -8, 12, 7则输出12 * 7 = 84

// 子数组的最大乘积int MaxProduct(int *a, int n){    int maxProduct = 1; // max positive product at current position    int minProduct = 1; // min negative product at current position    int r = 1; // result, max multiplication totally    for (int i = 0; i < n; i++)    {        if (a[i] > 0)        {            maxProduct *= a[i];            minProduct = min(minProduct * a[i], 1);        }        else if (a[i] == 0)        {            maxProduct = 1;            minProduct = 1;        }        else // a[i] < 0        {            int temp = maxProduct;            maxProduct = max(minProduct * a[i], 1);            minProduct = temp * a[i];        }        r = max(r, maxProduct);    }    return r;}

原理:先定规则【从第一个元素开始组队,遇到零结束队伍,并从下一个元素开始重新组队】在组队过程中记录下组队的最大正值和最小负值,初始都是1。遇正数最大正值继续变大,最小负值保持不动;遇负数把最小负值(!=1)赋给最大正值,把最大正值乘以负值后赋值给最小负值。最大正值的峰值就是欲求值。


12.将一个含有n个元素的数组向右循环移动k位,要求时间复杂度是O(n),且只能使用两个额外的变量

原理:1 2 3 4 5 移位结果 4 5 1 2 3 , 分成两部分1 2 3 | 4 5,先将1 2 3逆序(首尾互换位置,首尾指向不断往中间靠拢)变成3 2 1,然后将3 2 1 | 4 5整体逆序


13.给定一个含有n个元素的字符数组a,将其原地逆序

和上面整型数组的逆序是一样的


14.给定一个含有n个元素的整型数组a,找出其中的最大值和最小值

分治法 递归

15.给定一个含有n个元素的整型数组,求其最大值和次大值

分治法 递归 和上题一样,只是在需要根据大小合并一下


16.给定一个含有n个元素的整型数组a,从中任取m个元素,求所有组合

计算组合数量原理:f(n,m) = f(n-1,m-1) + f(n-2,m-1) + f(n-3,m-1) + .. + f(m-1,m-1)

           if  ( n == m ) :  f(n,m) = 1

           if ( m == 1 ) :  f(n,m) =  n

           例:f(5,3) = f(4,2) + f(3,2) + f(2,2) = (f(3,1)+ f(2,1) + f(1,1)) + (f(2,1) + f(1,1)) + f(2,2) = (3+2+1) + (2+1) + 1 = 10

计算组合的原理:f(array,n,m) = (*array + f(array+1,n-1,m-1)) + (*(array+1) + f(array+2,n-2,m-1)) + .. + (*(array+n-m) + f(array+n-m+1,m-1,m-1))

                              

                              if  ( n == m ) : f(array,n,m) = (*array + *(array+1) + *(array+2) + .. + *(array+n-1)) 

                               //所有元素作为一个集合

                            

                              if ( m == 1 ) : f(arary,n,m) = (*array) + (*(array+1)) + (*(*array+2)) + .. + (*(array+n-1)) 

                               //每个元素是单独的集合


                              例:f([1,2,3,4,5],5,3) = (print(1) + f([2,3,4,5],4,2)) + (print(2) + f([3,4,5],3,2)) + (print(3) + f([4,5],2,2))

                                                                  = (pirnt(1) + (print(2) + f([3,4,5],3,1)) +

                                                                     (print(3) + f([4,5],2,1)) + (print(4) + f([5],1,1)) )  + ( print(2) + (print(3)+f([4,5],2,1)) +(print(4)+f([5],1,1)) ) +

                                                                     (print(3) + f([4,5],2,2))

                                                                  =(1,2,3) (1,2,4) (1,2,5) (1,3,4) (1,3,5) (1,4,5) (2,3,4) (2,3,5) (2,4,5) (3,4,5)


17.给定含有n个元素的两个有序(非降序)整型数组a和b。合并两个数组中的元素到整型数组c,要求去除重复元素并保持c有序(非降序)

原理:连个指针分别指向两个数组首地址,在往后移动的过程中根据两个指针所指元素大小来决定谁移动(把要移动的那个指针所指元素拷贝到新数组),其中一个指针达到末尾后把另一个数组剩余的元素拷贝到新数组


18.给定含有n个元素的整型数组a,其中包括0元素和非0元素,对数组进行排序,要求:1. 排序后所有0元素在前,所有非零元素在后,且非零元素排序前后相对位置不变 2. 不能使用额外存储空间

原理:跟冒泡有点像,从后往前遍历,遇到非零时把非零值赋给到泡泡串的最后那个泡泡,然后把非零值的槽槽置零


19.给定一个有序整数序列(非递减序),可能包含负数,找出其中绝对值最小的元素,比如给定序列 -5, -3, -1, 2, 8 则返回1

原理:先处理特殊情况,如果开头是正数(说明全是正数),第一个元素就是欲求值;如果结尾是非正数(全是非正数),最后一个元素就是欲求值;否则,欲求值在正负交界处,下面处理这个情况

           用分治法(折半查找)来做,找到中点后:

           如果中点是0,中点即欲求值;

           如果中点是正数,查看中点左边的元素,该值为负就比较得到欲求值,为零即得欲求值,为正则抛弃右半边继续处理左边;

           如果中点是负数,查看中点右边的元素,该值为正就比较得到欲求值,为零即得欲求值,为正则抛弃左半边继续处理右边


20.有一根27厘米的细木杆,在第3厘米、7厘米、11厘米、17厘米、23厘米这五个位置上各有一只蚂蚁。木杆很细,不能同时通过一只蚂蚁。开始时,蚂蚁的头朝左还是朝右是任意的,它们只会朝前走或调头,但不会后退。当任意两只蚂蚁碰头时,两只蚂蚁会同时调头朝反方向走。假设蚂蚁们每秒钟可以走一厘米的距离。编写程序,求所有蚂蚁都离开木杆的最小时间和最大时间。

原理:当任意两只蚂蚁碰头时,两只蚂蚁会同时调头朝反方向走。“
           其实你可以当作它们擦肩而过。
           所以,最长时间应该是 最左短端的蚂蚁一直往右走 和 最右短端的蚂蚁一直往左走 两个时间的最大值。(不考虑别的蚂蚁,一直走就对了)
           最小时间应该是 左边的3个蚂蚁往左走,右边的2个蚂蚁往右走 花的时间。

#include <iostream>#include <algorithm>using namespace std;class Ant{private:        static int LONG;        int a[5];        int minTime;        int maxTime;public:        Ant() : minTime(0), maxTime(0)        {                a[0] = 3;                a[1] = 7;                a[2] = 11;                a[3] = 17;                a[4] = 23;        };        void gogogo()        {                for (int i = 0; i < 5; i++) {                        minTime = max(minTime, min(a[i], LONG - a[i]));                        maxTime = max(maxTime, max(a[i], LONG - a[i]));                }        }        int getMax() {  return maxTime;  }        int getMin() {  return minTime;  }};int Ant::LONG = 27;int main(int argc, char** argv){    Ant* client = new Ant();    client->gogogo();    cout << "最慢" << client->getMax() << endl;    cout << "最快" << client->getMin() << endl;}

21.给定几个10G的大文件,文件中一行存储一个字符串,要求每个字符串出现的频率

原理:受内存限制,顺序扫描并在内存中统计各字符串的频率是不靠谱的

            顺序扫描把每个字符串用md5散列成一个数值,并在以该数值为文件名的文件中记录对应的信息(比如追加一个字节的数据),扫描结束后统计文件的大小就可以得到每个字符串出现的次数


22.一个数组,下标从0到n,元素为从0到n的整数。判断其中是否有重复元素

原理:从前往后遍历元素,没抓住一个就死咬住它刨根究底:找到它为索引的元素,修改目标值为-1标记这个索引值已经在出现过一次了(那么下次以这个索引值取出元素==-1时表示有重复),然后把目标原值赋值给遍历正在访问的元素;接着再以这个元素值为索引去找,如果这个索引值找到的是自己就不要死循环了,继续下一个元素的遍历。

code:

int hasDuplicate(int a[], int n){for (int i = 0; i < n; ++i) {while ((a[i]!=i) && (a[i]!=-1)) {int index = a[i];if (a[index] == -1) {return 1;} else {a[i] = a[index];a[index] = -1;}}if (a[i] == i)a[i] = -1;}return 0;}int main(int argc, char** argv){DUPLICATE_TEST_FLAG:int n;scanf("%d", &n);int tmp;int* aa = new int[n];for (int i = 0; i < n; ++i) {scanf("%d", &tmp);aa[i] = tmp;}cout << (hasDuplicate(aa,n)?"有重复":"没有重复");goto DUPLICATE_TEST_FLAG;return 0;}

23.求二叉树的深度

原理:分别求左右子树的深度,取其大者加一即得整个树的深度。对左右子树深度的求解采用前述方式递归。

伪代码:

int depth(Tree t) {if (!t)return 0;else {int a = depth(t.right);int b = depth(t.left);return (a>b)?(a+1):(b+1);}}


原创粉丝点击