数据结构青铜到王者第三话---ArrayList与顺序表(2)

续接上一话:

目录

一、ArrayList的使用(续)

1、ArrayList的扩容机制(续)

 五、ArrayList的相关练习

1、杨辉三角

2、简单的洗牌算法

六、ArrayList的问题及思考


一、ArrayList的使用(续)

1、ArrayList的扩容机制(续)

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

 Object[] elementData;   // 存放元素的空间private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};  // 默认空间private static final int DEFAULT_CAPACITY = 10;  // 默认容量大小public boolean add(E e) {ensureCapacityInternal(size + 1);  // Increments modCount!!elementData[size++] = e;return true;}private void ensureCapacityInternal(int minCapacity) {ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));}private static int calculateCapacity(Object[] elementData, int minCapacity) {if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {return Math.max(DEFAULT_CAPACITY, minCapacity);}return minCapacity;}private void ensureExplicitCapacity(int minCapacity) {modCount++;// overflow-conscious codeif (minCapacity - elementData.length > 0)grow(minCapacity);}private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;private void grow(int minCapacity) {// 获取旧空间大小int oldCapacity = elementData.length;// 预计按照1.5倍方式扩容int newCapacity = oldCapacity + (oldCapacity >> 1);// 如果用户需要扩容大小 超过 原空间1.5倍,按照用户所需大小扩容if (newCapacity - minCapacity < 0)newCapacity = minCapacity;// 如果需要扩容大小超过MAX_ARRAY_SIZE,重新计算容量大小if (newCapacity - MAX_ARRAY_SIZE > 0)newCapacity = hugeCapacity(minCapacity);// 调用copyOf扩容elementData = Arrays.copyOf(elementData, newCapacity);}private static int hugeCapacity(int minCapacity) {// 如果minCapacity小于0,抛出OutOfMemoryError异常if (minCapacity < 0)throw new OutOfMemoryError();return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE;}

总结:

(1)检测是否真正需要扩容,如果是调用grow准备扩容

(2)预估需要库容的大小

                初步预估按照1.5倍大小扩容

                如果用户所需大小超过预估1.5倍大小,则按照用户所需大小扩容

                真正扩容之前检测是否能扩容成功,防止太大导致扩容失败

(3)使用copyOf进行扩容

 五、ArrayList的相关练习

1、杨辉三角

(118. 杨辉三角 - 力扣(LeetCode))

        分析:

   

        这里就是外侧全是1,其他的分别是上面紧挨着的两个的加和。但是,显然我们不能像左图一样创建顺序表,可以转换成如右图所示的样式!!!如右图,最外侧都为1,若位置为(i,j),则[i][j] = [i-1][j] + [i-1][j-1]

        因此,我们创建一个空列表ret,用来存储所有的行(每行是一个整数列表)。

        先处理第一行,元素为1;再循环生成一共numRows行,将每行的第一个元素添加为1。        

        利用上一行计算中间元素,最后添加上尾巴(1)。

class Solution {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++) {int val1 = preRow.get(j);int val2 = preRow.get(j-1);curRow.add(val1+val2);}//尾巴curRow.add(1);ret.add(curRow);}return ret;}
}

2、简单的洗牌算法

(一副新牌,四种花色(♥,♦,♠,♣),数值分别为1--13,经过一系列的洗牌之后,分别发给三个人每人5张(轮流抓牌),最后展示剩余牌和三人手里的牌)

