数据结构与算法:数据结构基础

目录

数组

定义

形式

顺序存储

基本操作

读取元素

更新元素

插入元素

删除元素

扩容

初始化

时机

步骤

优劣势

链表

定义

单向链表

特点

双向链表

随机存储

基本操作

查找节点

更新节点

插入节点

删除元素

数组VS链表

栈与队列

定义

基本操作

1.入栈

2.出栈

队列

定义

基本操作

1.入队

2.出队

栈和队列的运用

1.栈的应用

2.队列的运用

3.双端队列

4.优先队列

散列表

定义

哈希函数

实现

读写操作

写操作

读操作

哈希冲突

解决办法


数组

定义

        有限个相同类型变量所组成的有序集合,数组中每个变量都被称为元素。数组是最简单、最常见的数据结构。

形式

以整形数组为例:

        数组中的每一个元素也有着自己的下标,只不过这个下标从0开始,一直到数组长度-1。

顺序存储

        数组的另一个特点,是在内存中顺序存储,因此可以很好地实现逻辑上的顺序表。

        内存是由一个个连续的内存单元组成的,每一个内存单元都有自己的地址。在 这些内存单元中,有些被其他数据占用了,有些是空闲的。

        数组中的每一个元素,都存储在小小的内存单元中,并且元素之间紧密排列, 既不能打乱元素的存储顺序,也不能跳过某个存储单元进行存储

        不同类型的数组,每个元素所占的字节个数也不同

基本操作

读取元素

        读取元素是最简单的操作。由于数组在内存中顺序存储,所以 只要给出一个数组下标,就可以读取到对应的数组元素

注意:

        通过下标读取数组,下标必须在数组长度范围内,否则会出现数组越界

示例:

int[] array = new int[]{3,1,2,5,4,9,7,2};System.out.println(array[3]);

更新元素

        把数组中某一个元素的值替换为一个新值,直接利用数组下标,就可以把新值赋给该元素。

示例:

1. int[] array = new int[]{3,1,2,5,4,9,7,2};
2. // 给数组下标为5的元素赋值
3. array[5] = 10;
4. // 输出数组中下标为5的元素
5. System.out.println(array[5]);

插入元素

数组的实际元素数量有可能小于数组的长度,所以存在3种情况:

1.尾部插入

        是最简单的情况,直接把插入的元素放在数组尾部的空闲位置即 可,等同于更新元素的操作

2.中间插入

        由于数组的每一个元素都有其固定下标,所以不得 不首先把插入位置及后面的元素向后移动,腾出地方,再把要插入的元素放到对应 的数组位置上。

3.超范围插入

        可以创建一个新数组,长度是旧数组的2倍,再把旧数组中的元素统统复制 过去,这样就实现了数组的扩容。

删除元素

        数组的删除操作和插入操作的过程相反,如果删除的元素位于数组中间,其后 的元素都需要向前挪动1位。

/**
2. * 数组删除元素
3. * @param index 删除的位置
4. */
5. public int delete(int index) throws Exception {
6. //判断访问下标是否超出范围
7. if(index<0 || index>=size){
8. throw new IndexOutOfBoundsException("超出数组实际元素范围!");
9. }
10. int deletedElement = array[index];
11. //从左向右循环,将元素逐个向左挪1位
12. for(int i=index; i<size-1; i++){
13. array[i] = array[i+1];
14. }
15. size--;
16. return deletedElement;
17. }

扩容

初始化

        如果参数等于0,则将数组初始化为一个空数组,

        如果不等于0,将数组初始化为一个容量为10的数组

时机

     当数组的大小大于初始容量的时候(比如初始为10,当添加第11个元素的时候),就会进行扩容,新的容量为旧的容量的1.5倍

步骤

        1:定义一个新数组,新数组的长度要比原数组增加或者减小;

        2:将原数组中的元素拷贝到新数组中;

        3:将原数组的名称变量指向新数组

优劣势

        优点:拥有非常高效的随机访问能力,只要给出下标,就 可以用常量时间找到对应元素

        缺点:由于数 组元素连续紧密地存储在内存中,插入、删除元素都会导致大量元素被迫移动,影响效率

