zoukankan      html  css  js  c++  java
  • 算法--2016搜狐面试:员工一起玩扎金花

    扎金花这种小游戏,我想作为一名程序员。大部分小时候都玩过吧!现在我们一起来看看搜狐这道面试题吧!看看如何用代码实现扎金花。


    Q题目

    事因

    两个搜狐的程序员加了一个月班,终于放假了,于是他们决定扎金花渡过愉快的假期 。
    

    游戏规则:

    共52张普通牌,牌面为2,3,4,5,6,7,8,9,10,J,Q,K,A之一,大小递增,各四张; 每人抓三张牌。两人比较手中三张牌大小,大的人获胜。

    对于牌型的规则如下:

    • 1.三张牌一样即为豹子

    • 2.三张牌相连为顺子(A23不算顺子)

    • 3.有且仅有两张牌一样为对子 豹子>顺子>对子>普通牌型 在牌型一样时,比较牌型数值大小(如AAA>KKK,QAK>534,QQ2>10104) 在二人均无特殊牌型时,依次比较三张牌中最大的。大的人获胜,如果最大的牌一样,则比较第二大,以此类推(如37K>89Q) 如二人牌面相同,则为平局。

    输入描述:

    输入两个字符串代表两个玩家的牌(如”10KQ” “354”),
    先输入的作为玩家1,后输入的作为玩家2
    

    输出描述:

     1 代表 玩家1赢     
     0 代表 平局   
     -1 代表 玩家2赢 
     -2 代表不合法的输入
    

    输入例子:

    KQ3 3Q9
    10QA 6102
    5810 7KK
    632 74J
    10102 K77
    JKJ 926
    68K 27A
    

    输出例子:

    1
    1
    -1
    -1
    1
    1
    -1
    

    A解法

    1.逻辑分析

    • (1)拿到玩家1和2输入的字符串,判断是否合法

    • (2)合法后,拆分字符串为字符串数组

    • (3)将字符串数组转化为int数组,并排序

    • (4)判断3张牌的相等情况

    • (5)比较大小,谁输谁赢

    2.难点分析

    • 存在10时,字符串的拆分问题:可以根据字符串长度来判断拆分

    • 将字母转为数字:先将拿到的字符串都转为大写,这样小写和大写字母都一样了,然后直接用if判断return就可以了

    • 比较谁输谁赢:采用从大到小的方式比较,先判断是否有豹子,在判断顺子,再判断对子,最后判断无牌型的

    • 对顺子的处理问题

    3.代码实现

    package 搜狐面试2016;
    
    import java.util.Arrays;
    import java.util.Scanner;
    
    public class Test1 {
    	public static void main(String[] args) {
    		// 2,3,4,5,6,7,8,9,10,J,Q,K,A
    		Scanner scanner = new Scanner(System.in);
    		boolean isContinue=true;
    		while (isContinue) {
    			//1.游戏规则
    			System.out.println("游戏规则:共52张普通牌,牌面为2,3,4,5,6,7,8,9,10,J,Q,K,A之一,大小递增,各四张; 每人抓三张牌。两人比较手中三张牌大小,大的人获胜。");
    			System.out.println("对于牌型的规则如下:");
    			System.out.println("1.三张牌一样即为豹子");
    			System.out.println("2.三张牌相连为顺子(A23不算顺子)");
    			System.out.println("3.有且仅有两张牌一样为对子 豹子>顺子>对子>普通牌型 在牌型一样时,比较牌型数值大小");
    			System.out.println("谁输谁赢:1 --代表玩家1赢;0 --代表 平局   ;-1 --代表玩家2赢 ;-2 --代表不合法的输入");
    			 
    			//2.分别出牌
    			System.out.println("请玩家1出牌:");
    			String num1 = scanner.next();
    			System.out.println("请玩家2出牌:");
    			String num2 = scanner.next();
    			
    			//3.判断是否合法
    			boolean flag=isValid(num1, num2);
    			if(!flag){
    				//不合法
    				System.out.println("-2");
    			}else {
    				//输入合法---先拆分字符串---再转化为int数组
    				//4.拆分字符串
    				String[] nums1=getStrArray(num1);
    				String[] nums2=getStrArray(num2);
    				System.out.println("拆分后的字符串数组A:"+Arrays.toString(nums1));
    				System.out.println("拆分后的字符串数组B:"+Arrays.toString(nums2));
    				
    				//5.转化为int数组
    				int[] nums11=strToNumber(nums1);
    				int[] nums22=strToNumber(nums2);
    				System.out.println("转化为int后的数组A:"+Arrays.toString(nums11));
    				System.out.println("转化为int后的数组B:"+Arrays.toString(nums22));
    				
    				//6.获得三张牌的相等情况
    				int[] equalNum11=equalNum(nums11);
    				int[] equalNum22=equalNum(nums22);
    				System.out.println("三张牌的相等情况--数组A:"+Arrays.toString(equalNum11));
    				System.out.println("三张牌的相等情况--数组B:"+Arrays.toString(equalNum22));
    				
    				//7.判断输赢
    				int whoWin=whoWin(equalNum11, nums11, equalNum22, nums22);
    				System.out.println(""+whoWin);
    				
    			}
    			
    			//是否继续
    			System.out.println("是否继续?输入N或n退出,其他任意键继续!");
    			String string = scanner.next();
    			string=string.toUpperCase();
    			if("N".equals(string)){
    				isContinue=false;
    			}
    		}
    
    	}
    
    	/*1.判断输入的内容是否合法
    	 * 			不合法两种情况:(1)出现的字符不是2,3,4,5,6,7,8,9,10,J,Q,K,A
    						        (2)每种牌只有4张,超过4张则不合法了
    	 *方法说明:
    	 *该方法只处理情况(1),情况(2)放在判断输赢的时候处理,因为第二种情况涉及牌面转化后计算的问题*/
    	public static boolean isValid(String num1, String num2) {
    		String reg = "([2-9JQKA]|10){3}";// 正则匹配,只能出现2,3,4,5,6,7,8,9,10,J,Q,K,A,并且一共只能出现3次
    		boolean a = num1.matches(reg);
    		boolean b = num2.matches(reg);
    
    		// 有一方不合法就返回false
    		if (a == false || b == false) {
    			return false;
    		} else {
    			// 都合法
    			return true;
    		}
    	}
    
    	// 1.拆分字符串,得到三个数字
    	public static String[] getStrArray(String num) {
    		// 字符串的长度和拆分后的数组
    		int length = num.length();
    		String[] nums = new String[3];
    		// 无论输入的J,Q,K,A是否为大写,都改为大写
    		num.toUpperCase();
    
    		// 字符串不含10时,长度都为3
    		if (length == 3) {
    			// nums=num.split("");//使用该方法拆分会多出一个空格位--比如33a-->[,3,3,1]
    			for (int i = 0; i < nums.length; i++) {
    				nums[i] = num.substring(i, i + 1);
    			}
    		} else if (length == 4) {
    			// 字符串含一个10
    			int index = num.indexOf("1");
    
    			// 10在首位:10XX
    			if (index == 0) {
    				nums[0] = "10";
    				nums[1] = num.substring(2, 3);
    				nums[2] = num.substring(3);
    			} else if (index == 1) {
    				// 10在中位:X10X
    				nums[0] = num.substring(0, 1);
    				nums[1] = "10";
    				nums[2] = num.substring(3);
    			} else {
    				// 10在末位:XX10
    				nums[0] = num.substring(0, 1);
    				nums[1] = num.substring(1, 2);
    				nums[2] = "10";
    			}
    		} else if (length == 5) {
    			// 字符串2个10----1010X 或 10X10 或 X1010
    			int first = num.indexOf("1");// 第一个10
    			int second = num.lastIndexOf("1");// 第二个10
    			int cha = second - first;
    
    			// 两个1距离大于2时,说明X在中间
    			if (cha > 2) {
    				nums[0] = nums[2] = "10";
    				nums[1] = num.substring(2, 3);
    			} else {
    				// 两个1距离等于2时,说明两个10是挨在一起的
    				if (first == 0) {
    					nums[0] = nums[1] = "10";
    					nums[2] = num.substring(4);
    				} else {
    					nums[0] = num.substring(0, 1);
    					nums[1] = nums[2] = "10";
    				}
    			}
    
    		} else {
    			// 字符串为3个10
    			for (int i = 0; i < nums.length; i++) {
    				nums[i] = "10";
    			}
    		}
    
    		return nums;
    	}
    
    	// 2.将字符串数组转为int数组
    	public static int[] strToNumber(String[] nums) {
    		int[] arr = new int[3];
    		for (int i = 0; i < nums.length; i++) {
    			arr[i] = letterToNumber(nums[i]);
    		}
    		Arrays.sort(arr);
    		return arr;
    	}
    
    	/*
    	 * 3.比较拆分后的三个数字相等情况,并返回数组--[参数1,参数2] 
    	 * 
    	 * 参数1:数字相同的个数;
    	 * 参数2:若有相同数字,参数2表示相同的是哪个数字;-------此时参数2只可能为2-14中的一个 
    	 *      若没有相同数字--参数2表示是否有顺子--有顺子为1,没顺子为0----此时参数2只可能是0或1
    	 * 
    	 * 例如: 
    	 * 若有两个相同,则返回[2,相同的数字]; 
    	 * 若三个都相同,则返回[3,相同的数字];
    	 * 若三个数字都不同,且不是顺子,则返回[0,0];若是顺子则返回[0,1]
    	 */
    	public static int[] equalNum(int[] nums) {
    		int[] arr = new int[2];
    
    		// 判断相等的个数
    		if (nums[0] == nums[1] && nums[0] == nums[2]) {
    			// 三个数相等
    			arr[0] = 3;
    			arr[1] = nums[0];
    		} else if (nums[0] != nums[1] && nums[0] != nums[2] && nums[1] != nums[2]) {
    			// 三个数均不相等--此时有一个顺子的问题要处理
    			arr[0] = 0;
    
    			// 因为A23不算顺子,所以只能出现2-14间的顺子,即234,345,...,121314
    			// 此时相邻两个数差为1
    			if (nums[1] - nums[0] == 1 && nums[2] - nums[1] == 1) {
    				// 为顺子
    				arr[1] = 1;
    			} else {
    				arr[1] = 0;
    			}
    		} else {
    			// 两个数相等
    			arr[0] = 2;
    			
    			// 若数组中两个数差值为0,说明就是这个数为对子
    			if(nums[0]-nums[1]==0){
    				arr[1]=nums[0];
    			}else if (nums[0]-nums[2]==0) {
    				arr[1]=nums[0];
    			}else {
    				arr[1]=nums[2];
    			}
    		}
    
    		return arr;
    	}
    
    	/*
    	 * 4.判断谁输谁赢 参数说明:a,primaryA--玩家1 b,primaryB--玩家2
    	 * 
    	 * 参数a,b:判断数字相等情况后返回的数组----即方法isEqual()处理后的结果
    	 * 参数primaryA,primaryB:原始数组(备注:转化为int后的数组---即方法strToNumber()处理后的结果)
    	 * 
    	 * 
    	 * 备注:该方法太长,可以将豹子,顺子,对子,普通牌型分别提取为一个方法
    	 *     那么需要再创建一个方法用于判断玩家1和2的牌中出现是豹子,顺子,对子,普通牌型中的哪一种
    	 *     将返回值做为if的条件,再分别去调用对应的豹子,顺子,对子,普通牌型方法
    	 *     
    	 *     因为这样方法太多,笔者就不单独提出来封装了
    	 */
    	public static int whoWin(int[] a, int[] primaryA, int[] b, int[] primaryB) {
    		// 1)判断是否为豹子
    		if (a[0] == 3 && b[0] == 3) {
    			// 都是豹子则比大小
    			if (a[1] > b[1]) {
    				return 1;
    			} else if (a[1] < b[1]) {
    				return -1;
    			} else {
    				// 玩家1和2豹子相同是不可能的,每种牌只有4张
    				return -2;
    			}
    		} else if (a[0] == 3 && b[0] != 3) {
    			// 只有玩家1是豹子--处理可能出现5张相同牌的情况--需要判断玩家2是否有对子,有,那么是否与豹子是相同的牌
    
    			// 玩家2有对子,并且与玩家1的豹子牌面相同
    			if (b[0] == 2 && a[1] == b[1]) {
    				return -2;
    			}
    
    			return 1;
    		} else if (a[0] != 3 && b[0] == 3) {
    			// 只有玩家2是豹子--同理上面
    
    			// 玩家1有对子,并且与玩家2的豹子牌面相同
    			if (a[0] == 2 && a[1] == b[1]) {
    				return -2;
    			}
    			return -1;
    		} else {
    			// 2)都没豹子,判断是否为顺子--利用非顺子时a[1]和b[1]不可能出现1,只会为2-14
    			if (a[1] == 1 && b[1] == 1) {
    				// 都为顺子,则比大小--因为是顺子,所以比较第一个数值即可
    				if (primaryA[0] > primaryB[0]) {
    					return 1;
    				} else if (primaryA[0] < primaryB[0]) {
    					return -1;
    				} else {
    					return 0;
    				}
    			} else if (a[1] == 1 && b[1] != 1) {
    				// 只有玩家1是顺子
    				return 1;
    			} else if (a[1] != 1 && b[1] == 1) {
    				// 只有玩家2是顺子
    				return -1;
    			} else {
    				// 3)都不是顺子,判断是否有对子
    				if (a[0] == 2 && b[0] == 2) {
    					// 都有对对子,则比大小
    					if (a[1] > b[1]) {
    						return 1;
    					} else if (a[1] < b[1]) {
    						return -1;
    					} else {
    						// 对子相同,则比较单个的那个数
    						int thirdA = 0;// 玩家1,单独的牌
    						int thirdB = 0;// 玩家2,单独的牌
    						for (int i = 0; i < primaryA.length; i++) {
    							if (primaryA[i] != a[1]) {
    								thirdA = primaryA[i];
    							}
    							if (primaryB[i] != b[1]) {
    								thirdB = primaryB[i];
    							}
    						}
    
    						// 比较单个数字
    						if (thirdA > thirdB) {
    							return 1;
    						} else if (thirdA < thirdB) {
    							return -1;
    						} else {
    							return 0;
    						}
    					}
    				} else if (a[0] == 2 && b[0] != 2) {
    					// 只有玩家1有对子
    					return 1;
    				} else if (a[0] != 2 && b[0] == 2) {
    					// 只有玩家2有对子
    					return -1;
    				} else {
    					// 4)都没豹子,顺子,对子,直接比大小
    					if (primaryA[2] > primaryB[2]) {
    						return 1;
    					} else if (primaryA[2] < primaryB[2]) {
    						return -1;
    					} else {
    						// 最大值相等,比较第二大的
    						if (primaryA[1] > primaryB[1]) {
    							return 1;
    						} else if (primaryA[1] < primaryB[1]) {
    							return -1;
    						} else {
    							// 最大值和第二大值都相等
    							if (primaryA[0] > primaryB[0]) {
    								return 1;
    							} else if (primaryA[0] < primaryB[0]) {
    								return -1;
    							} else {
    								return 0;
    							}
    						}
    					}
    
    				}
    			}
    
    		}
    
    	}
    
    	// 5.将字符转为数字----------将非数字的J,Q,K,A转换为数字11,12,13,14--并将本身数字的字符串转为int类型
    	public static int letterToNumber(String letter) {
    		if (letter.equals("J")) {
    			return 11;
    		} else if (letter.equals("Q")) {
    			return 12;
    		} else if (letter.equals("K")) {
    			return 13;
    		} else if (letter.equals("A")) {
    			return 14;
    		}
    
    		return Integer.parseInt(letter);
    	}
    }
    
    

    运行测试

    长度不合法
    这里写图片描述

    单个牌6出现了5次,不合法
    这里写图片描述

    豹子
    这里写图片描述

    顺子和对子
    这里写图片描述

    都是字母,顺子和对子
    这里写图片描述

    出现10,两个顺子
    这里写图片描述

    都无牌型,直接比大小
    这里写图片描述

  • 相关阅读:
    《Hadoop实战》第一章
    找工作必备技能
    范数的深刻解读(转自知乎)
    贝叶斯定理(贝叶斯分类)
    什么是机器学习?
    线性可分 与线性不可分
    正则化和归一化
    过拟合问题是什么?
    CVPR 2016 paper reading (6)
    CVPR 2016 paper reading (3)
  • 原文地址:https://www.cnblogs.com/TCB-Java/p/6950233.html
Copyright © 2011-2022 走看看