public class Card {private String suit;//花色private int rank;   //牌面值public Card(String suit, int rank) {this.suit = suit;this.rank = rank;}@Overridepublic String toString() {/*return "Card{" +"suit='" + suit + '\'' +", rank=" + rank +'}';*/return String.format("[%s %d]", suit, rank);}
}
import java.util.List;
import java.util.ArrayList;
import java.util.Random;public class CardDemo {public static final String[] suits = {"♥","♠","♣","♦"};//买来一副新牌public List<Card> buyCard() {List<Card> cardList = new ArrayList<>(52);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);}}private void swap(List<Card> cardList,int i,int j) {/*Card tmp =  cardList[i];cardList[i] = cardList[j];cardList[j] = tmp;*/Card tmp = cardList.get(i);cardList.set(i,cardList.get(j));cardList.set(j,tmp);}//分别发牌public List<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);}}return hand;}
}
import java.util.*;
public class Test {public static void main(String[] args) {CardDemo cardDemo = new CardDemo();//1. 买52张牌List<Card> cardList = cardDemo.buyCard();System.out.println("刚买回来的牌:");System.out.println(cardList);//2. 洗牌cardDemo.shuffle(cardList);System.out.println("洗过的牌:");System.out.println(cardList);//3. 3个人每个人轮流发牌5张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);}

运行程序可以得到类似的运行结果:

刚买回来的牌:
[[♠ 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]]
洗过的牌:
[[♥ 11], [♥ 6], [♣ 13], [♣ 10], [♥ 13], [♠ 2], [♦ 1], [♥ 9], [♥ 12], [♦ 5], [♥ 8], [♠ 6], [♠ 3], [♥ 5], [♥ 1], [♦ 6], [♦ 13], [♣ 12], [♦ 12], 
[♣ 5], [♠ 4], [♣ 3], [♥ 7], [♦ 3], [♣ 2], [♠ 1], [♦ 2], [♥ 4], [♦ 8], [♠ 10], [♦ 11], [♥ 10], [♦ 7], [♣ 9], [♦ 4], [♣ 8], [♣ 7], [♠ 8], [♦ 9], [♠ 
12], [♠ 11], [♣ 11], [♦ 10], [♠ 5], [♠ 13], [♠ 9], [♠ 7], [♣ 6], [♣ 4], [♥ 2], [♣ 1], [♥ 3]]
第1个人的牌:[[♥ 11], [♣ 10], [♦ 1], [♦ 5], [♠ 3]]
第2个人的牌:[[♥ 6], [♥ 13], [♥ 9], [♥ 8], [♥ 5]]
第3个人的牌:[[♣ 13], [♠ 2], [♥ 12], [♠ 6], [♥ 1]]
剩下的牌:
[[♦ 6], [♦ 13], [♣ 12], [♦ 12], [♣ 5], [♠ 4], [♣ 3], [♥ 7], [♦ 3], [♣ 2], [♠ 1], [♦ 2], [♥ 4], [♦ 8], [♠ 10], [♦ 11], [♥ 10], [♦ 7], [♣ 9], [♦ 
4], [♣ 8], [♣ 7], [♠ 8], [♦ 9], [♠ 12], [♠ 11], [♣ 11], [♦ 10], [♠ 5], [♠ 13], [♠ 9], [♠ 7], [♣ 6], [♣ 4], [♥ 2], [♣ 1], [♥ 3]]

六、ArrayList的问题及思考

1. ArrayList底层使用连续的空间,任意位置插入或删除元素时,需要将该位置后序元素整体往前或者往后搬 移,故时间复杂度为O(N)

解决方案:

        使用LinkedList:链表结构插入删除时间复杂度为O(1)
        分段数组:将大数组分成多个小数组块
        树状数组:使用树形结构维护数据

链表+数组组合(Unrolled Linked List)
class Chunk {private static final int CHUNK_SIZE = 64;private Object[] data;private int size;private Chunk next;// 插入删除只在当前chunk内搬移数据// 大大减少数据移动量
}

2. 增容需要申请新空间,拷贝数据,释放旧空间。会有不小的消耗。

解决方案:

        预分配策略:根据业务场景预估容量
        增量式扩容:每次只扩容部分空间
        内存池技术:预先申请大块内存
        使用链表结构:完全避免扩容问题

3. 增容一般是呈2倍的增长,势必会有一定的空间浪费。例如当前容量为100,满了以后增容到200,我们再继 续插入了5个数据,后面没有数据插入了,那么就浪费了95个数据空间。

解决方案:

