数据结构------手撕顺序表

文章目录

  • 线性表
  • 顺序表的使用及其内部方法
  • ArrayList 的扩容机制
  • 顺序表的几种遍历方式
  • 顺序表的优缺点
  • 顺序表的模拟实现
  • 洗牌算法

线性表

  线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列…

  线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的,线性表在物理上存储时,通常以数组和链式结构的形式存储
在这里插入图片描述


顺序表的使用及其内部方法

  顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改。ArrayList底层是一段连续的空间,并且可以动态扩容,是一个动态类型的顺序表

ArrayList的使用
ArrayList 的构造方法如下:
在这里插入图片描述
示例代码如下:

public static void main(String[] args) {
// ArrayList创建,推荐写法
List<Integer> list1 = new ArrayList<>();
// 构造一个具有10个容量的列表
List<Integer> list2 = new ArrayList<>(10);
list2.add(1);
list2.add(2);
list2.add(3);
// list2.add("hello"); // 编译失败,List<Integer>已经限定了,list2中只能存储整形元素
// list3构造好之后,与list中的元素一致
ArrayList<Integer> list3 = new ArrayList<>(list2);
// 避免省略类型,否则:任意类型的元素都可以存放,使用时将是一场灾难
List list4 = new ArrayList();
list4.add("111");
list4.add(100);
}

ArrayList 中的方法
在这里插入图片描述


ArrayList 的扩容机制

ArrayList是一个动态类型的顺序表,即:在插入元素的过程中会自动扩容。

【总结】

  1. 默认容量为10
  2. 检测是否真正需要扩容,如果是调用grow准备扩容
  3. 预估需要扩容的大小
  • 初步预估按照1.5倍大小扩容
  • 如果用户所需大小超过预估1.5倍大小,则按照用户所需大小扩容
  • 真正扩容之前检测是否能扩容成功,防止太大导致扩容失败
  1. 使用copyOf进行扩容

顺序表的几种遍历方式

 public static void main(String[] args) {ArrayList<Integer> list = new ArrayList<>();list.add(1);list.add(2);list.add(3);System.out.println(list);System.out.println("=========for循环遍历=========");for (int i = 0; i < list.size(); i++) {System.out.print(list.get(i) +" ");}System.out.println();System.out.println("=========foreach循环遍历=========");for(Integer x : list) {System.out.print(x +" ");}System.out.println();System.out.println("=========迭代器遍历1=========");Iterator<Integer> it = list.iterator();while(it.hasNext()) {System.out.print(it.next() +" ");}System.out.println();System.out.println("=========迭代器遍历2=========");ListIterator<Integer> tmpList = list.listIterator();while(tmpList.hasNext()) {System.out.print(tmpList.next() +" ");}System.out.println();System.out.println("=========迭代器遍历3=========");ListIterator<Integer> tmpList2 = list.listIterator(list.size());while(tmpList2.hasPrevious()) {System.out.print(tmpList2.previous() +" ");}System.out.println();}

顺序表的优缺点

优点:

  • 内存连续—>物理上和逻辑上
  • 根据下标查找元素,时间复杂度为O(1)

缺点:

  • 插入数据和删除数据不合适,因为每次都要移动元素
  • 时间复杂度会达到O(N)

顺序表的模拟实现

  顺序表的底层使用数组实现的,所以这里模拟实现顺序表也使用数组来实现。
在实现之前,先看看ArrayList中的常用方法,后续要模拟实现这些方法,如下:

public class IList {
// 新增元素,默认在数组最后新增
public void add(int data) { }
// 在 pos 位置新增元素
public void add(int pos, int data) { }
// 判定是否包含某个元素
public boolean contains(int toFind) { return true; }
// 查找某个元素对应的位置
public int indexOf(int toFind) { return -1; }
// 获取 pos 位置的元素
public int get(int pos) { return -1; }
// 给 pos 位置的元素设为 value
public void set(int pos, int value) { }
//删除第一次出现的关键字key
public void remove(int toRemove) { }
// 获取顺序表长度
public int size() { return 0; }
// 清空顺序表
public void clear() { }
// 打印顺序表,注意:该方法并不是顺序表中的方法,为了方便看测试结果给出的
public void display() { }
}

