【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 ,于是也可以直接添加,从每一行的第二个元素开始求 ,也就是中间的这些位置:根据公式,需要先求该行的前一行的元素信息,然后利用这些信息进行该行的元素求解;最后返回列表。

java 复制代码
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,用来描述买来的一副牌的组成:

java 复制代码
public class Card {
    private String suit;//花色
    private int rank;//牌面值

    //构造方法
    public Card(String suit,int rank) {
        this.suit = suit;
        this.rank = rank;
    } 

    //重写toSTring
    public 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类型引用作为参数传递给泛型类。

java 复制代码
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 方法,用于生成一个随机数值,该方法的有两种使用方式:

  1. nextInt():生成一个随机的 int 值,范围是所有可能的 int 值
  2. 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() 方法来代替进行位置交换操作。

java 复制代码
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的那张牌。

问题:如何将对应的牌放到对应的人手里呢?

------ 使用二维数组

java 复制代码
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.完整代码

java 复制代码
//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 + "}";
    }
}
java 复制代码
//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.测试

java 复制代码
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);
    }
}

输出结果:

相关推荐
码界奇点6 小时前
基于飞算JavaAI的在线图书借阅平台从设计到实现的完整指南
java·飞书
闻缺陷则喜何志丹6 小时前
【C++贪心】P10537 [APIO2024] 九月|普及+
c++·算法·贪心·洛谷
QiZhang | UESTC6 小时前
JAVA算法练习题day27
java·开发语言·c++·算法·leetcode·hot100
hrrrrb6 小时前
【Spring Boot】Spring Boot 中常见的加密方案
java·spring boot·后端
十碗饭吃不饱6 小时前
sql报错:java.sql.SQLSyntaxErrorException: Unknown column ‘as0‘ in ‘where clause‘
java·数据库·sql
饼干吖6 小时前
记一次滑动数组解题
java·算法
ss2737 小时前
手写MyBatis第96弹:异常断点精准捕获MyBatis深层BUG
java·开发语言·bug·mybatis
小马爱打代码7 小时前
分布式锁:原理算法和使用建议
分布式·算法
程序定小飞7 小时前
基于springboot的在线商城系统设计与开发
java·数据库·vue.js·spring boot·后端