适合的是读操作多、写操作少的场景。

链表

定义

        是一种在物理上非连续、非顺序的数据结构,由若干节点组成。

单向链表

        每一个节点又包含两部分,一部分是存放数据的变量data,另一部 分是指向下一个节点的指针next。

private static class Node {

        int data;

        Node next;

}

        链表的第1个节点被称为头节点,最后1个节点被称为尾节点,尾节点的next指 针指向空。

特点

        与数组按照下标来随机寻找元素不同,对于链表的其中一个节点A,我们只能根 据节点A的next指针来找到该节点的下一个节点B,再根据节点B的next指针找到下 一个节点C……

双向链表

        双向链表比单向链表稍微复杂一些,它的每一个节点除了拥有data和next指 针,还拥有指向前置节点的prev指针

随机存储

链表在内存中的存储方式则 是随机存储。

内存分配方式:链表则采用了见缝插针的方式,链表的每一个节点分布在内存的不同位 置,依靠next指针关联起来。这样可以灵活有效地利用零散的碎片空间

基本操作

查找节点

        在查找元素时,链表不像数组那样可以通过下标快速进行定位,只能从头节点开始向后一个一个逐步查找。

例如给出一个链表,需要查找从头节点开始的第3个节点。

        第1步,将查找的指针定位到头节点

        第2步,根据头节点的next指针,定位到第2个节点。

        第3步,根据第2个节点的next指针,定位到第3个节点,查找完毕。

更新节点

        如果不考虑查找节点的过程,链表的更新过程会像数组那样简单,直接把旧数 据替换成新数据即可。

插入节点

与数组类似,链表插入节点时,同样分为3种情况:

1.尾部插入:把最后一个节点的next指针指向新插入的节点即 可

2.头部插入:把新节点的next指针指向原先的头节点;把新节点变为链表的头节点

3.中间插入:新节点的next指针,指向插入位置的节点;插入位置前置节点的next指针,指向新节点。

只要内存空间允许,能够插入链表的元素是无穷无尽的,不需要像数组那样考 虑扩容的问题

Node insertedNode = new Node(data);
if(size == 0){
//空链表
head = insertedNode;
last = insertedNode;
} else if(index == 0){
//插入头部
insertedNode.next = head;
head = insertedNode;
}else if(size == index){
//插入尾部
last.next = insertedNode;
last = insertedNode;
}else {
//插入中间
Node prevNode = get(index-1);
insertedNode.next = prevNode.next;
prevNode.next = insertedNode;
}
size++;

删除元素

链表的删除操作同样分为3种情况:

1.尾部删除:把倒数第2个节点的next指针指向空即可

2.头部删除:把链表的头节点设为原先头节点的next指针即可

3.中间删除:把要删除节点的前置节点的next指针,指向要删除元 素的下一个节点即可。

        Java拥有自动化的垃圾回收机制,所以我们不用刻意去释放被删除的节点,只要没有外部引用指向它们,被删除的节点 会被自动回收。

// 头节点指针
private Node head;
// 尾节点指针
private Node last;
// 链表实际长度
private int size;Node removedNode = null;
if(index == 0){
//删除头节点
removedNode = head;
head = head.next;
}else if(index == size-1){
//删除尾节点
Node prevNode = get(index-1);
removedNode = prevNode.next;
prevNode.next = null;
last = prevNode;
}else {
//删除中间节点
Node prevNode = get(index-1);
Node nextNode = prevNode.next.next;
removedNode = prevNode.next;
prevNode.next = nextNode;
}
size--;

数组VS链表

数组的优势在于能够快速定位元素, 对于读操作多、写操作少的场景更合适;

链表的优势在于能够灵活地进行插入和删除操 作,如果需要在尾部频繁插入、删除元素,用链表更合适一些

栈与队列

定义

        是一种线性数据结构。最早进入的元素存放的位置叫作栈底(bottom),最后进入的元素存放的位置叫作栈顶

数组实现:

链表实现:

