和数组处理有关的一些OJ题;ArrayList 实现简单的洗牌算法(JAVA)(ArrayList)

news2024/11/25 4:28:09

接上次博客:数据结构初阶(2)(ArrayList简介、ArrayList()的构造方法、ArrayList的扩容、方法和三种遍历方法、ArrayList实现杨辉三角、ArrayList 的优缺点)_di-Dora的博客-CSDN博客

1、给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。 不要使用额外的数组空间,你必须确保时间复杂度为O(N),空间复杂度为O,并原地修改输入数组。元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

力扣链接:27. 移除元素 - 力扣(Leetcode)

因为它这里要求的是直接在原数组上进行修改,那么我们可以通过双指针法来原地修改输入数组,来实现移除元素的操作。

1、使用两个指针 left 和 right,初始化为数组的起始位置。

2、当 right 指向的元素等于要移除的值 val 时,将 right 指针向右移动一位。

3、当 right 指向的元素不等于要移除的值 val 时,将 right 指向的元素复制到 left 指向的位置,并同时将 left 和 right 指针都向右移动一位。

4、重复步骤 2 和步骤 3,直到 right 指针超过数组的长度。

5、返回 left 指针的值,即为新的长度。

public class RemoveElement {
    public static int removeElement(int[] nums, int val) {
        int left = 0;
        int right = 0;

        while (right < nums.length) {
            if (nums[right] != val) {
                nums[left] = nums[right];
                left++;
            }
            right++;
        }

        return left;
    }

    public static void main(String[] args) {
        int[] nums = {3, 2, 2, 3};
        int val = 3;

        int newLength = removeElement(nums, val);

        System.out.println("New Length: " + newLength);
        System.out.print("Modified Array: ");
        for (int i = 0; i < newLength; i++) {
            System.out.print(nums[i] + " ");
        }
    }
}

其实总的来说就是:

右指针指向当前将要处理的元素,左指针指向下一个将要被赋值的位置。

如果右指针指向的元素不等于 val ,那么就代表它是我们最后数组里面保留的数字,我们就可以将右指针指向的元素赋值给左指针,然后将左右指针同时++;

如果右指针指向的元素等于 val ,那么我们就要把它覆盖掉,这个时候保持左指针不动,右指针继续右移去寻找下一个不等于 val 的值,如果找到,那么把那个数字赋值给我们左指针仍指向的这个等于 val 的数字,相当于把它覆盖掉了。如果没有找到,那么就遗留在数组的最后不用去管它,当左右指针遍历完输入数组以后,我们 left 的值就是输出数组的长度。

力扣的官方题解中还给出了一种效率更高的方法,这个方法避免了需要保留的元素的重复赋值操作(在原来的方法中,我们对需要保存下来的值也进行了一次从right到left赋值的操作): 

作者:力扣官方题解
链接:https://leetcode.cn/problems/remove-element/solutions/730203/yi-chu-yuan-su-by-leetcode-solution-svxi/

class Solution {
    public int removeElement(int[] nums, int val) {
        int left = 0;
        int right = nums.length;
        while (left < right) {
            if (nums[left] == val) {
                nums[left] = nums[right - 1];
                right--;
            } else {
                left++;
            }
        }
        return left;
    }
}

我们才学完 ArrayList ,可以试试使用 ArrayList 来实现移除元素的操作,尽管题目要求是原地修改输入数组,但是其实这里也是可以通过 ArrayList 辅助进行处理的。

import java.util.ArrayList;

public class RemoveElement {
    public static int removeElement(int[] nums, int val) {
        ArrayList<Integer> list = new ArrayList<>();

        // 将不等于 val 的元素添加到 ArrayList 中
        for (int num : nums) {
            if (num != val) {
                list.add(num);
            }
        }

        // 将 ArrayList 中的元素复制回原数组
        int newSize = list.size();
        for (int i = 0; i < newSize; i++) {
            nums[i] = list.get(i);
        }

        return newSize;
    }

    public static void main(String[] args) {
        int[] nums = {3, 2, 2, 3};
        int val = 3;

        int newLength = removeElement(nums, val);

        System.out.println("New Length: " + newLength);
        System.out.print("Modified Array: ");
        for (int i = 0; i < newLength; i++) {
            System.out.print(nums[i] + " ");
        }
    }
}

我们这里甚至把原数组的值也改变了,使之只包含我们需要的数据。

2、给你一个升序排列的数组 nums ,请你原地删除重复出现的元素,使每个元素只出现一次,返回删除后数组的新长度。元素的相对顺序应该保持一致 。然后返回 nums 中唯一元素的个数。

