【Java】杨辉三角、洗牌算法
一、杨辉三角
给定一个非负数numRows,生成杨辉三角的前 numRows 行。
在杨辉三角中,每个数是它左上方和右上方的数之和。
示例:输入:numRows = 5
输出: [ [1],[1,1],[1,2,1],[1,3,3,1],[1,4,6,4,1] ]
思路:使用二维数组 的思路,使用列表的思路创建二维数组,定义行和列。
我们说过要用List列表的思路去创建二维数组:
示例:可以往这个列表中添加元素,只不过这个元素是一个个实例化的对象(传递的参数是一个泛型类,那么它也需要明确传递的参数类型,因此需要实例化对象),证明是一个二维数组:
输出结果:
那么,具体如何向这个二维数组存放具体的元素呢?
—— 前面说过,传递的参数是一个泛型类,因此需要实例化对象,但是不可以这样:
那么我们可以单独将列表每个实例化的元素写出来,往这一个个元素中,使用add方法添加元素,然后再将这些实例化的对象的引用传递给列表:
输出结果:
我们也可以通过调试观察:
前面的准备知识学习完后,我们正式开始写杨辉三角:
使用 [i][j] = [i-1][j-1] + [i-1][j-1] 的思路。
写一个方法 generate ,它的返回值是List<LIst<Integer>>,在方法中,我们首先处理第一行,因为杨辉三角的第一行只有一个元素,那就是1,因此,我们先处理第一行,将这一行的元素先添加到列表中;然后从第二行开始,进行求每一个元素(使用前面的公式),由于杨辉三角的每一行的第一个元素和最后一个元素都是1,于是也可以直接添加,从每一行的第二个元素开始求,也就是中间的这些位置:根据公式,需要先求该行的前一行的元素信息,然后利用这些信息进行该行的元素求解;最后返回列表。
public class Test {public static List<List<Integer>> generate(int numRows) {List<List<Integer>> ret = new ArrayLIst<>();//处理第一行List<Integer> list0 = new ArrayList<>();list0.add(1);ret.add(list0);//从第2行开始 进行求元素for(int i = 1;i < numRows; i++) {//处理第一个元素List<Integer> curRow = new ArrayList<>();curRow.add(1);//中间//从第二个元素开始求元素List<Integer> preRow = ret.get(i-1);//前一行for(int j = 1;j < i; j++) {//[i][j] = [i-1][j-1] + [i-1][j-1] int val1 = preRow.get(j);int val2 = preRow.get(j-1);ret.add(val1 + val2);} //尾巴curRow.add(1);ret.add(curRow);}return ret;}public static void main(String[] args) {List<List<Integer>> list = new ArrayList<>();List<List<Integer>> ret = generate(4);for(int i = 0;i < ret.size(); i++) {for(int j = 0;j < ret.get(i).size(); j++) {System.out.print(ret.get(i).get(j) + " ");}System.out.println();}}
}
输出结果:
二、洗牌算法
要完成洗牌,有三个步骤:
- 1.买扑克牌 52张牌
- 2.洗牌
- 3.设有3个人,每个人轮流揭5张牌
1.Card类——描述一副牌的组成
首先我们写一个类Card,用来描述买来的一副牌的组成:
public class Card {private String suit;//花色private int rank;//牌面值//构造方法public Card(String suit,int rank) {this.suit = suit;this.rank = rank;} //重写toSTringpublic void toString() {return "{" + suit + rank + "}";}
}
2.CardDemo类——完成买牌、洗牌等操作
然后再写一个类CardDemo,在这个类中,完成买牌、洗牌等一些列事情;一副牌中,包括四种花色,分别是红心、方块、梅花和黑桃,每种花色有13张牌,总计52张牌。
因此,我们可以先定义一个字符串数组,数组内存放四种花色牌,各一张,由于这四种花色是固定不变的,可以写成一个常量数组(final修饰);
然后写一个 buyCard 方法,它的返回值是List<Card>,表示买牌的动作,在这个方法中:先实例化一个Card类对象,然后通过for循环嵌套将各个花色的所有牌放入对象中(每个花色的牌各有13张,一样的牌各有4张),最后通过add方法添加到Card类对象中。
注意:该方法的返回值是一个泛型类,它的参数是一个Card类类型,因此,需要实例化Card类对象,这里每添加一个花色牌就利用Card的构造方法初始化一下suit和rank,最后将这个Card类型引用作为参数传递给泛型类。
public class CardDemo {public static final String[] suits = {"♥","♠","♣","♦"};//买一副牌public List<Card> buyCard() {List<Card> cardList = new ArrayList<>();for(int i = 1;i <= 13; i++) {for(int j = 0;j < 4; j++) {int rank = i;String suit = suits[j];Card card = new Card(suit,rank);cardList.add(card);}}return cardList;}
}
接着写一个 shuffle 方法,表示洗牌的动作,在这个方法中,我们需要做的就是打乱牌的顺序,即洗牌。这里需要用到一个类——Random,这个类中的一个 nextInt 方法,用于生成一个随机数值,该方法的有两种使用方式:
- nextInt():生成一个随机的 int 值,范围是所有可能的 int 值
- nextInt(int bound):生成一个在 [0, bound) 范围内的随机 int 值,包含 0 但不包含 bound
而我们此时需要用的是第2种方式。
写一个for循环,初始条件从最后一张牌开始,在这个循环中,使用random.nextInt() 方法,每次将随机生成的数值和最后一张牌进行随机交换,比如,有52张牌,它们对应下标是0~51,对这副牌进行洗牌,从最后一张牌开始进入循环,通过random.nextInt(51)生成0~50之间的随机牌,让最后一张牌,即第52张牌与随机的一张牌进行位置交换,接着是通过random.nextInt(50)生成0~49之间的随即牌,再将此时的最后一张牌与随机牌位置交换,直到全部牌交换完成,就完成了洗牌的动作。
注意:循环继续直到i > 0,当i变成0时,循环停止。这是因为当i=0时,只有一个元素 left,不需要交换自己。
而位置交换的动作则写一个 swap 方法来完成:按照之前写swap方法的思路:
- Card tmp = cardLIst[i];
- cardList[i] = cardList[j];
- cardList[j] = tmp;
但是这样写是不行的,cardList引用的类型是List<Card>泛型类,并不是一个数组,不可以使用数组下标运算符[],对于List<Card>类,应该使用 get() 和 set() 方法来代替进行位置交换操作。
public void shuffle(List<Card> cardList) {Random random = new Random();for(int i = cardList.size()-1;i > 0; i--) {int index = random.nextInt(i);swap(cardList,i,index);}
}
public void swap(List<Card> cardList,int i,int j) {Card tmp = cardList.get(i);cardList.set(i,cardList.get(j));cardList.set(j,tmp);
}
最后写一个轮流揭牌的方法 play ,假设有3个人,每个人要揭5张牌,那么每个人的5张牌要如何存放呢?—— 3个人各自实例化一个List泛型类,将各自的牌存放在各自的引用对象中;使用for循环嵌套,进行轮流揭牌,每次揭玩一张牌,牌就会少一张,使用remove方法,每次揭玩一张牌就删除最上面的那张牌,即下标为0的那张牌。
问题:如何将对应的牌放到对应的人手里呢?
—— 使用二维数组。
public <List<Card>> play(List<Card> cardList) {List<Card> hand0 = new ArrayList<>();List<Card> hand1 = new ArrayList<>();List<Card> hand2 = new ArrayList<>();List<List<Card>> hand = new ArrayList<>();hand.add(hand0);hand.add(hand1);hand.add(hand2);for(int i = 0;i < 5; i++) {for(int j = 0;j < 3; j++) {Card card = cardList.remove(0);hand.get(j).add(card);//比如:hand.hand0.add(card);}}return hand;
}
3.完整代码
//Card类public class Card {private String suit;private int rank;public Card(String suit,int rank) {this.suit = suit;this.rank = rank;}public void toString() {return "{" + suit + rank + "}";}
}
//CardDemo类public class CardDemo {public static final String[] suits = {"♥","♠","♣","♦"};//买一副牌public List<Card> buyCard() {List<Card> cardList= new ArrayList<>();for(int i = 1;i <= 13; i++) {for(int j = 0;j < 4; j++) {int rank = i;String suit = suits[j];Card card = new Card(suit,rank);cardList.add(card);}}return cardList;}//洗牌public void shuffle(List<Card> cardList) {Random random = new Random();for(int i = cardList.size()-1;i > 0; i--) {int index = random.nextInt(i);swap(cardList,i,index);}}public void swap(List<Card> cardList,int i,int j) {Card tmp = cardList.get(i);cardList.set(i,cardList.get(j));cardList.set(j,tmp);}//3个人,轮流揭5张牌public List<List<Card>> play(List<Card> cardList) {List<Catd> hand0 = new ArrayList<>();List<Card> hand1 = new ArrayList<>();List<Card> hand1 = new ArrayList<>();List<List<Card>> hand = new ArrayList<>();for(int i = 0;i < 5; i++) {for(int j = 0;j < 3; j++) {Card card = cardList.remove(0);hand.get(j).add(card);}}return hand;}
}
4.测试
public class Test {public static void main(String[] args) {CardDemo cardDemo = new CardDemo();//买一副牌System.out.println("洗牌前:");List<Card> cardList = cardDemo.buyCard();System.out.println(cardList);//洗牌System.out.println("洗牌后:");cardDemo.shuffle(cardList);System.out.println(cardList);//3个人轮流揭5张牌System.out.println("抽牌:");List<List<Card>> ret = cardDemo.play(cardList);for(int i = 0;i < ret,size(); i++) {System.out.println("第"+(i+1)+"个人的牌:"+ret.get(i));}System.out.println("剩下的牌:");System.out.println(cardList);}
}
输出结果: