面试常见-Java 原生实现常见数据结构

Java 原生实现常见数据结构

文章目录

  • Java 原生实现常见数据结构
    • 一、引言
    • 二、数组(Array)
      • (一)概念
      • (二)代码实现
    • 三、链表(Linked List)
      • (一)概念
      • (二)代码实现(以单链表为例)
    • 四、栈(Stack)
      • (一)概念
      • (二)代码实现(基于数组实现栈)
    • 五、队列(Queue)
      • (一)概念
      • (二)代码实现(基于链表实现队列,也可以基于数组实现)

一、引言

在计算机科学的广袤领域中,数据结构犹如构建高楼大厦的基石,其重要性不言而喻。它们为数据的组织、存储与操作提供了标准化的方式,直接影响着算法的效率以及软件系统的整体性能。Java,作为一门广泛应用于企业级开发、移动应用开发、大数据处理等众多领域的编程语言,虽然其类库已经内置了丰富的数据结构实现,但深入探究如何用 Java 原生代码实现这些数据结构,对于每一位渴望提升编程内功、透彻理解计算机底层原理的开发者来说,都具有不可估量的价值。这不仅能够加深我们对数据结构本质的认识,更能在面对复杂多变的业务需求和性能挑战时,游刃有余地进行定制化开发与优化。本文将带领读者踏上一段深入的编程之旅,详细介绍如何用 Java 原生代码实现几种常见且极为重要的数据结构——数组、链表、栈和队列,并对它们的特性、应用场景以及性能特点进行全面剖析。

二、数组(Array)

(一)概念

数组,作为一种最为基础和常用的线性数据结构,在内存中呈现为一段连续的存储空间。它犹如一列整齐排列的储物柜,每个储物柜都有其唯一的编号(索引),从 0 开始,依次递增。这些储物柜专门用于存放相同类型的数据元素,例如整数、浮点数或者对象引用等。这种连续存储的特性使得数组在随机访问元素时具有极高的效率,只需通过索引,就能在常量时间内迅速定位并获取到指定位置的元素。然而,数组的长度在创建之初就已确定,这就好比储物柜的数量一旦确定就难以轻易更改,当需要插入或删除元素时,尤其是在数组中间位置进行操作时,往往需要移动大量后续元素,这无疑会带来较大的时间开销。

(二)代码实现

public class MyArray {private int[] data; // 存储数组元素的内部数组private int size; // 当前数组中元素的个数// 构造函数,初始化数组的容量public MyArray(int capacity) {data = new int[capacity];size = 0;}// 获取数组中元素的个数public int getSize() {return size;}// 获取数组的容量public int getCapacity() {return data.length;}// 判断数组是否为空public boolean isEmpty() {return size == 0;}// 在数组末尾添加元素public void addLast(int element) {add(size, element);}// 在数组指定位置添加元素public void add(int index, int element) {// 检查索引的合法性,若索引不合法则抛出异常if (index < 0 || index > size) {throw new IllegalArgumentException("索引不合法");}// 当数组已满时,进行扩容操作if (size == data.length) {resize(2 * data.length);}// 将指定位置及之后的元素向后移动一位,为新元素腾出空间for (int i = size - 1; i >= index; i--) {data[i + 1] = data[i];}data[index] = element;size++;}// 获取指定位置的元素public int get(int index) {// 检查索引的合法性,若索引不合法则抛出异常if (index < 0 || index >= size) {throw new IllegalArgumentException("索引不合法");}return data[index];}// 修改指定位置的元素public void set(int index, int newElement) {// 检查索引的合法性,若索引不合法则抛出异常if (index < 0 || index >= size) {throw new IllegalArgumentException("索引不合法");}data[index] = newElement;}// 查找元素是否在数组中存在,返回索引,不存在返回 -1public int contains(int element) {for (int i = 0; i < size; i++) {if (data[i] == element) {return i;}}return -1;}// 删除指定位置的元素,并返回被删除的元素public int remove(int index) {// 检查索引的合法性,若索引不合法则抛出异常if (index < 0 || index >= size) {throw new IllegalArgumentException("索引不合法");}int ret = data[index];// 将指定位置之后的元素向前移动一位,覆盖要删除的元素for (int i = index + 1; i < size; i++) {data[i - 1] = data[i];}size--;// 当数组元素数量过少时,进行缩容操作,以节省内存空间if (size == data.length / 4 && data.length / 2!= 0) {resize(data.length / 2);}return ret;}// 从数组末尾删除元素public int removeLast() {return remove(size - 1);}// 数组扩容或缩容方法private void resize(int newCapacity) {int[] newData = new int[newCapacity];// 将原数组中的元素复制到新数组中for (int i = 0; i < size; i++) {newData[i] = data[i];}data = newData;}
}