考虑 nums 的唯一元素的数量为 k,你需要做以下事情确保你的题解可以被通过:

更改数组 nums ,使 nums 的前 k 个元素包含唯一元素,并按照它们最初在 nums 中出现的顺序排列。 nums 的其余元素与 nums 的大小不重要。返回 k 。

例如;

输入:nums = [0,0,1,1,1,2,2,3,3,4]

输出:5, nums = [0,1,2,3,4]

解释:函数应该返回新的长度5.并且原数组 nums 的前五个元素被修改为【0,1,2,3,4】不需要考虑数组中超出新长度后面的元素。

力扣链接:26. 删除有序数组中的重复项 - 力扣(Leetcode) 

这一题有一个地方值得注意:这是一个有序的数组,所以如果有重复元素,那么它们一定是相邻的,我们仍然可以沿用刚刚的双指针方法:

    public int removeDuplicates(int[] nums) {
        int left = 0;
        int right = 0;
        while (right < nums.length) {
            if (nums[left] == nums[right]) {
                right++;
            } else {
                if (nums[right] != nums[left + 1]) {
                    nums[left + 1] = nums[right];
                }
                left++;
                right++;
            }
        }
        return left + 1;
    }

3、给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2,另有两个整数 m 和 n ,分别表示 nums1 和 nums2 中的元素数目。
请你 合并 nums2 到 nums1 中,使合并后的数组同样按 非递减顺序 排列。

注意:最终,合并后数组不应由函数返回,而是存储在数组 nums1 中。为了应对这种情况,nums1 的初始长度为 m + n,其中前 m 个元素表示应合并的元素,后 n 个元素为 0 ,应忽略。nums2 的长度为 n 。

对于这道题,如果要在不使用额外空间的情况下合并两个有序数组,我们可以考虑从后向前进行合并,避免元素的频繁移动。所以我们还是可以使用双指针法来完成:

1、初始化指针 p1,指向 nums1 的第一个元素,指针 p2,指向 nums2 的第一个元素。

2、创建一个临时数组 merged,用于存储合并后的数组。

3、比较 nums1[p1] 和 nums2[p2] 的值,将较小的值添加到 merged 数组中,并将对应指针向后移动一位。

4、重复步骤 3,直到其中一个数组的所有元素都添加到 merged 数组中。

5、将剩余的数组中的元素添加到 merged 数组中,如果是 nums1 数组剩余元素,无需处理;如果是 nums2 数组剩余元素,则直接复制到 merged 数组中。

6、将 merged 数组中的元素复制回 nums1 数组中。

 public void merge(int[] nums1, int m, int[] nums2, int n) {
        int p1 = m - 1;
        int p2 = n - 1;
        int p = m + n - 1;

        while (p1 >= 0 && p2 >= 0) {
            if (nums1[p1] > nums2[p2]) {
                nums1[p] = nums1[p1];
                p1--;
            } else {
                nums1[p] = nums2[p2];
                p2--;
            }
            p--;
        }

        // 将 nums2 中剩余的元素合并到 nums1 中
        while (p2 >= 0) {
            nums1[p] = nums2[p2];
            p2--;
            p--;
        }
    }

这种方法只使用了常量级的额外空间,符合题目要求的空间复杂度为 O(1),同时也满足题目要求的将合并后的数组存储在 nums1 中。

如果用上了 ArrayList 就会有点复杂:

import java.util.ArrayList;
import java.util.List;

public class MergeSortedArray {
    public static void merge(int[] nums1, int m, int[] nums2, int n) {
        List<Integer> merged = new ArrayList<>();

        int p1 = 0;
        int p2 = 0;

        while (p1 < m && p2 < n) {
            if (nums1[p1] < nums2[p2]) {
                merged.add(nums1[p1]);
                p1++;
            } else {
                merged.add(nums2[p2]);
                p2++;
            }
        }

        while (p1 < m) {
            merged.add(nums1[p1]);
            p1++;
        }

        while (p2 < n) {
            merged.add(nums2[p2]);
            p2++;
        }

        // 将 merged 数组复制回 nums1
        for (int i = 0; i < merged.size(); i++) {
            nums1[i] = merged.get(i);
        }
    }

    public static void main(String[] args) {
        int[] nums1 = {1, 3, 5, 0, 0, 0};
        int[] nums2 = {2, 4, 6};
        int m = 3;
        int n = 3;

        merge(nums1, m, nums2, n);

        System.out.print("Merged Array: ");
        for (int num : nums1) {
            System.out.print(num + " ");
        }
    }
}

