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

news2024/12/23 16:51:56

目录

数组

定义

形式

顺序存储

基本操作

读取元素

更新元素

插入元素

删除元素

扩容

初始化

时机

步骤

优劣势

链表

定义

单向链表

特点

双向链表

随机存储

基本操作

查找节点

更新节点

插入节点

删除元素

数组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.coloradmin.cn/o/995609.html

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

相关文章

分类预测 | Matlab实现SO-RF蛇群算法优化随机森林多输入分类预测

分类预测 | Matlab实现SO-RF蛇群算法优化随机森林多输入分类预测 目录 分类预测 | Matlab实现SO-RF蛇群算法优化随机森林多输入分类预测效果一览基本介绍程序设计参考资料 效果一览 基本介绍 Matlab实现SO-RF蛇群算法优化随机森林多输入分类预测&#xff08;完整源码和数据&…

notepad++去除每一行第二个等号之后的内容解决ResolvePackageNotFound

([^])$ 正则表达式用第一行的 https://blog.csdn.net/Charlotte_Si/article/details/132333988 原文的正则表达式不知道为什么没用

XXE-Lab for PHP

环境配置 1.将靶场进行下载.... https://github.com/c0ny1/xxe-lab 2.将PHPStudy的中间件与版本信息调制为php-5.4.45Apache访问以下地址开始练习... http://127.0.0.1/xxelabs/php_xxe/ 靶场实操 1.在登录界面输入账号密码并抓取数据包.... 2.尝试读取本地文件.... <…

第6章_瑞萨MCU零基础入门系列教程之串行通信接口(SCI)

本教程基于韦东山百问网出的 DShanMCU-RA6M5开发板 进行编写&#xff0c;需要的同学可以在这里获取&#xff1a; https://item.taobao.com/item.htm?id728461040949 配套资料获取&#xff1a;https://renesas-docs.100ask.net 瑞萨MCU零基础入门系列教程汇总&#xff1a; ht…

TCP协议报文,核心特性可靠的原因,超时重传详细介绍

目录 一、TCP协议 二、TCP核心特性的保障 三、保留的六位标志位对于应答报文的作用 四、如何处理丢包——超时重传的原理 五、超时重传的时间 一、TCP协议 每一行是四个字节&#xff0c;前面的20个字节是固定的&#xff08;TCP最短长度&#xff0c;20字节&#xff0c;选项…

进制和编码

目标&#xff1a;了解计算机中一些必备的尝试知识&#xff0c;了解常见名词背后的意义 1.python的运行方式 交互式运行脚本式运行 2.进制 2.1 进制的转换 计算机中底层的所有数据都是0101010101的形式存在的 八进制无法直接转为2进制 十进制转其他进制 bin(25) # 10进制转…

[XSCTF]easyxor

查看&#xff0c;main函数&#xff0c;首先是将输入的数据与key中的字符依次异或 在这个循环里&#xff0c;将异或得到的结果每次减去1&#xff0c;同时在数组里写上1&#xff0c;往后遍历数组 直到异或得到的结果减为0&#xff0c;在数组中写一个0进行标记 最后比较数组是否与…

百度飞桨(厦门)人工智能产业赋能中心签约,共创人工智能产业协同服务新生态...

9月8日&#xff0c;第二十三届中国国际投资贸易洽谈会在厦门正式开幕。 当日&#xff0c;厦门市思明区政府和火炬管委会&#xff0c;与百度正式签约&#xff0c;联手共建百度飞桨&#xff08;厦门&#xff09;人工智能产业赋能中心。思明区委书记林重阳&#xff0c;厦门市工信局…

【 NTR-domain ;NTR-C345C】

tissue inhibitors of metalloproteases (TIMP) TIMP NTR-C345C HPX-binding-domain [ MMP CAT(FN2)PEX (hemopexin) ] [ MMP-9-inhibitor a-2M (NTR-C345C) ] netrin Laminin-GLaminin-EGFNTR-domain

软件测试报告有什么用?

报告类型 不同的报告类型有不同的报告用途&#xff0c;以下分类别进行分析 1、登记测试报告 可以用于软件产品的增值税即征即退、软件企业的双软评估以及计算机系统集成资质的材料 2、鉴定\确认测试报告 可以用用于政府项目申报、高新认证、项目结题、创新产品认定、各类政…