基本操作

1.入栈

把新元素放入栈中,只允许从栈顶一侧放入元素,新元素的位置将会成为新的栈顶
数组为例:

2.出栈

把元素从栈中弹出,只有栈顶元素才允许出栈,出栈元素的前一个元素将会成为新的栈顶。

数组为例:

队列

定义

        是一种线性数据结构,它的特征和行驶车辆的单行隧道很相似。不同于栈的先入后出,队列中的元素只能先入先出 。队列的出口端叫作 队头 ,队列的入口端叫作 队尾
数组实现:
链表实现:

基本操作

1.入队

就是把新元素放入队列中,只允许在队尾的位置放入元素,新元素的下一个位置将会成为新的队尾

2.出队

就是把元素移出队列,只允许在队头一侧移出元素,出队元素的后一个元素将会成为新的队头

栈和队列的运用

1.栈的应用

        实现递归的逻辑,就可以用栈来代替,因为栈可以回溯方法的调用链

        面包屑导航,使用户在浏览页面时可以轻松地回溯到上一级或更上一级页面

2.队列的运用

        在多线程中,争夺公平锁的等待队列,就是按照访问顺序来决定线程在队列中的次序的

        网络爬虫实现网站抓取时,也是把待抓取的网站URL存入队列中,再按照存入队列的顺序来依次抓取和解析的

3.双端队列

        双端队列这种数据结构,可以说综合了栈和队列的优点,对双端队列来说,从队头一端可以入队或出队,从队尾一端也可以入队或出队。
把栈和队列的特点结合起来,既可以先入先出,也可以先入后出

4.优先队列

谁的优先级最高,谁先出队
优先队列已经不属于线性数据结构的范畴了,它是基于二叉堆来实现的

散列表

定义

散列表也叫作 哈希表 ,这种数据结构提供了 键(Key) (Value) 的映射关系。只要给出一个Key,就可以高效查找到它所匹配的Value,时 间复杂度接近于 O(1)

哈希函数

通过某种方式,把Key和数组下标进行转换。这个中转站就叫作 哈希函数;

实现

以hashMap为例:

每一个对象都有属于自己的hashcode,这个hashcode是区分不同对象的重要标识,无论对象自身的类型是什么,它们的hashcode都是一个整型变量,整型变量想要转化成数组的下标也就不难实现了:
index = HashCode (Key) % Array.length

读写操作

写操作

写操作就是在散列表中插入新的键值对
实现: 插入一组Key为002931、Value为王五的键值对
        1.通过哈希函数,把Key转化成数组下标
        2.如果数组下标5对应的位置没有元素,就把这个Entry填充到数组下标5 的位置

读操作

读操作就是通过给定的Key,在散列表中查找对应的Value
实现: 查找Key为002936的Entry在散列表中所对应的值
        1.通过哈希函数,把Key转化成数组下标2
        2. 找到数组下标2所对应的元素,如果这个元素的Key是002936,那么就 找到了;如果Key不是002936也没关系,由于数组的每个元素都与一个链表对 应,我们可以顺着链表慢慢往下找,看看能否找到与Key相匹配的节点

哈希冲突

        由于数组的长度是有限的,当插入的Entry越来越多时,不同的Key通过哈希函数获得的下标有可能是相同的。例如002936这个Key对应的数组下标是2;002947这个Key对应的数组下标也是2。

解决办法

解决哈希冲突的方法主要有两种,一种是开放寻址法,一种是链表法
开放寻址法当一个Key通过哈希函数获得对应的数组下标已被占用时,我们可以“另谋高就”,寻找下一个空档位置:
链表法这种方法被应用在了Java的集合类HashMap当中;
HashMap数组的每一个元素不仅是一个Entry对象,还是一个链表的头节点。每一个Entry对象通过next指针指向它的下一个Entry节点。当新来的Entry映射到与 之冲突的数组位置时,只需要插入到对应的链表中即可。

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

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

相关文章

uniapp使用webview将页面转换成图片支持h5、app、小程序