这种方法使用了额外的 ArrayList 来存储合并后的数组,然后将其复制回 nums1 数组。

注意,这种方法的空间复杂度为 O(m+n),不符合题目要求的原地修改输入数组的要求。

4、简单的洗牌算法:

我们先来定义一下我们的扑克牌类:

public class Card {
    public int rank; // 扑克牌的数字
    public String suit; // 花色

    public int getRank() {
        return rank;
    }

    public void setRank(int rank) {
        this.rank = rank;
    }

    public String getSuit() {
        return suit;
    }

    public void setSuit(String suit) {
        this.suit = suit;
    }
    public Card() {

    }
    public Card(String suit,int rank) {
        this.rank = rank;
        this.suit = suit;
    }

    @Override
    public String toString() {
        return String.format("[%s %d]", suit, rank);
    }

}

好了,现在我们要开始洗牌发牌了:

先思考一下,一共需要几个方法?

我们是不是得先有一副完整的扑克牌?(当然啦,没有“大小王”的,否则就太困难了) 

所以我们首先需要买一副完整的扑克牌。

有了一副完整的扑克,那我们是不是得先打乱这副牌的顺序?

因为刚买回来的牌必然是按照花色和数字排好的,这将导致大家拿到的牌是相邻的,花色几乎一样。

所以我们还需要一个洗牌的方法。

我们还需要注意:每个人摸牌的时候是怎么摸的?

我们定义,三个人,每个人轮流抓 5 张牌,每轮一人拿一张牌。

import java.awt.*;
import java.util.*;
import java.util.List;

public class CardList {
    public static final String[] SUITS = {"♠", "♥", "♣", "♦"};//花色一般不会改变,所以可以加static


    // 买一副牌
    private static List<Card> buyDeck() {
        List<Card> deck = new ArrayList<>(52);
        for (int i = 0; i < SUITS.length; i++) {
            for (int j = 1; j <= 13; j++) {
                //1
/*                String suit = SUITS[i];
                int rank = j;
                Card card = new Card();
                card.rank = rank;
                card.suit = suit;*/
                //2
/*                String suit = SUITS[i];
                int rank = j;
                Card card = new Card(suit,rank);*/
                //3
                Card card = new Card(SUITS[i],j);
                deck.add(card);
            }
        }
        return deck;
    }

    //交换牌面
    private static void swap(List<Card> deck, int i, int j) {
        Card t = deck.get(i);
        deck.set(i, deck.get(j));
        deck.set(j, t);
    }

    //洗牌
    private static void shuffle(List<Card> deck) {
        Random random = new Random();
        //倒着遍历,50的时候生成的就是1~49的随机数
        for (int i = deck.size() - 1; i > 0; i--) {
            int r = random.nextInt(i);
            swap(deck, i, r);
        }
    }

    public static void main(String[] args) {
        List<Card> deck = buyDeck();
        System.out.println("刚买回来的牌:");
        System.out.println(deck);
        shuffle(deck);
        System.out.println("洗过的牌:");
        System.out.println(deck);
        // 总共有三个人,每个人轮流抓 5 张牌
        //每轮一人拿一张牌
        List<Card> hand1 = new ArrayList<>();
        List<Card> hand2 = new ArrayList<>();
        List<Card> hand3 = new ArrayList<>();
        List<List<Card>> hands = new ArrayList<>();
        hands.add(hand1);
        hands.add(hand2);
        hands.add(hand3); 
        //控制每个人摸牌的次数
        for (int i = 0; i < 5; i++) {
            //三个人轮流揭牌
            for (int j = 0; j < 3; j++) {
                //hands.get(j).add(deck.remove(0));
                Card card = deck.remove(0);
                hands.get(j).add(card);
            }
        }
        System.out.println("剩余的牌:");
        System.out.println(deck);
        System.out.println("A 手中的牌:");
        System.out.println(hands.get(0));
        System.out.println("B 手中的牌:");
        System.out.println(hands.get(1));
        System.out.println("C 手中的牌:");
        System.out.println(hands.get(2));

    }


}

这里面有一句代码可能比较难以理解:

hands.get(j).add(deck.remove(0)) ;

或者我们也可以写为: Card card = deck.remove(0);       hands.get(j).add(card);

它表示将牌堆 deck 中的第一张牌(索引为0)移除,并将其添加到 hands 列表中第 j 个玩家的手牌中。