可以通过以下方式测试这个自定义数组类:

public class Main {public static void main(String[] args) {MyArray myArray = new MyArray(10);myArray.addLast(1);myArray.addLast(2);myArray.add(1, 3);System.out.println("数组元素个数: " + myArray.getSize());System.out.println("数组容量: " + myArray.getCapacity());System.out.println("索引为 1 的元素: " + myArray.get(1));myArray.remove(1);System.out.println("删除元素后数组元素个数: " + myArray.getSize());}
}

三、链表(Linked List)

(一)概念

链表,同样属于线性数据结构的范畴,但与数组截然不同。它就像是一条由多个节点连接而成的链条,每个节点恰似链条上的一环,包含了数据元素以及指向下一个节点的指针(在单链表的情况下)。链表的节点在内存中并非连续存储,而是通过指针相互链接,这种离散存储的方式使得链表在插入和删除元素时具有独特的优势,只需调整相关节点的指针指向,无需像数组那样大规模地移动元素,从而在频繁进行插入和删除操作的场景中表现出色。然而,链表在随机访问元素方面相对较弱,因为要访问某个特定位置的元素,需要从链表头开始逐个遍历节点,直到找到目标节点,这一过程的时间复杂度为 O(n),其中 n 为链表的长度。

(二)代码实现(以单链表为例)

// 定义链表节点类
class ListNode {int val;ListNode next;ListNode(int val) {this.val = val;this.next = null;}
}// 自定义单链表类
public class MyLinkedList {private ListNode head; // 头节点private int size; // 链表节点个数// 获取链表长度public int getSize() {return size;}// 判断链表是否为空public boolean isEmpty() {return size == 0;}// 在链表头部添加节点public void addFirst(int val) {ListNode newNode = new ListNode(val);newNode.next = head;head = newNode;size++;}// 在链表指定位置添加节点public void add(int index, int val) {// 检查索引的合法性,若索引不合法则抛出异常if (index < 0 || index > size) {throw new IllegalArgumentException("索引不合法");}if (index == 0) {addFirst(val);return;}ListNode prev = head;// 找到要插入位置的前一个节点for (int i = 0; i < index - 1; i++) {prev = prev.next;}ListNode newNode = new ListNode(val);newNode.next = prev.next;prev.next = newNode;size++;}// 在链表末尾添加节点public void addLast(int val) {add(size, val);}// 获取指定位置的节点的值public int get(int index) {// 检查索引的合法性,若索引不合法则抛出异常if (index < 0 || index >= size) {throw new IllegalArgumentException("索引不合法");}ListNode cur = head;// 遍历链表,找到指定位置的节点for (int i = 0; i < index; i++) {cur = cur.next;}return cur.val;}// 修改指定位置节点的值public int set(int index, int newVal) {// 检查索引的合法性,若索引不合法则抛出异常if (index < 0 || index >= size) {throw new IllegalArgumentException("索引不合法");}ListNode cur = head;// 遍历链表,找到指定位置的节点for (int i = 0; i < index; i++) {cur = cur.next;}int oldVal = cur.val;cur.val = newVal;return oldVal;}// 查找元素是否在链表中存在,返回索引,不存在返回 -1public int contains(int val) {ListNode cur = head;for (int i = 0; i < size; i++) {if (cur.val == val) {return i;}cur = cur.next;}return -1;}// 删除指定位置的节点,并返回被删除节点的值public int remove(int index) {// 检查索引的合法性,若索引不合法则抛出异常if (index < 0 || index >= size) {throw new IllegalArgumentException("索引不合法");}if (index == 0) {return removeFirst();}ListNode prev = head;// 找到要删除位置的前一个节点for (int i = 0; i < index - 1; i++) {prev = prev.next;}ListNode retNode = prev.next;prev.next = retNode.next;size--;return retNode.val;}// 从链表头部删除节点,并返回被删除节点的值public int removeFirst() {if (isEmpty()) {throw new IllegalArgumentException("链表为空");}ListNode retNode = head;head = head.next;size--;return retNode.val;}// 从链表末尾删除节点,并返回被删除节点的值public int removeLast() {return remove(size - 1);}
}