uniapp使用webview将页面转换成图片支持h5、app、小程序 在uniapp项目中新建主页和webview页面 index.vue代码 <template><view><!-- 微信小程序要设置src为网络路径 --><web-view src"/hybrid/html/webview.html"></web-view><…

51单片机的智能台灯控制系统仿真( proteus仿真+程序+原理图+报告+讲解视频)

51单片机的红外光敏检测智能台灯控制系统仿真 1.主要功能&#xff1a;2.仿真3. 程序代码4. 原理图5. 设计报告6. 设计资料内容清单&&下载链接 51单片机的红外光敏检测智能台灯控制系统仿真( proteus仿真程序原理图报告讲解视频&#xff09; 仿真图proteus7.8及以上 程…

SpringMVC应用

文章目录 一、常用注解二、参数传递2.1 基础类型String2.2 复杂类型2.3 RequestParam2.4.路径传参 PathVariable2.4 Json数据传参 RequestBody2.5 RequestHeader 三、方法返回值3.1 void3.2 Stringmodel3.3 ModelAndView 一、常用注解 SpringMVC是一个基于Java的Web框架&#…

深度学习在医疗保健领域的应用:从图像识别到疾病预测

文章目录 深度学习在医学影像识别中的应用1. 癌症检测2. 病理学图像分析3. 医学图像分割 深度学习在疾病预测中的应用1. 疾病风险预测2. 疾病诊断辅助3. 药物研发 深度学习在个性化治疗中的应用1. 基因组学分析2. 临床数据集成 深度学习在医疗保健中的挑战和未来数据隐私和安全…

【小吉送书—第二期】阿里后端开发:抽象建模经典案例

文章目录 0.引言1.抽象思维2.软件世界中的抽象2.1 命名抽象2.2 分层抽象2.3 原则抽象 3. 经典抽象案例3.1 方案一&#xff1a;战术抽象&#xff0c;多快好省&#xff0c;跑步前进3.2 方案二&#xff1a;深入分析&#xff0c;透过表象&#xff0c;探寻本质 5. 推荐一本书&#x…

给 Ubuntu 操作系统配置静态 IP

针对 Ubuntu 22.04.3 操作系统的静态 IP 配置 一、查看初始的网络信息 查看网卡名称 ifconfig查看网关信息 route -n二、编辑网络配置文件 编辑文件&#xff0c;配置文件的名称可能不一样&#xff0c;自己去 /etc/netplan/ 目录查看 sudo vim /etc/netplan/01-network-manager-…

虚幻引擎集成web前端<一>:win环境UE4.27导出像素流并集成到vue2环境(附案例)

本案例附件&#xff1a;https://download.csdn.net/download/rexfow/88303544 第一部分&#xff1a;虚幻引擎导出像素流windows包 第1步&#xff1a;软件设置 -AudioMixer -PixelStreamingIPlocalhost -PixelStreamingPort8888 第2步&#xff1a;信令服务器设置 1、执行run_l…

WebSocket原理简介

慢聊Go之GoLang中使用Gorilla Websocket&#xff5c;Go主题月 - 掘金 (juejin.cn) 【Go项目】24. WebSocket 基本原理_哔哩哔哩_bilibili 1.http和socket的区别 1&#xff09; http要先给服务器发请求&#xff0c;然后才会得到响应&#xff0c;基本是一问一答式。 而socke…

DataX实现Mysql数据同步到ElasticSearch(ES)

Linux环境要求 jdk1.8及以上 python2 准备工作 Linux安装jdk yum install -y java-1.8.0-openjdk.x86_64查看是否安装成功 java -versionlinux安装python yum install -y python查看python版本号&#xff0c;判断是否安装成功 python --version下载DataX&#xff1a; Dat…

部署elasticsearch集群

创建es集群 编写一个docker-compose.yaml文件&#xff0c;内容如下 version: 2.2 services:es01:image: elasticsearch:7.12.1container_name: es01environment:- node.namees01- cluster.namees-docker-cluster- discovery.seed_hostses02,es03- cluster.initial_master_nod…

线性代数(六) 线性变换