具体步骤如下:

1、  hands.get(j):根据索引 j 获取 hands 列表中第j个玩家的手牌列表。
2、 .add(deck.remove(0)):将 deck 列表中的第一张牌移除,并使用 .add() 方法将其添加到玩家的手牌列表中。

当然,你可能会疑惑:

这个代码里面:Card card = deck.remove(0);  hands.get(j).add(card);,

一旦执行deck.remove(0); 不就代表那个牌已经被移除了吗?还怎么添加到集合里?

其实:deck.remove(0)会从列表中删除指定位置的元素,并将其返回。

因此,执行完deck.remove(0)后,牌堆中的第一张牌已经被移除,而card变量中保存了该牌的引用。

接下来,hands.get(j).add(card)将card添加到相应玩家的手牌集合中,即将已经移除的牌加入到集合中,这样玩家就获得了这张牌。

最后实现效果如下:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/552434.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

FreeRTOS(6)----软件定时器

一&#xff0c;软件定时器概述 软件定时器允许设置一段时间&#xff0c;当设定的时间到达之后就会执行指定的功能函数&#xff0c;被定时器调用的这个函数叫做定时器的回调函数。回调函数的两次执行间隔叫做定时器的定时周期。 二&#xff0c;回调函数的注意事项 回调函数是…

Linux文件权限管理

1、Linux权限介绍 权限管理&#xff0c;其实就是指对不同的用户&#xff0c;设置不同的文件访问权限。 Linux 系统&#xff0c;最常见的文件权限有 3 种&#xff0c;即对文件的读&#xff08;用 r 表示&#xff09;、写&#xff08;用 w 表示&#xff09;和执行&#xff08;用…

github.io创建个人网站

文章目录 github.io介绍使用步骤新建仓库添加文件 github.io介绍 github.io是 GitHub 提供的免费 Pages服务&#xff0c;不需要购买云服务器和域名&#xff0c;就可以将自己的项目、博客在互联网上进行共享。 使用步骤 新建仓库 创建一个新的仓库&#xff0c;仓库名设置为如…

Nacos环境隔离

随着Nacos 0.8版本的release&#xff0c;Nacos离正式生产版本又近了一步&#xff08;其实已经有不少企业已经上了生产&#xff0c;如虎牙&#xff09;。一般而言&#xff0c;企业研发的流程一般是这样的&#xff1a;先在测试环境开发和测试功能&#xff0c;然后再灰度&#xff…

CANFDCAN协议对比 - 基础介绍_01

目录 一、为什么会出现CANFD&#xff1f; 1、信号数量大量增加 2、新要求&#xff0c;新总线系统 3、CAN性能限制 4、更短的位时间也能满足 &#xff1f;&#xff1f;&#xff1f;CAN是否会被取代&#xff1f; 二、CANFD优点 1、更多数据&#xff0c;更低总线负载率 2…

计算机网络 三(数据链路层)下

流量控制与可靠传输机制 流量控制手段&#xff1a;接收方收不下就不回复确认。 传输层流量控制手段&#xff1a;接收端给发送端一个窗口公告 #可用协议 可用协议 停止等待协议 概念、出现原因 停止等待协议是一种简单的数据传输协议&#xff0c;常用于数据传输的可靠性较低…

【Docker实战】使用Docker部署Tomcat

【Docker实战】使用Docker部署Tomcat 一、Tomcat介绍1. Tomcat简介2. Tomcat特点3. Tomcat容器部署的优点4. Tomcat的配置文件 二、检查本地环境三、检查本地Docker环境1. 检查本地Docker服务2. 检查Docker版本 四、搜索docker hub中的tomcat镜像五、下载tomcat镜像六、创建Tom…

chatgpt赋能Python-pythona的三次方

Python 的三次方&#xff1a;探索 Python 强大的数学计算能力 Python 是一种高级编程语言&#xff0c;被广泛应用于数据分析、机器学习、人工智能等领域。除此之外&#xff0c;Python 还拥有强大的数学计算能力&#xff0c;其中包括 Python 的三次方计算功能。在本文中&#x…

AI人工智能标记数据的技术:类型、方法、质量控制、应用

AI人工智能 标记数据 在人工智能&#xff08;Artificial Intelligence&#xff0c;简称AI&#xff09;领域中&#xff0c;标记数据是非常重要的一环。它是指对原始数据进行标记和注释&#xff0c;以便机器学习算法可以理解和利用这些数据。标记数据可以提高机器学习模型的准确…