【思路】:把这些ArrayList中的方法放入一个接口中(当然这些方法放入接口中暂时没有具体实现,为抽象方法),让模拟实现的MyArrayList 类实现这个接口,然后在MyArrayList 类中把这些方法全部重写并实现。


创建一个 IList 接口,接口中包含各种关于ArrayList的抽象方法,代码示例如下:

public interface IList {// 新增元素,默认在数组最后新增void add(int data);// 在 pos 位置新增元素void add(int pos, int data);// 判定是否包含某个元素boolean contains(int toFind);// 查找某个元素对应的位置int indexOf(int toFind) ;//// 获取 pos 位置的元素int get(int pos);// 给 pos 位置的元素设为 valuevoid set(int pos, int value);//删除第一次出现的关键字keyvoid remove(int toRemove);// 获取顺序表长度int size();// 清空顺序表void clear();// 打印顺序表,注意:该方法并不是顺序表中的方法,为了方便看测试结果给出的void display();//判断数组是否为空boolean isEmpty();//删除所有数据为toRemove的元素void removeAll(int toRemove);
}

创建MyArrayList 类 使其实现 IList 接口,在类中重写接口中的方法

import java.util.Arrays;
public class MyArrayList implements IList{public int[] elem;public int usedSize;public MyArrayList() {this.elem = new int[10];}//判断数组是否满了public boolean isFull() {return elem.length == usedSize;}//数组满了,进行扩容private void grow() {elem = Arrays.copyOf(elem,2*elem.length);}/*** 对数组的 最后一个位置 新增data* @param data*/@Overridepublic void add(int data) {if(isFull()) {grow();}elem[usedSize] = data;usedSize++;}/*** 在pos位置 新增 元素data* @param pos* @param data*/@Overridepublic void add(int pos, int data) {if(isFull()) {grow();}if(pos < 0 || pos > usedSize) {throw new PosOutOfException("pos位置不合法");}for (int i = usedSize-1; i >= pos; i--) {elem[i+1] = elem[i];}elem[pos] = data;usedSize++;}/*** 查找一个数据,返回true或false* @param toFind* @return*/@Overridepublic boolean contains(int toFind) {if(isEmpty()) {return false;}for (int i = 0; i < usedSize; i++) {if(elem[i] == toFind) {return true;}}return false;}/*** 查找一个数据,返回其下标* @param toFind* @return*/@Overridepublic int indexOf(int toFind) {for (int i = 0; i < usedSize; i++) {if(elem[i] == toFind) {return i;}}return -1;}/*** 查找下标为pos的元素,返回其数值* @param pos* @return*/@Overridepublic int get(int pos) {if(isEmpty()) {throw new EmptyArrayListException("顺序表为空");}if(pos < 0 || pos >= usedSize) {throw new RuntimeException("pos位置不合法");}return elem[pos];}/*** pos位置 设置成 value* @param pos* @param value*/@Overridepublic void set(int pos, int value) {if(pos < 0 || pos >= usedSize) {throw new PosOutOfException("pos位置不合法");}elem[pos] = value;}/*** 删除数组中的一个元素 toMove* @param toRemove*/@Overridepublic void remove(int toRemove) {int index = indexOf(toRemove);if(index == -1) {return;}for (int i = index; i < usedSize-1; i++) {elem[i] = elem[i+1];}usedSize--;}@Overridepublic int size() {return usedSize;}@Overridepublic void clear() {usedSize = 0;}@Overridepublic void display() {for (int i = 0; i < usedSize; i++) {System.out.print(elem[i] +" ");}System.out.println();}@Overridepublic boolean isEmpty() {if(usedSize == 0) {return true;}return false;}@Overridepublic void removeAll(int toRemove) {while(contains(toRemove)) {int index = indexOf(toRemove);for (int i = index; i < usedSize-1; i++) {elem[i] = elem[i+1];}usedSize--;}}
}