前言 《线性空间》定义了空间&#xff0c;这章节来研究空间与空间的关联性 函数 函数是一个规则或映射&#xff0c;将一个集合中的每个元素&#xff08;称为自变量&#xff09;映射到另一个集合中的唯一元素&#xff08;称为因变量&#xff09;。 一般函数从 “A” 的每个元…

3招“挽回”:微信怎么恢复聊天记录

由于工作需要&#xff0c;经常使用微信与客户对接。害怕内存不足&#xff0c;所以我每个月都会清理一些不需要的文件&#xff0c;结果却不小心误删了与客户的聊天记录&#xff0c;有什么方法能够恢复回来吗&#xff1f; 聊天记录是微信的一个重要组成部分&#xff0c;里面保存着…

智慧公厕是对智慧城市“神经末梢”的有效激活,公共厕所实现可感知、可视化、可管理、可控制

在当今科技迅速发展的时代&#xff0c;智慧城市已经成为人们关注的热点话题。作为城市基础设施的重要组成部分&#xff0c;公共厕所也逐渐融入到智慧城市的建设中&#xff0c;成为城市管理的焦点之一。智慧公厕作为智慧城市的“神经末梢”&#xff0c;通过可感知、可视化、可管…

JP《乡村振兴振兴战略下传统村落文化旅游设计》许少辉书香续,山水长

JP《乡村振兴振兴战略下传统村落文化旅游设计》许少辉书香续&#xff0c;山水长

浅析目标检测入门算法:YOLOv1,SSD,YOLOv2,YOLOv3,CenterNet,EfficientDet,YOLOv4

本文致力于让读者对以下这些模型的创新点和设计思想有一个大体的认识&#xff0c;从而知晓YOLOv1到YOLOv4的发展源流和历史演进&#xff0c;进而对目标检测技术有更为宏观和深入的认知。本文讲解的模型包括&#xff1a;YOLOv1,SSD,YOLOv2,YOLOv3,CenterNet,EfficientDet,YOLOv4…

TypeScript:赋予JavaScript数据类型新的力量,提升编程效率!

&#x1f3ac; 岸边的风&#xff1a;个人主页 &#x1f525; 个人专栏 :《 VUE 》 《 javaScript 》 ⛺️ 生活的理想&#xff0c;就是为了理想的生活 ! ​ &#x1f4da; 前言 TypeScript&#xff1a;扩展JavaScript数据类型&#xff0c;赋予编程更强大的表达能力&#xff01…

React三属性之:props

作用 将父组件的参数传递给子组件 父组件 import ./App.css; import React from react; import PropsTest from ./pages/propsTest class App extends React.Component{render(){return(<div><h2>App组件</h2><PropsTest obj{{name:王惊涛,age:27}}>…

构建企业分支网络

构建企业分支网络 目录 1.1 项目背景 1.2 项目拓扑 1.3 项目需求 1.4 设备选型 1.5 技术选型 1.6 地址规划 1.6.1 交换设备地址规划表 1.6.2 路由设备地址规划表 1.6.3 ISP设备地址规划表 1.6.4 终端地址规划表 1.6.4.1 VLAN 规划 1.7 VLAN 规划 1.8 项目实施 1.…

无法将类型为“Newtonsoft.Json.Linq.JObject”的对象转换为类型“Newtonsoft.Json.Linq.JArray”解决方法

对于“Newtonsoft.Json.Linq.JObject”的对象强制类型转换为类型“Newtonsoft.Json.Linq.JArray”报错 第一的图为对象{“*************”:“********”} 第二个图片为数组[{“…”:“…”}] 在我这里进行强制转换对象转换为类型“Newtonsoft.Json.Linq.JArray”报错. 那我们…

Maven中导入jQuery,前端页面中引用jQuery

第一步pom文件中&#xff0c;配置maven坐标。 第二步&#xff0c;在前端页面中引用jQuery 注&#xff1a;该前端页面需要在web根目录即webapp目录下。可认为在maven中导入jQuery后&#xff0c;jquery.min.js文件放在目录webapp/webjars/jquery/3.3.1下。