        更合理的扩容因子:1.5倍或其他经验值
        缩容机制:当空间利用率低于阈值时自动缩容
        弹性数组:动态调整容量
        内存碎片整理:定期整理内存空间

弹性数组(弹性扩容因子)
class ElasticArrayList<T> {private static final double GROW_FACTOR = 1.5;private static final double SHRINK_THRESHOLD = 0.3;private Object[] elementData;private int size;// 根据使用情况动态调整扩容因子private int calculateNewCapacity(int minCapacity) {if (size < 1000) return (int)(size * 1.8);else if (size < 10000) return (int)(size * 1.5);else return (int)(size * 1.2);}// 自动缩容机制public void trimToUsage() {if (size < elementData.length * SHRINK_THRESHOLD) {elementData = Arrays.copyOf(elementData, size);}}
}

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

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

相关文章

[Vid-LLM] docs | 视频理解任务

链接&#xff1a;https://github.com/yunlong10/Awesome-LLMs-for-Video-Understanding docs&#xff1a;Vid-LLM 本项目是关于视频大语言模型(Vid-LLMs)的全面综述与精选列表。 探讨了这些智能系统如何处理和理解视频内容&#xff0c;详细介绍了它们多样的架构与训练方法、旨…

构建高可用Agent状态管理API:Gin+GORM全流程解析

继写给 Javaer 看的 Go Gin 教程 之后新写一篇真实的go开发教程:技术栈​&#xff1a;Go 1.21 Gin 1.9 GORM 2.0 MySQL 5.7 Docker一、技术选型&#xff1a;为什么是GinGORM&#xff1f;1.​性能与简洁性平衡​•​Gin​&#xff1a;基于httprouter的高性能框架&#xff0c…

[Java恶补day51] 46. 全排列

给定一个不含重复数字的数组 nums &#xff0c;返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。 示例 1&#xff1a; 输入&#xff1a;nums [1,2,3] 输出&#xff1a;[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]] 示例 2&#xff1a; 输入&#xff1a;nums …

《李沐读论文》系列笔记:论文读写与研究方法【更新中】

一、如何读论文读三遍&#xff1a;1. 第一遍读完标题和摘要后&#xff0c;直接跳到结论&#xff0c;这几个部分读完就大概知道文章在讲什么东西了&#xff0c;之后还可以看一下正文中的图表&#xff0c;判断一下这篇文章是否适合自己&#xff0c;是否要继续读&#xff1b;2. 第…

使用 gemini 来分析 github 项目

https://github.com/bravenewxyz/agent-c角色扮演&#xff1a; 你是一位顶级的软件架构师和代码审查专家&#xff0c;拥有超过20年的复杂系统设计和分析经验。你尤其擅长快速洞察一个陌生代码库的核心设计思想、关键实现和创新之处。我的目标&#xff1a; 我正在研究以下这个 G…

20.15 Hugging Face Whisper-large-v2中文微调实战:LoRA+混合精度单卡训练指南,3倍效率省90%显存

Hugging Face Whisper-large-v2中文微调实战:LoRA+混合精度单卡训练指南,3倍效率省90%显存 from transformers import Seq2SeqTrainingArguments, Seq2SeqTrainer# 训练参数配置(以中文语音识别任务为例) training_args = Seq2SeqTrainingArguments(output_dir="./wh…

GitGithub相关(自用,持续更新update 8/23)

文章目录Git常见命令1. 推送空提交2. 提交Clean-PR3. 回退add操作4. 交互式rebase4.1 切换模式4.2 保存与退出4.3 注意Rebase5. 合并多个commit问题一&#xff1a;Clone Github报错The TLS connection was non-properly terminated.TLS握手报错原因解决问题二&#xff1a;Faile…

改华为智能插座为mqtt本地控制

华为插座1. 打开插座后盖板&#xff0c;取出主板2.取下主板上的82663焊上esp32c3 supermini,热熔胶粘上&#xff0c;焊接电源正负极&#xff0c;及第5脚4.取下电源板阻容降压全部。因此电路不能提供足够电流给esp32工作。5.外接小型ac-dc电源5v6.刷代码Mqtt插座成品特别提醒&am…

2.4G和5G位图说明列表,0xff也只是1-8号信道而已

根据你提供的 SDK 代码&#xff0c;0xFF 仅表示启用 1 到 8 号信道&#xff08;即 2.4GHz 频段的信道&#xff09;。这是因为每个 BIT(x) 是一个位标志&#xff0c;0xFF 在二进制中对应的是 11111111&#xff0c;即启用信道 1 至 8。对于 5GHz 信道&#xff0c;你需要确保传输的…

【网络运维】Shell 脚本编程: for 循环与 select 循环

Shell 脚本编程&#xff1a; for 循环与 select 循环 循环语句命令常用于重复执行一条指令或一组指令&#xff0c;直到条件不再满足时停止&#xff0c;Shell脚本语言的循环语句常见的有while、until、for及select循环语句。 本文将详细介绍Shell编程中for循环和select循环的各种…

线性回归入门:从原理到实战的完整指南

线性回归入门&#xff1a;从原理到实战的完整指南线性回归是机器学习中最基础、最实用的算法之一 —— 它通过构建线性模型拟合数据&#xff0c;不仅能解决回归预测问题&#xff0c;还能为复杂模型&#xff08;如神经网络、集成算法&#xff09;提供基础思路。今天我们从 “直线…

积分排行样式

这个排名需要考虑不同child的位置<view class"pm-top"><!--背景 podiumtree 或 podium--><image class"podium-bg" :src"podium" mode"widthFix"></image><view class"podium-list"><vi…

【机器学习入门】1.1 绪论:从数据到智能的认知革命

引言&#xff1a;什么是机器学习&#xff1f;想象一下&#xff0c;当你在邮箱中看到一封邮件时&#xff0c;系统能自动识别出它是垃圾邮件&#xff1b;当你在购物网站浏览商品时&#xff0c;平台能精准推荐你可能感兴趣的物品&#xff1b;当自动驾驶汽车行驶在道路上时&#xf…

iptables 防火墙技术详解

目录 前言 1 iptables概述 1.1 Netfilter与iptables关系 1.1.1 Netfilter 1.1.2 iptables 1.1.3 两者关系 2 iptables的表、链结构 2.1 四表五链结构介绍 2.1.1 基本概念 2.1.2 四表功能*** 2.1.3 五链功能*** 2.2 数据包过滤的匹配流程*** 2.2.1 规则表应用顺序*…

SOME/IP-SD报文中 Entry Format(条目格式)-理解笔记3

&#x1f3af; 一、核心目标&#xff1a;解决“找服务”的问题 想象一下&#xff0c;一辆现代汽车里有上百个智能设备&#xff08;ECU&#xff09;&#xff0c;比如&#xff1a; 自动驾驶控制器&#xff08;需要“车速”服务&#xff09;中控大屏&#xff08;需要“导航”和“音…

AAA服务器技术

一、AAA认证架构理解AAA基本概念与架构先介绍&#xff1a; AAA是什么&#xff08;认证、授权、计费&#xff09;重点理解&#xff1a; 为什么需要AAA&#xff1f;它的三大功能分别解决什么问题&#xff1f;关联后续&#xff1a; 这是所有后续协议&#xff08;RADIUS/TACACS&…

客户生命周期价值帮助HelloFresh优化其营销支出

1 引言 了解客户的长期价值对HelloFresh至关重要。客户生命周期价值&#xff08;CLV&#xff09;代表了客户与公司关系的整个过程中所产生的总价值。通过预测这一指标&#xff0c;我们可以更明智地决定如何分配营销资源&#xff0c;以获得最大的影响。 在本文中&#xff0c;我…

Vue 2 中的 v-model和Vue3中的v-model

你问的是 v-model&#xff08;不是 v-modal 吧 &#x1f604;&#xff09;&#xff0c;我来帮你梳理一下 Vue2 和 Vue3 的 v-model 区别。&#x1f539; Vue 2 中的 v-model语法<input v-model"msg">v-model 本质上是 语法糖&#xff0c;等价于&#xff1a;<…

朴素贝叶斯算法学习总结

一、贝叶斯理论基础 1. 贝叶斯思想的核心 贝叶斯算法由 18 世纪英国数学家托马斯・贝叶斯提出&#xff0c;其核心是解决 “逆概” 问题 —— 区别于 “正向概率” 已知条件求结果概率的思路&#xff0c;逆概是通过观测到的结果&#xff0c;反推导致该结果的原因概率。比如在日常…

【Protues仿真】基于AT89C52单片机的舵机和直流电机控制

目录 1 PWM信号 1.1 三个最基本的量 1.1.1 周期 T&#xff08;Period&#xff09; 1.1.2脉冲宽度 Th&#xff08;High Time&#xff09; 1.1.3占空比 D&#xff08;Duty Cycle&#xff09; 1.2 为什么要用 PWM 1.3 关键参数对照表 1.4单片机里产生 PWM 的四种套路 1.4…