习题总结(一)

把之前遇到的一些比较好的习题进行自我总结整理,以后会陆续添加。


一.螺旋数组

打印螺旋矩阵,如果矩阵长度为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;	
	}

第2问:

	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


你可能感兴趣的:(java,数据结构,String,面试,Integer,Random)