转载请注明原文地址:http://www.cnblogs.com/ygj0930/p/6437550.html
心得:在读完题目对普遍输入的处理有了初步构思后,要往空输入、溢出、无效输入等情况去寻找函数漏洞,编码时一一处理特殊的输入。
编码前先设置测试用例!
1:反转句子中的单词顺序,但单词本身的字符顺序不变,如:i am a student->student a am i
Java解法:Java提供了split()方法,我们可以直接用String[] strs=inputstring.split(" ");获得句子的单词,然后倒序遍历strs输出即可。
C++解法:首先反转整个句子的字符顺序,然后对每个单词字符再逐个反转回来。定义一个反转函数从begin到end交换头尾指针所指。然后定义一个区分单词的函数:寻找空格所在从而确定单词字符串的begin、end,然后调用反转函数把单词字符再反转回来即可。
2:左旋字符串:把字符串的前n个字符保持顺序移动到字符串的后面。比如: abcde移动前二——>cdeab。
Java解法:用StringBuilder把字符串字符重新拼接就好。
C++解法:我们把整个字符串反转后发现abcde->edcba,把字符串分为两部分来反转:edc->cde,ba->ab,结果变为cdeab,就是我们要得到的结果。那么我们就可以得到解题思路了:先把字符串反转,然后对字符串的后n个字符的字符串反转,再对0~n-1段字符反转,即可实现左旋。思路同上一题,定义一个反转函数,再定义一个函数来控制反转的下标范围。
3:投n个骰子,求面朝上的点数和S的所有值出现的概率。
我们来模拟一下:
假设已有的骰子和为S的次数为k,现在我们来增加一个骰子;新的骰子只会出现1~6中随机一个的一次,为保持和仍为S,那么剩下就要投出和为 S-6~S-1 的情况。我们用一个数组sum_count[]来保存骰子们和的情况,第S个说明骰子和为S出现的次数。由上面描述可知,每新增一个骰子,为保持和为S,则次数为 sum_count(S-6)+...+sum_count(S-1)为新的和为S的次数。
void probablyCount(int num,int MaxValue){ if(num<1){ return; } int[][] counts=new int[2][num*MaxValue+1]; for(int i=0;i<MaxValue*num+1;++i){ counts[0][i]=0; counts[1][i]=0; } int which_count=0; //1个骰子时,数组前6位记录和为1~6出现的次数 for(int i=1;i<=MaxValue;++i){ counts[which_count][i]=1; } //逐步增加骰子,骰子的和也逐步增加。而和为M就记录在第M下标 for(int k=2;k<=num;++k){ for(int i=0;i<k;++i){ counts[1-which_count][i]=0; } for(int i=k;i<=k*MaxValue;++i){//i记录了K个骰子的点数和k~k*MaxValue counts[1-which_count][i]=0;//用另一个数组记录点数i的次数,先置0 //j是新增的骰子的点数范围:1~Max,且j的点数要小于和 for(int j=1;j<=i && j<=MaxValue;++j){ //那么和为i的次数等于 i-j的次数和 counts[1-which_count][i]+=counts[which_count][i-j]; } } which_count=1-which_count; } double total=Math.pow(MaxValue, num); for(int i=num;i<=num*MaxValue;++i){ System.out.println("Count="+i+",Radio="+counts[which_count][i]/total); } }
4:扑克牌顺子判断
抽5张扑克牌,判断是不是顺子。扑克牌A=1,J=11,Q=12,K=13,大小王可以看作任意数字。
既然我们可以把JQK转换成数字,我们不妨把大小王也转换成0,也就是说数组中的0就可以转换成任意数字。
这样思路就清晰了:把这5张牌用一个int[5]数组保存起来,注意存的时候JQK和大小王转为相应数字。然后对数组排序。如果有大小王,那么数组前面绝对是0.我们先来统计0的个数,得到king_count;然后从非0的第一个元素开始,统计递增序列的间隔数lack_count;然后比较king_count与lack_count即可,如果0大于等于lack_count,说明可以用大小王补间隔成为顺子。特殊情况:非0元素有对子出现,那么决定不是顺子,在统计间隔的时候增加一条如果当前元素与下一元素相等,则直接返回false。(由于间隔统计是从第一个非0元素开始的,所以不会统计到前面的相同0)。
5:每次删除圆环中第m个数字,求最后剩下的数字——Josephuse环问题
传统解法:用一个环形链表模拟圆环,每次遍历m步删除所指结点;
优化解法:我们发现:n结点时删除第m个数字最终剩下的数字下标last,由(last+m)%n可以得到n+1个结点时删除第m个数字最终剩下的数字下标。根据此递推式,所以我们用递归或者循环就可以从2开始推导出来。注意:只有1个结点时不成环,返回-1.
6:求1+2+...+n
高斯定理的使用:1+2+...+n=n(n+1)/2
发散思维:构造函数求法:定义两个全局变量:num、sum,在一个类的构造函数中进行num++,sum+=num;然后创建一个包含n个类对象的数组即可调用n次构造函数,而这n个对象依次创建就得到了1+2+3+...+n
7:把字符串转换成数字:
字符串除了是常规的数字字符串外,还可能是null、可能会溢出、可能是包含+-*/号的表达式、可能是+-数,还可能是非数字的无效输入。
常规输入的求法:sum=sum*10+num[i++];(i<=num.length)
8:求一棵树中两个结点的最近公共祖先。
如果是BST树:我们可以用递归查找:两个结点都在比根小,则在左边,递归左子树直到两结点分别位于当前根结点的两边,那么当前根节点即为所求;同理右边也是;
如果是普通树:我们可以用两个辅助链表,分别保存根节点到这两个结点的路径;然后问题转化为求两个链表的最右一个公共结点:因为根到最后一个公共结点段是相同的,之后才分叉。
9:找到数组中的重复数字
传统解法:用hashmap,遍历数组,用map.get(nums[i])判断是否已有这个元素,没有则map.put(),有返回则说明前面已出现过,此元素重复;
优化解法:用交换法把数字n放到下标n处——i从0开始遍历,若nums[i]==i,说明nums[i]处于合适位置;若nums[i]==j,比较nums[i]与nums[j],相等这说明i重复;不等则把nums[i]与nums[j]交换,把j放到合适的地方去。
10:给定数组A[n],令B[i]=A[0]*A[1]*...*A[i-1] * A[i+1]*...*A[n-1],求数组B[],不能使用除法。
定义一个连乘函数multi(int1,int2)计算A[int1]到A[int2]的连乘积;然后B[i]=multi(0,i-1)*multi(i+1,n-1)。