以下是测试代码:

public class Main {public static void main(String[] args) {MyLinkedList myLinkedList = new MyLinkedList();myLinkedList.addFirst(1);myLinkedList.addLast(2);myLinkedList.add(1, 3);System.out.println("链表长度: " + myLinkedList.getSize());System.out.println("索引为 1 的节点值: " + myLinkedList.get(1));myLinkedList.remove(1);System.out.println("删除节点后链表长度: " + myLinkedList.getSize());}
}

四、栈(Stack)

(一)概念

栈,作为一种特殊的线性数据结构,遵循后进先出(LIFO)的操作原则。可以将其形象地比喻为一个只能从顶部放入和取出物品的储物箱。栈只提供了两个基本操作:入栈(push),即将元素压入栈顶;出栈(pop),即将栈顶元素弹出。这种特性使得栈在处理具有嵌套结构或需要回溯的问题时大显身手,例如函数调用栈、表达式求值以及括号匹配等场景。当一个函数被调用时,其相关的局部变量、返回地址等信息就会被压入栈中,当函数执行完毕后,这些信息又会按照后进先出的顺序从栈中弹出,从而实现函数调用的正确返回和资源的回收。

(二)代码实现(基于数组实现栈)

public class MyStack {private int[] data; // 存储栈元素的内部数组private int top; // 栈顶指针,指向栈顶元素的下一个位置// 构造函数,初始化栈的容量public MyStack(int capacity) {data = new int[capacity];top = 0;}// 判断栈是否为空public boolean isEmpty() {return top == 0;}// 获取栈中元素的个数public int size() {return top;}// 入栈操作public void push(int element) {// 当栈已满时,进行扩容操作if (top == data.length) {resize(2 * data.length);}data[top++] = element;}// 出栈操作,返回弹出的栈顶元素public int pop() {// 检查栈是否为空,若为空则抛出异常if (isEmpty()) {throw new IllegalArgumentException("栈为空");}int ret = data[top - 1];top--;// 当栈中元素数量过少时,进行缩容操作,以节省内存空间if (top == data.length / 4 && data.length / 2!= 0) {resize(data.length / 2);}return ret;}// 获取栈顶元素,但不弹出public int peek() {// 检查栈是否为空,若为空则抛出异常if (isEmpty()) {throw new IllegalArgumentException("栈为空");}return data[top - 1];}// 栈的扩容或缩容方法private void resize(int newCapacity) {int[] newData = new int[newCapacity];// 将原栈中的元素复制到新栈中for (int i = 0; i < top; i++) {newData[i] = data[i];}data = newData;}
}

测试代码示例:

public class Main {public static void main(String[] args) {MyStack myStack = new MyStack(10);myStack.push(1);myStack.push(2);System.out.println("栈顶元素: " + myStack.peek());System.out.println("弹出元素: " + myStack.pop());System.out.println("栈是否为空: " + myStack.isEmpty());}
}

五、队列(Queue)

(一)概念

队列,与栈类似,也是一种线性数据结构,但它遵循先进先出(FIFO)的原则。就像日常生活中的排队场景,先到达的人先接受服务。队列提供了两个核心操作:入队(enqueue),即在队尾添加元素;出队(dequeue),即从队头取出元素。队列在许多实际应用场景中有着广泛的应用,例如任务调度系统中,任务按照提交的先后顺序依次被处理;消息队列中,消息按照发送的顺序被接收和处理,以确保消息的顺序性和公平性。

(二)代码实现(基于链表实现队列,也可以基于数组实现)