实战【云原生】--- Kubernetes集群

K8S集群负载均衡层防火墙 实例 一、kubeadm 部署 K8S 集群架构1、关闭所有节点的防火墙 核心防护 iptables swap交换2、修改主机名3、调整内核参数 二、安装Docker1、所有节点安装docker2、所有节点配置k8s源3、所有节点安装kubeadm&#xff0c;kubelet和kubectl 三、部署k8s集…

2023/5/21总结

因为之前高中学过一点点的html。虽然不是很多&#xff0c;但是有一点点基础&#xff0c;看了一些关于html的知识点&#xff0c;算是复习了&#xff0c;如果后面忘记打算再去查。 html是超文本标记语言&#xff0c;通常由<></>构成&#xff0c;当然也有单标记&…

chatgpt赋能Python-python5__2

Python中整除运算符 // 的用法和重要性 在Python中&#xff0c;整除运算符 // 有着广泛的应用&#xff0c;特别是在数据分析、科学计算、金融量化、游戏开发等领域中&#xff0c;它是很重要的基础运算符。 什么是整除运算符 //&#xff1f; 整除运算符 // 是Python中的一种二…

chatgpt赋能Python-pythonbmi

Python计算BMI - 计算和判断你的身体状况 BMI&#xff0c;即Body Mass Index&#xff0c;中文翻译为“身体质量指数”&#xff0c;是目前世界上公认的反映体重是否正常的最权威的指标之一。 计算BMI可以帮助我们判断自己的身体状况&#xff0c;是如今非常流行的健康指标之一。…

【iOS】SDWebImage源码学习--未完

SDWebImage的主要功能及相关知识点 SDWebImage是一个流行的第三方库&#xff0c;用于在iOS和macOS应用程序中异步下载和缓存图像。它提供了一种简单而强大的方式来处理网络图像加载和缓存&#xff0c;具有以下主要功能&#xff1a; 异步下载&#xff1a;SDWebImage使用多线程…

MyBatis中动态SQL的使用和注意事项说明

文章目录 0、前言1、if2、where3、trim4、choose-when-otherwise5、foreach应用场景1&#xff1a; 通过数组实现批量删除应用场景2&#xff1a; 通过list集合实现批量添加 6、include抽取公共SQL片段 0、前言 MyBatis框架动态SQL技术是根据特定的条件拼接SQL语句的功能&#x…

【计算机图形学】曲线和曲面

模块5 曲线和曲面 一 实验目的 编写曲线和曲面的算法 二 实验内容 1&#xff1a;绘制Bezier曲线&#xff0c;并采用自行设计输入和交互修改数据点的方式。 实验结果如下图所示&#xff1a; 第一步&#xff1a;输入特征多边形的顶点个数&#xff0c;并按照顺序输入顶点的坐…

《心静的力量》读书笔记

让心静下来&#xff0c;战胜一切忧虑 于我们每个人而言&#xff0c;最重要的就是不要去看远方模糊不清的事&#xff0c;而要做手边真实清楚的事。 明天的重担&#xff0c;加上昨天的重担&#xff0c;会成为今天的最大障碍&#xff0c;要把未来同过去一样紧紧地关在门外……未…

将MetaHuman的身体替换为虚幻商城模型的身体

一、准备好MetaHuman模型和虚幻商城模型 1.准备好MetaHuman模型,参考这篇文章 虚幻商城模型转MetaHuman制作MetaHuman并导入UE,同时复制一个MetaHuman模型 2.下载虚幻商城的原始模型,并导入UE 二、将虚幻商城模型的头去掉 1.打开虚幻商城的模型,找到分段 2.在右边点击…

chatgpt赋能Python-pythonapp开发

PythonApp开发&#xff1a;为什么选择Python实现&#xff1f; Python是当今最流行的编程语言之一&#xff0c;尤其在Web应用开发和数据分析领域更是大有作为。本文将探讨Python在App开发领域中的表现&#xff0c;为什么Python可以成为您理想的选择&#xff1f; 1. 简单易学 …

深度学习基础入门篇[8]::计算机视觉与卷积神经网络、卷积模型CNN综述、池化讲解、CNN参数计算

【深度学习入门到进阶】必看系列&#xff0c;含激活函数、优化策略、损失函数、模型调优、归一化算法、卷积模型、序列模型、预训练模型、对抗神经网络等 专栏详细介绍&#xff1a;【深度学习入门到进阶】必看系列&#xff0c;含激活函数、优化策略、损失函数、模型调优、归一化…