c++11 override 和 final 关键字

1. final&#xff1a;修饰虚函数&#xff0c;表示该虚函数不能再被重写 如果一个类不想被继承&#xff1a;下面分别提供了c98 和 c11的不同写法 c 98 的写法&#xff1a; c 11 的写法&#xff1a;final最终类 2. override: 检查派生类虚函数是否重写了基类某个虚函数&#xff…

机器学习——K最近邻算法(KNN)

机器学习——K最近邻算法&#xff08;KNN&#xff09; 文章目录 前言一、原理二、距离度量方法2.1. 欧氏距离2.2. 曼哈顿距离2.3. 闵可夫斯基距离2.4. 余弦相似度2.5. 切比雪夫距离2.6. 马哈拉诺比斯距离2.7. 汉明距离 三、在MD编辑器中输入数学公式&#xff08;额外&#xff0…

C++数据结构X篇_11_C++栈的应用-后缀表达式求解

上篇C栈的应用-中缀转后缀中我们介绍了我们所熟知的中缀表达式转为后缀表达式&#xff0c;那么如何通过后缀表达式获得原表达式的值呢&#xff1f;本篇将会参考博文栈的应用-后缀表达式求解介绍计算机是如何基于后缀表达式计算的&#xff1f; 文章目录 1. 后缀表达式求解计算规…

【MySQL】一文带你理解索引事务及其原理

MySQL- 索引事务 文章目录 MySQL- 索引事务索引操作索引原理 事务操作并发执行 索引 索引是一种特殊的文件&#xff0c;包含着对数据表里所有记录的引用指针。可以对表中的一列或多列创建索引&#xff0c;并指定索引的类型&#xff0c;各类索引有各自的数据结构实现。 通俗来…

C++项目实战——基于多设计模式下的同步异步日志系统-④-日志系统框架设计

文章目录 专栏导读模块划分日志等级模块日志消息模块日志消息格式化模块日志消息落地模块日志器模块日志器管理模块异步线程模块 模块关系图 专栏导读 &#x1f338;作者简介&#xff1a;花想云 &#xff0c;在读本科生一枚&#xff0c;C/C领域新星创作者&#xff0c;新星计划导…

新知同享 | Web 开发性能提升,优化体验

更加强大且开放的 Web 可以简化开发工作并支持 AI 一起来看 2023 Google 开发者大会上 Web 开发值得重点关注的升级与成果 了解 Web 如何实现加速开发&#xff0c;更加便捷 精彩大会现场一览 Web 开发不断发展&#xff0c;每年都带来性能提升和功能迭代&#xff0c;开启丰富多…

【多线程】volatile 关键字

volatile 关键字 1. 保证内存可见性2. 禁止指令重排序3. 不保证原子性 1. 保证内存可见性 内存可见性问题: 一个线程针对一个变量进行读取操作&#xff0c;另一个线程针对这个变量进行修改操作&#xff0c; 此时读到的值&#xff0c;不一定是修改后的值&#xff0c;即这个读线…

用python实现基本数据结构【01/4】

说明 如果需要用到这些知识却没有掌握&#xff0c;则会让人感到沮丧&#xff0c;也可能导致面试被拒。无论是花几天时间“突击”&#xff0c;还是利用零碎的时间持续学习&#xff0c;在数据结构上下点功夫都是值得的。那么Python 中有哪些数据结构呢&#xff1f;列表、字典、集…

Linux 下spi设备驱动

参考&#xff1a; Linux kernel 有关 spi 设备树参数解析 Linux kernel 有关 spi 设备树参数解析 - 走看看 Linux SPI驱动框架(1)——核心层 Linux SPI驱动框架(1)——核心层_linux spi驱动模型_绍兴小贵宁的博客-CSDN博客 Linux SPI驱动框架(2)——控制器驱动层 Linux SPI驱…

如何进行SEO优化数据分析?(掌握正确的数据分析方法,让您的网站更上一层楼!)

在互联网时代&#xff0c;SEO优化已经成为了每一个网站运营者必备的技能。而在SEO优化中&#xff0c;数据分析更是至关重要的一环。在本文中&#xff0c;我们将会详细介绍如何正确的进行SEO优化数据分析&#xff0c;让您的网站更上一层楼&#xff01; 数据分析的重要性 数据分…