测试类Test

    public static void main1(String[] args) {MyArrayList myArrayList = new MyArrayList();myArrayList.add(1);myArrayList.add(2);myArrayList.add(3);myArrayList.add(1,19);myArrayList.display();myArrayList.set(0,100);myArrayList.display();boolean ret = myArrayList.contains(100);System.out.println(ret);myArrayList.set(1,33);myArrayList.display();myArrayList.add(0,1);myArrayList.add(1,2);myArrayList.add(2,1);myArrayList.add(3,4);myArrayList.add(4,1);myArrayList.display();myArrayList.removeAll(1);myArrayList.display();}

洗牌算法

实现效果:能够完成创建扑克牌、洗牌、发牌操作
思路:
完成洗牌和发牌操作,需要先拿到一副扑克牌,所以先创建一副扑克牌(共52张牌,没有大小王)
一副扑克牌由 13张红桃♥、13张黑桃♠、13张方片♦、13张梅花♣ 组成。
创建 Card类,内部定义变量 suit 花色、rank 数字,重写toString方法,便于观察每张牌的信息
创建 Game类,内部实现 创建一副牌、洗牌、发牌操作.
Card类的代码示例如下:

public class Card {public String suit;//花色--红桃、黑桃、方块、梅花public int rank;//数字public Card(String suit, int rank) {this.suit = suit;this.rank = rank;}@Overridepublic String toString() {
//        return "Card{" +
//                "suit='" + suit + '\'' +
//                ", rank=" + rank +
//                '}';return "{ "+suit+rank+" }";}
}

1.创建一副牌,使用顺序表作为容器,将每一张牌放入这个容器中
如何把每一张牌放入顺序表中?–> 四种不同花色分别有13张牌,所以使用两个 for 循环,将卡牌放入顺序表
内部循环13次,把放入同一花色的13张牌放入顺序表,外部循环4次,每次都是不同的花色。代码示例如下:

