参考:非常感谢该筒子,几乎每个问题都给了代码实现
这个也不错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); } }