// 定义队列节点类(与链表节点类似)
class QueueNode {int val;QueueNode next;QueueNode(int val) {this.val = val;this.next = null;}
}public class MyQueue {private QueueNode head; // 队头节点private QueueNode tail; // 队尾节点private int size; // 队列中元素的个数// 判断队列是否为空public boolean isEmpty() {return size == 0;}// 获取队列中元素的个数public int getSize() {return size;}// 入队操作,在队尾添加元素public void enqueue(int element) {QueueNode newNode = new QueueNode(element);if (isEmpty()) {head = tail = newNode;} else {tail.next = newNode;tail = newNode;}size++;}// 出队操作,从队头取出元素并返回public int dequeue() {if (isEmpty()) {throw new IllegalArgumentException("队列为空");}int ret = head.val;head = head.next;if (head == null) {tail = null;}size--;return ret;}//

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

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

相关文章

1. 机器学习基本知识(5)——练习题(参考答案)

20.&#x1f517;本章代码笔记&#x1f4d3;链接&#xff08;需要&#x1fa9c;&#xff09;&#xff1a;&#xff08;01_the_machine_learning_landscape.ipynb - Colab (google.com)&#xff09; 如果你不想通过上面的官方网址下载本章的笔记&#xff0c;还可以在本篇博文的…

通常一个 Xml 映射文件,都会写一个 Dao 接口与之对应, 请问,这个 Dao 接口的工作原理是什么?Dao 接口里的方法, 参数不同时,方法能重载吗?

Dao 接口 即 Mapper 接口 。接口 的 全 限 名 &#xff0c;就是 映 射 文 件 中 的 namespace 的值 &#xff1b; 接口 的 方 法 名 &#xff0c; 就 是 映 射 文 件 中 Mapper 的 Statement 的 id 值&#xff1b; 接 口 方 法 内 的 参数 &#xff0c; 就 是 传 递 给 sql 的参…

硬件设计 | Altium Designer软件PCB规则设置

基于Altium Designer&#xff08;24.9.1&#xff09;版本 嘉立创PCB工艺加工能力范围说明-嘉立创PCB打样专业工厂-线路板打样 规则参考-嘉立创 注意事项 1.每次设置完规则参数都要点击应用保存 2.每次创建PCB&#xff0c;都要设置好参数 3.可以设置默认规则&#xff0c;将…

WebDAV服务不能上传大文件,文件超过50M报错[0x800700DF]怎么办?

这个问题需要分别从服务端和客户端解决。 1.Windows客户端 解除50M文件限制&#xff0c;Windows访问Webdav服务时&#xff0c;大于50M文件提示错误[错误:0x800700DF] 部署了webdav&#xff0c;Windows10映射网络磁盘&#xff0c;传输文件超过大约50MB的文件会弹出“0x800700…

安全基础学习-keil调试汇编代码

初始目的是为了通过汇编编写CRC功能。 但是基础为0&#xff0c;所以目前从搭建工程开始记录。 大佬绕路。 &#xff08;一&#xff09;创建项目 1. 新建项目 打开 Keil uVision。选择 Project -> New uVision Project 创建一个新项目。选择你的目标设备&#xff08;如 AR…

安装qt 5.15.2笔记

撰文是2024年12月 最终实现了 1、用梯子下载了离线包5.14.2&#xff0c;最后没用 2、用内地镜像在线安装5.15.2&#xff0c;3分钟装完 正文开始&#xff0c;qt官方简称官方。 官方包官方自5.15.X起&#xff0c;不再提供的exe/run安装包https://download.qt.io/archive/qt/ …

Redis Java 集成到 Spring Boot

Hi~&#xff01;这里是奋斗的明志&#xff0c;很荣幸您能阅读我的文章&#xff0c;诚请评论指点&#xff0c;欢迎欢迎 ~~ &#x1f331;&#x1f331;个人主页&#xff1a;奋斗的明志 &#x1f331;&#x1f331;所属专栏&#xff1a;Redis &#x1f4da;本系列文章为个人学习笔…

【Syncfusion系列】Diagram 杂谈 第三篇 序列化和反序列化

目录 序列化保存C# 代码示例&#xff0c; 方式1 &#xff1a;C# 代码示例&#xff0c; 方式2 &#xff1a; 反序列化加载C# 代码示例, 方式1&#xff1a;C# 代码示例, 方式2&#xff1a; **如何序列化自定义属性**序列化和反序列化都存在的一个问题解决方式 图表是否已修改&…

麒麟信安推出支持信创PC的新一代云桌面方案,助力政务信创高效安全运维

12月11日&#xff0c;在第二届国家新一代自主安全计算系统产业集群融通生态大会上&#xff0c;麒麟信安发布了支持信创PC的新一代云桌面方案&#xff0c;该方案是基于国际TCI架构实现国产PC机云化纳管在国内的首次发布&#xff0c;并与银河麒麟桌面操作系统、长城国产PC整机实现…

中国科学院2001年数据结构试题

一、单项选择题(每空2分&#xff0c;共20分) 1&#xff0e;下列函数中渐近时间复杂度最小的是( )。 A&#xff0e;T1(n)nlog2n5000n B&#xff0e;T2(n)n2-8000n C&#xff0e;T3(n)nlog221-6000n D&#xff0e;T4(n)2nlog2n-7000n 2&#xff0e;线性表的静态链表存储结构与顺序…

MySQL数据表记录删操作

删除操作&#xff1a;作用删除表里的记录行&#xff08;都是整行整行的删除的&#xff09; 1.单表的删除 语法 delete from 表名 where 要删除的记录筛选条件; 案例&#xff1a;删除员工编号大于203的员工信息 delete from employees where employee_id>203; 2.多表的删除…

网络原理04

可靠传输&#xff0c;是TCP最核心的特性 可靠传输不是说数据100%传输给接收方了 1&#xff09;发送方发出数据后&#xff0c;能过知道接收方是否收到数据 2&#xff09;一旦发现对方没收到&#xff0c;可以通过一定的方法”补救” 1. 确认应答 发送方&#xff0c;把数据已…

微信小程序5-图片实现点击动作和动态加载同类数据

搜索 微信小程序 “动物觅踪” 观看效果 感谢阅读&#xff0c;初学小白&#xff0c;有错指正。 一、功能描述 a. 原本想通过按钮加载背景图片&#xff0c;来实现一个可以点击的搜索button&#xff0c;但是遇到两个难点&#xff0c;一是按钮大小调整不方便&#xff08;网上搜索…

Java里局部变量和成员变量的隐式初始化

注&#xff1a;本文是对另一篇文档&#xff08; https://blog.csdn.net/duke_ding2/article/details/142365872 &#xff09;的补充。 文章目录 环境初始化局部变量&#xff08;栈&#xff09;成员变量&#xff08;堆&#xff09;其它数组 分析安全性性能成员变量 VS. 局部变量…

孚盟云 MailAjax.ashx SQL注入漏洞复现

0x01 产品简介 上海孚盟软件有限公司是一家外贸SaaS服务提供商,也是专业的外贸行业解决方案专业提供商。 全新的孚盟云产品,让用户可以用云模式实现信息化管理,让用户的异地办公更加流畅,大大降低中小企业在信息化上成本,用最小的投入享受大型企业级别的信息化服务,主要…

“切片赋值”创建列表批量操作“新”方法(Python)

[start:end]切片赋值&#xff0c;扩展了list批量增减元素的操作能力。 (笔记模板由python脚本于2024年12月06日 15:07:56创建&#xff0c;本篇笔记适合研python基础的coder翻阅) 【学习的细节是欢悦的历程】 Python 官网&#xff1a;https://www.python.org/ Free&#xff1a;…

LabVIEW实现GPS通信

目录 1、GPS通信原理 2、硬件环境部署 3、程序架构 4、前面板设计 5、程序框图设计 6、测试验证 本专栏以LabVIEW为开发平台,讲解物联网通信组网原理与开发方法,覆盖RS232、TCP、MQTT、蓝牙、Wi-Fi、NB-IoT等协议。 结合实际案例,展示如何利用LabVIEW和常用模块实现物联网系…

Java简介:打开通往变成世界的大门

Java是什么&#xff1f;为什么它是全球开发者广泛使用的语言&#xff1f;本篇文章介绍Java的特点、应用场景以及“写一次&#xff0c;随处运行”的核心特性&#xff0c;让零基础的你建立对Java语言的初步认知。 注&#xff1a;此文章可以仅作了解&#xff0c;不影响之后的学习。…

Unraid实现相册同步与展示的方案探讨

背景&#xff1a;Unraid作为一个NAS系统&#xff0c;能够实现基本的NAS文件管理功能&#xff0c;但是不提供额外的功能如影音、同步、办公、和内网穿透等&#xff0c;这些在其他的NAS产品如群晖、绿联、威联通等都是提供支持的。然而unraid也有其他方案&#xff0c;即通过特别方…

常见的网络攻击手段

IP 欺骗 IP 是什么? 在网络中&#xff0c;所有的设备都会分配一个地址。这个地址就仿佛小蓝的家地址「多少号多少室」&#xff0c;这个号就是分配给整个子网的&#xff0c;「室」对应的号码即分配给子网中计算机的&#xff0c;这就是网络中的地址。「号」对应的号码为网络号…