import java.util.ArrayList;
import java.util.List;
import java.util.Random;public class Game {public String[] suits = {"♥","♠","♦","♣"};public List<Card> cardsList = new ArrayList<>();//作为扑克牌的容器//创建一副扑克牌public List<Card> createCards() {for (int i = 0; i < suits.length; i++) {for (int j = 1; j <= 13 ; j++) {Card card = new Card(suits[i],j);cardsList.add(card);}}return cardsList;}

2.洗牌操作,现实中洗牌是将任意不同的卡牌交换位置,转换为代码层面,可以把任意不同下标的卡牌进行交换,for(i)循环遍历顺序表,每次生成一个i下标后面的随机数(i+1,51),但是这样生成随机数的实现比较麻烦,因此可以从最后面即下标为 51 的位置向前遍历顺序表,每次生成 [0,i) 的随机数,然后交换下标为i 和 下标为randIndex的元素。 代码示例如下:

    //洗牌操作public void shuffle(List<Card> cardsList) {Random random = new Random();for (int i = 51; i > 0; i--) {int randIndex = random.nextInt(i);//生成 [0,i-1] 区间的随机数//i下标位置 与 randIndex下标位置元素进行交换swap(i,randIndex);}}//i下标位置 与 randIndex下标位置元素进行交换private void swap(int i,int randIndex) {Card tmp = cardsList.get(i);cardsList.set(i,cardsList.get(randIndex));cardsList.set(randIndex,tmp);}

3.发牌操作
要求:三人依次取牌,每人每次取牌一张,进行五次轮回取牌,最终每人手中有五张牌
三个人取出的牌,也需要一个容器容纳扑克牌,可以为每个人创建一个顺序表容纳取出的扑克牌
三人依次取牌,每次一张,进行五次轮回,就可以使用两个for循环,完成取牌和牌进入入顺序表的操作
每次取牌都要把取出的这张牌从顺序表中删除,避免其他人重复取这张牌(现实中也是如此,取出一张牌,那么这张牌就不会存在于 待取的那摊扑克牌中,谁取的这张牌,这张牌就在谁手上),转换为代码层面,就是调用 cardsList.remove(0)方法。将cardsList.remove(0)方法的返回值 传给 每个人的手中

【注意】这里cardsList.remove(0)方法中的参数一直都是0,因为每次取牌都是最上面的牌,而顺序表删除已取出的牌之后,第二张牌又会称为新的下标为 0 的新的元素。
代码示例如下:

//发牌,三个人轮流取牌,一次取一张,最终每个人五张牌public List<List<Card>> play(List<Card> cardsList) {List<List<Card>> cardLists = new ArrayList<>();List<Card> hand0 = new ArrayList<>();List<Card> hand1 = new ArrayList<>();List<Card> hand2 = new ArrayList<>();cardLists.add(hand0);cardLists.add(hand1);cardLists.add(hand2);for (int i = 0; i < 5; i++) {for (int j = 0; j < 3; j++) {Card card = cardsList.remove(0);cardLists.get(j).add(card);}}return cardLists;}

Test测试类,代码示例如下:

import java.util.List;public class Test {public static void main(String[] args) {Game game = new Game();List<Card> cardsList = game.createCards();System.out.println("初始牌:"+cardsList);game.shuffle(cardsList);System.out.println("洗过后的牌:"+cardsList);System.out.println("发牌后,每人手中的牌:"+game.play(cardsList));}
}

运行结果:

“C:\Program Files\Java\jdk-17\bin\java.exe” “-javaagent:D:\softstall\idea\IntelliJ IDEA Community Edition 2021.3.3\lib\idea_rt.jar=63623:D:\softstall\idea\IntelliJ IDEA Community Edition 2021.3.3\bin” -Dfile.encoding=UTF-8 -classpath D:\javacode\J20241025_CardGame\out\production\J20241025_CardGame Test
初始牌:[{ ♥1 }, { ♥2 }, { ♥3 }, { ♥4 }, { ♥5 }, { ♥6 }, { ♥7 }, { ♥8 }, { ♥9 }, { ♥10 }, { ♥11 }, { ♥12 }, { ♥13 }, { ♠1 }, { ♠2 }, { ♠3 }, { ♠4 }, { ♠5 }, { ♠6 }, { ♠7 }, { ♠8 }, { ♠9 }, { ♠10 }, { ♠11 }, { ♠12 }, { ♠13 }, { ♦1 }, { ♦2 }, { ♦3 }, { ♦4 }, { ♦5 }, { ♦6 }, { ♦7 }, { ♦8 }, { ♦9 }, { ♦10 }, { ♦11 }, { ♦12 }, { ♦13 }, { ♣1 }, { ♣2 }, { ♣3 }, { ♣4 }, { ♣5 }, { ♣6 }, { ♣7 }, { ♣8 }, { ♣9 }, { ♣10 }, { ♣11 }, { ♣12 }, { ♣13 }]
洗过后的牌:[{ ♣13 }, { ♥13 }, { ♠5 }, { ♦10 }, { ♠13 }, { ♦12 }, { ♥4 }, { ♦6 }, { ♣5 }, { ♣3 }, { ♣1 }, { ♠12 }, { ♣11 }, { ♣2 }, { ♣7 }, { ♥5 }, { ♦9 }, { ♥11 }, { ♥2 }, { ♦2 }, { ♥7 }, { ♦5 }, { ♣12 }, { ♠7 }, { ♠11 }, { ♦11 }, { ♥1 }, { ♠8 }, { ♥10 }, { ♣8 }, { ♥8 }, { ♥3 }, { ♠4 }, { ♣10 }, { ♦4 }, { ♠2 }, { ♠1 }, { ♥9 }, { ♠3 }, { ♣6 }, { ♠6 }, { ♦7 }, { ♥12 }, { ♦8 }, { ♣4 }, { ♠10 }, { ♦13 }, { ♠9 }, { ♦1 }, { ♣9 }, { ♥6 }, { ♦3 }]
发牌后,每人手中的牌:[[{ ♣13 }, { ♦10 }, { ♥4 }, { ♣3 }, { ♣11 }], [{ ♥13 }, { ♠13 }, { ♦6 }, { ♣1 }, { ♣2 }], [{ ♠5 }, { ♦12 }, { ♣5 }, { ♠12 }, { ♣7 }]]


在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

TLS协议基本原理与Wireshark分析

01背 景 随着车联网的迅猛发展&#xff0c;汽车已经不再是传统的机械交通工具&#xff0c;而是智能化、互联化的移动终端。然而&#xff0c;随之而来的是对车辆通信安全的日益严峻的威胁。在车联网生态系统中&#xff0c;车辆通过无线网络与其他车辆、基础设施以及云端服务进行…

Lucas带你手撕机器学习——套索回归

好的&#xff0c;下面我将详细介绍套索回归的背景、理论基础、实现细节以及在实践中的应用&#xff0c;同时还会讨论其优缺点和一些常见问题。 套索回归&#xff08;Lasso Regression&#xff09; 1. 背景与动机 在机器学习和统计学中&#xff0c;模型的复杂性通常会影响其在…

【云原生】Kubernets1.29部署StorageClass-NFS作为存储类,动态创建pvc(已存在NFS服务端)

文章目录 在写redis集群搭建的时候,有提到过使用nfs做storageclass,那时候kubernetes是1.20版本,https://dongweizhen.blog.csdn.net/article/details/130651727 现在使用的是kubernetes 1.29版本,根据之前的修改方式并未生效,反而提示:Error: invalid argument "Re…

Claude Financial Data Analyst:基于Claude的金融数据分析工具!免费开源!

大家好&#xff0c;我是木易&#xff0c;一个持续关注AI领域的互联网技术产品经理&#xff0c;国内Top2本科&#xff0c;美国Top10 CS研究生&#xff0c;MBA。我坚信AI是普通人变强的“外挂”&#xff0c;专注于分享AI全维度知识&#xff0c;包括但不限于AI科普&#xff0c;AI工…

智创 AI 新视界 -- 探秘 AIGC 中的生成对抗网络(GAN)应用

&#x1f496;&#x1f496;&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎你们来到 青云交的博客&#xff01;能与你们在此邂逅&#xff0c;我满心欢喜&#xff0c;深感无比荣幸。在这个瞬息万变的时代&#xff0c;我们每个人都在苦苦追寻一处能让心灵安然栖息的港湾。而 我的…

【算法设计与分析】-回溯法的回忆-学习【期末复习篇章】

引言 简单说,迷宫问题的求解方法就是走的通就走&#xff0c;走不通 就回头寻找另外的路径的一种满足某约束条件的穷举式 搜索技术 回溯法是一种在解空间中搜索可行解或最优解的方法。 该方法通常将解空间看做树形结构&#xff0c;即状态空间树。从根结 点开始,以深度优先对状态…

李沐读论文-启发点记录2:Resnet--残差连接--kaiming老师神作

&#xff08;一&#xff09;可以借鉴&#xff1a; 1. 计算机视觉的论文&#xff0c;都会在第一页的右上角&#xff0c;放上一张好看的图&#xff01; 2.bottleNet的设计——很大程度上节省了计算FLOPs开销&#xff0c;这是Resnet50及其更大版本都会用到的设计。 3.Resnet在de…

[RK3566-Android11] 使用SPI方式点LED灯带-JE2815/WS2812,实现呼吸/渐变/随音量变化等效果

问题描述 之前写了一篇使用GPIO方式点亮LED灯带的文章 https://blog.csdn.net/jay547063443/article/details/134688745?fromshareblogdetail&sharetypeblogdetail&sharerId134688745&sharereferPC&sharesourcejay547063443&sharefromfrom_link 使用GPIO…

OceanBase 首席科学家阳振坤:大模型时代的数据库思考

2024年 OceanBase 年度大会 即将于10月23日&#xff0c;在北京举行。 欢迎到现场了解更多“SQL AI ” 的探讨与分享&#xff01; 近期&#xff0c;2024年金融业数据库技术大会在北京圆满举行&#xff0c;聚焦“大模型时代下数据库的创新发展”议题&#xff0c;汇聚了国内外众多…

详细尝鲜flutter

flutter 161由于官方的汉化文档感觉还是有很多没有汉化的地方 &#xff0c;所以自己打一遍的同时写下了以下笔记 社区生态 官方文档 所有的控件:Widget 目录 | Flutter 中文文档 - Flutter 中文开发者网站 - Flutter 官方论坛的教程 Flutter Widget框架概述 - Flutter中文网…

微信小程序中关闭默认的 `navigationBar`,并使用自定义的 `nav-bar` 组件

要在微信小程序中关闭默认的 navigationBar&#xff0c;并使用自定义的 nav-bar 组件&#xff0c;你可以按照以下步骤操作&#xff1a; 1. 关闭默认的 navigationBar 在你的页面的配置文件 *.json 中设置 navigationBar 为 false。你需要在页面的 JSON 配置文件中添加以下代码…

JS 中 reduce()方法及使用

摘要&#xff1a; 开发中经常会遇到求合计的状况&#xff01;比如和&#xff0c;积等&#xff01;这次遇到的是求合计的和&#xff01; reduce()方法是JavaScript中Array对象的一种高阶函数&#xff0c;用于对数组中的每个元素执行一个由您提供的reducer函数&#xff08;回调函…

内置数据类型、变量名、字符串、数字及其运算、数字的处理、类型转换

内置数据类型 python中的内置数据类型包括&#xff1a;整数、浮点数、布尔类型&#xff08;以大写字母开头&#xff09;、字符串 变量名 命名变量要见名知意&#xff0c;确保变量名称具有描述性和意义&#xff0c;这样可以使得代码更容易维护&#xff0c;使用_可以使得变量名…

STM32-Modbus协议(一文通)

Modbus协议原理 RT-Thread官网开源modbus RT-Thread官方提供 FreeModbus开源。 野火有移植的例程。 QT经常用 libModbus库。 Modbus是什么&#xff1f; Modbus协议&#xff0c;从字面理解它包括Mod和Bus两部分&#xff0c;首先它是一种bus&#xff0c;即总线协议&#xff0c;和…

学习threejs,利用THREE.ExtrudeGeometry拉伸几何体实现svg的拉伸

&#x1f468;‍⚕️ 主页&#xff1a; gis分享者 &#x1f468;‍⚕️ 感谢各位大佬 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍⚕️ 收录于专栏&#xff1a;threejs gis工程师 文章目录 一、&#x1f340;前言1.1 ☘️THREE.ExtrudeGeometry拉伸…

通过ssh端口反向通道建立并实现linux系统的xrdp以及web访问

Content 1 问题描述2 原因分析3 解决办法3.1 安装x11以及gnome桌面环境查看是否安装x11否则使用下面指令安装x11组件查看是否安装gnome否则使用下面指令安装gnome桌面环境 3.2 安装xrdp使用下面指令安装xrdp&#xff08;如果安装了则跳过&#xff09;启动xrdp服务 3.3 远程服务…

C2W4.LAB.Word_Embedding.Part1

理论课&#xff1a;C2W4.Word Embeddings with Neural Networks 文章目录 Word Embeddings First Steps: Data PreparationCleaning and tokenizationSliding window of wordsTransforming words into vectors for the training setMapping words to indices and indices to w…

七,Linux基础环境搭建(CentOS7)- 安装Scala和Spark

Linux基础环境搭建&#xff08;CentOS7&#xff09;- 安装Scala和Spark 大家注意以下的环境搭建版本号&#xff0c;如果版本不匹配有可能出现问题&#xff01; 一、Scala下载及安装 Scala是一门多范式的编程语言&#xff0c;一种类似java的编程语言&#xff0c;设计初衷是实现…

合并数组的两种常用方法比较

在 JavaScript 中&#xff0c;合并数组的两种常用方法是使用扩展运算符 (...) 和使用 push 方法。 使用扩展运算符 this.items [...this.items, ...data.items]; 优点&#xff1a; 易于理解&#xff1a;使用扩展运算符的语法非常直观&#xff0c;表达了“将两个数组合并成一个…

24.redis高性能

Redis的单线程和高性能 Redis是单线程吗&#xff1f; Redis 的单线程主要是指 Redis 的网络 IO 和键值对读写是由一个线程来完成的&#xff0c;这也是 Redis 对外 提供键值存储服务的主要流程。 Redis 的多线程部分&#xff0c;比如持久化、异步删除、集群数据同步等&#xff…