把之前遇到的一些比较好的习题进行自我总结整理,以后会陆续添加。
一.螺旋数组
打印螺旋矩阵,如果矩阵长度为5,输出结果下图所示
1 2 3 4 5
16 17 18 19 6
15 24 25 20 7
14 23 22 21 8
13 12 11 10 9
C程序员面试宝典上的,两年前看过。
思路:
按照右,下,左,上四个方向依次给数组赋值。
数组最上行,最右列,最下行,最左列依次为0,n-1,n-1,0。
当按照右方向赋值结束时,最上行加1,
当按照下方向赋值结束时,最右列减1,
当按照左方向赋值结束时,最左列加1,
当按照上方向赋值结束时,最下行减1,
实际上,顺序改变初始方向顺序可以输出4个方向的螺旋矩阵。
我的解法如下(java):
public static void spiralRectPrint(int n){ if(n < 1){ System.out.println("输入参数不正确"); return; } final int TOTAL = n * n; final int[] DIRECTION = new int[]{0,1,2,3};//右,下,左,上 final int MAX_LENGTH = String.valueOf(TOTAL).length();//最大数位数,用于打印对齐 int firstRow = 0,firstCol = 0; int lastRow = n - 1,lastCol = n - 1; int directionIndex = 0;//初始方向为右 int num = 1; int[][] printArray = new int[n][n];//最后输出的数组 while(num <= TOTAL){ switch (DIRECTION[directionIndex % DIRECTION.length]) { case 0: for(int i = firstCol;i <= lastCol;++i) printArray[firstRow][i] = num++; ++firstRow; break; case 1: for(int i = firstRow;i <= lastRow;++i) printArray[i][lastCol] = num++; --lastCol; break; case 2: for(int i = lastCol;i >= firstCol;--i) printArray[lastRow][i] = num++; --lastRow; break; case 3: for(int i = lastRow;i >= firstRow;--i) printArray[i][firstCol] = num++; ++firstCol; break; } ++directionIndex; } //输出 for(int i = 0;i < n;++i){ for(int j = 0; j < n;++j){ int value = printArray[i][j]; for(int k = 0; k <= MAX_LENGTH - String.valueOf(value).length();++k) System.out.print(" "); System.out.print(value); } System.out.println(); } }
将给定的x个连续整数m-n(m < n),随机存放在一个大小为x+1的数组中,其中有且只有1个数k出现2次。
1.求出该数组
2.根据1中的数组和m,求出出现2次的数字
要求:
1,2中时间复杂度均为O(N),1中空间复杂度为O(N),2中空间复杂度为O(1)
1中输入参数为m,n,输出为整形数组arr
2中输入参数为arr和m,输出为重复数字k
思路:
1.很简单,建立一个大小为x+1的数组,前x个数组元素依次为m-n,最后一个元素随机,然后洗牌即可。
注意:在任意整数[m,n]随机的语句为:
random.nextInt(n - m + 1) + m
2.注意到k = 数组所有元素之和 - (m~n)所有数字之和 记为k = s1 - s2
其中s2可以用高斯公式计算
因为只需累加arr数组前x个元素,所以可以将结果放在arr最后一个元素中,这样就不需要任何空间。
我的解法如下(java):
第1问:
public static int[] createArray(int m,int n){ int[] arr = new int[n - m + 2]; Random random = new Random(); for(int i = 0;i < arr.length - 1;++i)arr[i] = m + i; //set random display two time number in[m,n] arr[arr.length - 1] = random.nextInt(n - m + 1) + m; System.out.printf("before find num %d display two times\n",arr[arr.length - 1]); //random array for(int i = 0;i < arr.length;++i){ int index = random.nextInt(arr.length); //swap if(i != index){ arr[i] ^= arr[index]; arr[index] ^= arr[i]; arr[i] ^= arr[index]; } } return arr; }
public static int getDisplayTwoTimesNumber(int[] arr,int m){ for(int i = 0;i < arr.length - 1;++i) arr[arr.length - 1] += arr[i]; return arr[arr.length - 1] - (m + m + arr.length - 2) * (arr.length - 1) / 2; }
三.概率问题
某位射击队员射中8,9,10环的概率分别为a,b,c,a+b+c=1
求该队员10次射击恰好射中k环的概率,其中80<=k<=100
输入参数:a,b,c,k
输出结果:恰好射中k环的概率,结果保留6位小数
示例:
输入:.25,.37,.38,98
输出:0.003091
好像在哪本程序员面试宝典上见过类似的题目。
思路:
理论上可能出现的情况有10^10种,但是每次射击只会有3种结果,所以总情况数应为3^10。
如何遍历3^10种情况就成了问题的关键点,因为每次射击的环数可以累加并相应地累加概率,如果总环数等于k,那么就将当前概率p累加至总概率P。
可以考虑用递归做。
但我认为用3进制做更好,可以将总情况数看做xxxxxxxxxx(3),x的取值为0,1,2,正好在是射击数数组8,9,10的3个下标。
第一次为0000000000(3),每次递增1,直到2222222222(3)
所以添加一个长度为10的整形伴随数组即可。
最后再用BigDecimal四舍五入。
我的解法如下(java)
public static double calcRatio(double[] parmas,int k) { final int TOTAL_TIME = 10; int[] scoreArr = new int[]{8,9,10}; int[] timeArr = new int[TOTAL_TIME]; final int TOTAL_COUNT = (int)Math.pow(scoreArr.length, TOTAL_TIME); double ratio = 1,totalRatio = 0; for(int i = 0,sum = 0;i < TOTAL_COUNT;++i,sum = 0,ratio = 1){ int time = i; for(int j = 0;j < timeArr.length;++j){ timeArr[j] = time % scoreArr.length; time /= scoreArr.length; sum += scoreArr[timeArr[j]]; ratio *= parmas[timeArr[j]]; } if(sum == k)totalRatio += ratio; } BigDecimal result = new BigDecimal(totalRatio).setScale(6,RoundingMode.HALF_UP); return result.doubleValue(); }
四.类似栈的数据结构
实现类似栈的数据结构(数据类型可以只为Integer),包含push(),pop(),findMin()3个方法,其中findMin()是输出当前数据结构中最小元素的值。
注意:所有的方法时间复杂度必须为O(1)!!!即不能使用循环遍历的方法找出最小值!!
思路:
出题目的人很坏,要注意到push(),pop()后,findMin()方法都可以正确调用,且时间复杂度均为O(1)
我用一个伴随数组记录最小值,从而解决问题。
之前发的帖子(第一问):
http://topic.csdn.net/u/20110618/21/44742d9b-b9bd-49c7-b754-3270dddc57f7.html
我的解法如下(java)
public class MyStack { private static final int SIZE = 100; private int[] data = new int[SIZE]; private int topOfArray = -1; private int topOfMinData = -1; private int size = 0; private int min; private int[] minData = new int[SIZE]; public MyStack(){ clear(); } public void clear(){ data = new int[SIZE]; minData = new int[SIZE]; topOfArray = -1; size = 0; } public void push(int x){ data[++topOfArray] = x; size++; if(topOfArray == 0 || data[topOfArray] <= min){ min = data[topOfArray]; minData[++topOfMinData] = data[topOfArray]; } } public int pop(){ size--; if(data[topOfArray] == min && topOfArray > 0){ min = minData[--topOfMinData]; } else if(topOfArray == 0){ clear(); return 0; } return data[topOfArray--]; } public int findMin(){ if(topOfArray == -1){ return Integer.MAX_VALUE * (-1); } return minData[topOfMinData]; } }
五.洗牌
在给定的数[m,n]内随机产生k个不同的数。或者是随机获取集合中的的某些不同元素的集合。
思路:
不能无脑random,再判断random后的数在不在已经生成的数列中。因为当k很大时(接近n-m+1),随到不同的数概率非常低,严重影响效率。
用洗牌的方法,一次性产生包含m~n所有数的数组,然后打乱顺序取前k个。
下面附上我的扑克牌类,其中包含的洗牌方法(java)。
public class Poker { private final static int CARD_COUNT = 52; private static String[] CARD_FLOWERS = new String[]{"Spade","Heart","Club","Diamond"}; private static String[] CARD_VALUES = new String[]{"A","2","3","4","5","6","7","8","9", "10","J","Q","K"}; private static String[] CARD_GHOSTS_FLOWERS = new String[]{"ghost","ghost"}; private static String[] CARD_GHOSTS_VALUES = new String[]{"Small","Big"}; private String flower; private String value; private int index; private static Poker[] _pokers; public String getFlower() { return flower; } public String getValue() { return value; } public int getIndex() { return index; } public Poker(String flower,String value,int index){ this.flower = flower; this.value = value; this.index = index; } private static Poker[] getCardsInstance(){ if(_pokers != null){ return _pokers; } _pokers = new Poker[CARD_COUNT]; for(int i = 0;i < CARD_FLOWERS.length;++i){ for(int j = 0;j < CARD_VALUES.length;++j){ int index = i * CARD_VALUES.length + j; _pokers[index] = new Poker(CARD_FLOWERS[i], CARD_VALUES[j], index + 1); } } for(int i = CARD_GHOSTS_FLOWERS.length - 1;i >= 0;--i){ int index = CARD_COUNT - i; _pokers[index - 1] = new Poker(CARD_GHOSTS_FLOWERS[i], CARD_GHOSTS_VALUES[i], index); } return _pokers; } public static Poker[] shufflingCards(){ Random random = new Random(); Poker[] pokers = getCardsInstance(); for(int i = 0;i < pokers.length;++i){ int index = random.nextInt(pokers.length); if(i != index){ Poker cardTemp = pokers[i]; pokers[i] = pokers[index]; pokers[index] = cardTemp; } } return pokers; } public String toString(){ return this.flower + this.value; } public static void displayPlayerCards(Poker[] poker){ final int PLAYERS = 4; for(int i = 1;i <= poker.length;++i){ System.out.print(poker[i - 1].toString() + " "); if(i % (poker.length / PLAYERS) == 0)System.out.println(); } } }
今天暂时就写这么多,以后再补充。
2012.8.29