数据结构与集合(一)

当我们谈论数据结构时,我们实际上在讨论一种组织和管理数据的方式。数据结构是计算机科学中非常重要的一部分,它为我们提供了存储、检索和操作数据的方法。在数据结构中,链表是一种基本且常用的数据结构,它由一系列节点组成,节点之间通过指针相互连接。

在本博客中,我们将深入探讨链表的原理、操作及其在实际应用中的重要性。无论您是初学者还是有一定经验的程序员,了解链表的基本概念和高级应用都将对您的编程技能产生积极的影响。

 

目录

1、数据结构剖析

1.1 研究对象一:数据间逻辑运算关系

1.2 研究对象二:数据的存储结构(或物理结构)

1.2.1 顺序结构

1.2.2 链式结构

1.2.3 索引结构

1.2.4 散列结构

1.3 研究对象三:运算结构

2、一维数组 

2.1 数组的特点

2.2 自定义数组

3、链表

3.1 链表的特点

3.2 自定义链表 

3.2.1 自定义单向链表

3.2.2 自定义双向链表

4、栈

4.1 栈的特点

4.2 Stack使用举例 

4.3 自定义栈

5、队列


1、数据结构剖析

简单来说,数据结构,就是一种程序设计优化的方法论,研究数据的逻辑结构物理结构以及它们之间相互关系,并对这种结构定义相应的运算目的是加快程序的执行速度、减少内存占用的空间。

1.1 研究对象一:数据间逻辑运算关系

数据的逻辑结构指反映数据元素之间的逻辑关系,而与数据的存储无关,是独立于计算机的。

  • 集合结构:数据结构中的元素之间除了“同属一个集合” 的相互关系外,别无其他关系。集合元素之间没有逻辑关系。

  • 线性结构:数据结构中的元素存在一对一的相互关系。比如:排队。结构中必须存在唯一的首元素和唯一的尾元素。体现为:一维数组、链表、栈、队列

  • 树形结构:数据结构中的元素存在一对多的相互关系。比如:家谱、文件系统、组织架构

  • 图形结构:数据结构中的元素存在多对多的相互关系。比如:全国铁路网、地铁图

1.2 研究对象二:数据的存储结构(或物理结构)

数据的物理结构/存储结构:包括数据元素的表示关系的表示。数据的存储结构是逻辑结构用计算机语言的实现,它依赖于计算机语言。

1.2.1 顺序结构

  • 顺序结构就是使用一组连续的存储单元依次存储逻辑上相邻的各个元素。

  • 优点: 只需要申请存放数据本身的内存空间即可,支持下标访问,也可以实现随机访问。

  • 缺点: 必须静态分配连续空间,内存空间的利用率比较低。插入或删除可能需要移动大量元素,效率比较低

1.2.2 链式结构

  • 不使用连续的存储空间存放结构的元素,而是为每一个元素构造一个节点。节点中除了存放数据本身以外,还需要存放指向下一个节点的指针。

  • 优点:不采用连续的存储空间导致内存空间利用率比较高,克服顺序存储结构中预知元素个数的缺点。插入或删除元素时,不需要移动大量的元素。

  • 缺点:需要额外的空间来表达数据之间的逻辑关系,不支持下标访问和随机访问。

1.2.3 索引结构

  • 除建立存储节点信息外,还建立附加的索引表来记录每个元素节点的地址。索引表由若干索引项组成。索引项的一般形式是:(关键字,地址)。

  • 优点:用节点的索引号来确定结点存储地址,检索速度快。

  • 缺点: 增加了附加的索引表,会占用较多的存储空间。在增加和删除数据时要修改索引表,因而会花费较多的时间。

1.2.4 散列结构

  • 根据元素的关键字直接计算出该元素的存储地址,又称为Hash存储。

  • 优点:检索、增加和删除结点的操作都很快。

  • 缺点:不支持排序,一般比用线性表存储需要更多的空间,并且记录的关键字不能重复。

1.3 研究对象三:运算结构

施加在数据上的运算包括运算的定义和实现。运算的定义是针对逻辑结构的,指出运算的功能;运算的实现是针对存储结构的,指出运算的具体操作步骤。

  • 分配资源,建立结构,释放资源

  • 插入和删除

  • 获取和遍历

  • 修改和排序


2、一维数组 

2.1 数组的特点

在Java中,数组是用来存放同一种数据类型的集合,注意只能存放同一种数据类型。

//只声明了类型和长度
数据类型[]  数组名称 = new 数据类型[数组长度];//声明了类型,初始化赋值,大小由元素个数决定
数据类型[] 数组名称 = {数组元素1,数组元素2,......}

物理结构特点:

1、申请内存:一次申请一大段连续的空间,一旦申请到了,内存就固定了。
2、不能动态扩展(初始化给大了,浪费;给小了,不够用),插入快,删除和查找慢。
-3、存储特点:所有数据存储在这个连续的空间中,数组中的每一个元素都是一个具体的数据(或对象),所有数据都紧密排布,不能有间隔。

2.2 自定义数组

class Array {private Object[] elementData;private int size;public Array(int capacity){elementData = new Object[capacity];}/*** 添加元素* @param value*/public void add(Object value){if(size >= elementData.length){throw new RuntimeException("数组已满,不可添加");}elementData[size] = value;size++;}/*** 查询元素value在数组中的索引位置* @param value* @return*/public int find(Object value){for (int i = 0; i < size; i++) {if(elementData[i].equals(value)){return i;}}return -1;}/*** 从当前数组中移除首次出现的value元素* @param value* @return*/public boolean delete(Object value){int index = find(value);if(index == -1){return false;}for(int i = index;i < size - 1;i++){elementData[i] = elementData[i + 1];}elementData[size - 1] = null;size--;return true;}/*** 将数组中首次出现的oldValue替换为newValue* @param oldValue* @param newValue* @return*/public boolean update(Object oldValue,Object newValue){int index = find(oldValue);if(index == -1){return false;}elementData[index] = newValue;return true;}/*** 遍历数组中所有数据*/public void print(){System.out.print("{");for (int i = 0; i < size; i++) {if(i == size - 1){System.out.println(elementData[i] + "}");break;}System.out.print(elementData[i] + ",");}}
}//测试类
public class ArrayTest {public static void main(String[] args) {Array arr = new Array(10);arr.add(123);arr.add("AA");arr.add(345);arr.add(345);arr.add("BB");arr.delete(345);arr.update(345,444);arr.print();}
}

3、链表

3.1 链表的特点

  • 逻辑结构:线性结构

  • 物理结构:不要求连续的存储空间

  • 存储特点:链表由一系列结点node(链表中每一个元素称为结点)组成,结点可以在代码执行过程中动态创建。每个结点包括两个部分:一个是存储数据元素的数据域另一个是存储下一个结点地址的指针域

3.2 自定义链表 

3.2.1 自定义单向链表

/*
单链表中的节点。
节点是单向链表中基本的单元。
每一个节点Node都有两个属性:一个属性:是存储的数据。另一个属性:是下一个节点的内存地址。*/
public class Node {// 存储的数据Object data;// 下一个节点的内存地址Node next;public Node(){}public Node(Object data, Node next){this.data = data;this.next = next;}
}
/*
链表类(单向链表)*/
public class Link<E> {// 头节点Node header;private int size = 0;public int size(){return size;}// 向链表中添加元素的方法(向末尾添加)public void add(E data){//public void add(Object data){// 创建一个新的节点对象// 让之前单链表的末尾节点next指向新节点对象。// 有可能这个元素是第一个,也可能是第二个,也可能是第三个。if(header == null){// 说明还没有节点。// new一个新的节点对象,作为头节点对象。// 这个时候的头节点既是一个头节点,又是一个末尾节点。header = new Node(data, null);}else {// 说明头不是空!// 头节点已经存在了!// 找出当前末尾节点,让当前末尾节点的next是新节点。Node currentLastNode = findLast(header);currentLastNode.next = new Node(data, null);}size++;}/*** 专门查找末尾节点的方法。*/private Node findLast(Node node) {if(node.next == null) {// 如果一个节点的next是null// 说明这个节点就是末尾节点。return node;}// 程序能够到这里说明:node不是末尾节点。return findLast(node.next); // 递归算法!}/*// 删除链表中某个数据的方法public void remove(Object obj){//略}// 修改链表中某个数据的方法public void modify(Object newObj){//略}// 查找链表中某个元素的方法。public int find(Object obj){//略}*/
}

3.2.2 自定义双向链表

/*
双向链表中的节点。*/
public class Node<E> {Node prev;E data;Node next;Node(Node prev, E data, Node next) {this.prev = prev;this.data = data;this.next = next;}
}
public class MyLinkedList<E> implements Iterable<E>{private Node first;  //链表的首元素private Node last;   //链表的尾元素private int total;public void add(E e){Node newNode = new Node(last, e, null);if(first == null){first = newNode;}else{last.next = newNode;}last = newNode;total++;}public int size(){return total;}public void delete(Object obj){Node find = findNode(obj);if(find != null){if(find.prev != null){find.prev.next = find.next;}else{first = find.next;}if(find.next != null){find.next.prev = find.prev;}else{last = find.prev;}find.prev = null;find.next = null;find.data = null;total--;}}private Node findNode(Object obj){Node node = first;Node find = null;if(obj == null){while(node != null){if(node.data == null){find = node;break;}node = node.next;}}else{while(node != null){if(obj.equals(node.data)){find = node;break;}node = node.next;}}return find;}public boolean contains(Object obj){return findNode(obj) != null;}public void update(E old, E value){Node find = findNode(old);if(find != null){find.data = value;}}@Overridepublic Iterator<E> iterator() {return new Itr();}private class Itr implements Iterator<E>{private Node<E> node = first;@Overridepublic boolean hasNext() {return node!=null;}@Overridepublic E next() {E value = node.data;node = node.next;return value;}}
}

自定义双向链表测试:

public class MyLinkedListTest {public static void main(String[] args) {MyLinkedList<String> my = new MyLinkedList<>();my.add("hello");my.add("world");my.add(null);my.add(null);my.add("java");my.add("java");my.add("atguigu");System.out.println("一共有:" + my.size());System.out.println("所有元素:");for (String s : my) {System.out.println(s);}System.out.println("-------------------------------------");System.out.println("查找java,null,haha的结果:");System.out.println(my.contains("java"));System.out.println(my.contains(null));System.out.println(my.contains("haha"));System.out.println("-------------------------------------");System.out.println("替换java,null后:");my.update("java","JAVA");my.update(null,"songhk");System.out.println("所有元素:");for (String s : my) {System.out.println(s);}System.out.println("-------------------------------------");System.out.println("删除hello,JAVA,null,atguigu后:");my.delete("hello");my.delete("JAVA");my.delete(null);my.delete("atguigu");System.out.println("所有元素:");for (String s : my) {System.out.println(s);}}
}

4、栈

4.1 栈的特点

  • 栈(Stack)又称为堆栈或堆叠,是限制仅在表的一端进行插入和删除运算的线性表。

  • 栈按照先进后出(FILO,first in last out)的原则存储数据,先进入的数据被压入栈底,最后的数据在栈顶。每次删除(退栈)的总是删除当前栈中最后插入(进栈)的元素,而最先插入的是被放在栈的底部,要到最后才能删除。

  • 核心类库中的栈结构有Stack和LinkedList。

    • Stack就是顺序栈,它是Vector的子类。

    • LinkedList是链式栈。

  • 体现栈结构的操作方法:

    • peek()方法:查看栈顶元素,不弹出

    • pop()方法:弹出栈

    • push(E e)方法:压入栈

  • 时间复杂度:

    • 索引: O(n)

    • 搜索: O(n)

    • 插入: O(1)

    • 移除: O(1)

4.2 Stack使用举例 

public class TestStack {/** 测试Stack* */@Testpublic void test1(){Stack<Integer> list = new Stack<>();list.push(1);list.push(2);list.push(3);System.out.println("list = " + list);System.out.println("list.peek()=" + list.peek());System.out.println("list.peek()=" + list.peek());System.out.println("list.peek()=" + list.peek());/*System.out.println("list.pop() =" + list.pop());System.out.println("list.pop() =" + list.pop());System.out.println("list.pop() =" + list.pop());System.out.println("list.pop() =" + list.pop());//java.util.NoSuchElementException
*/while(!list.empty()){System.out.println("list.pop() =" + list.pop());}}/** 测试LinkedList* */@Testpublic void test2(){LinkedList<Integer> list = new LinkedList<>();list.push(1);list.push(2);list.push(3);System.out.println("list = " + list);System.out.println("list.peek()=" + list.peek());System.out.println("list.peek()=" + list.peek());System.out.println("list.peek()=" + list.peek());/*System.out.println("list.pop() =" + list.pop());System.out.println("list.pop() =" + list.pop());System.out.println("list.pop() =" + list.pop());System.out.println("list.pop() =" + list.pop());//java.util.NoSuchElementException
*/while(!list.isEmpty()){System.out.println("list.pop() =" + list.pop());}}
}

4.3 自定义栈

public class MyStack {// 向栈当中存储元素,我们这里使用一维数组模拟。存到栈中,就表示存储到数组中。// 为什么选择Object类型数组?因为这个栈可以存储java中的任何引用类型的数据private Object[] elements;// 栈帧,永远指向栈顶部元素// 那么这个默认初始值应该是多少。注意:最初的栈是空的,一个元素都没有。//private int index = 0; // 如果index采用0,表示栈帧指向了顶部元素的上方。//private int index = -1; // 如果index采用-1,表示栈帧指向了顶部元素。private int index;/*** 无参数构造方法。默认初始化栈容量10.*/public MyStack() {// 一维数组动态初始化// 默认初始化容量是10.this.elements = new Object[10];// 给index初始化this.index = -1;}/*** 压栈的方法* @param obj 被压入的元素*/public void push(Object obj) throws Exception {if(index >= elements.length - 1){//方式1://System.out.println("压栈失败,栈已满!");//return;//方式2:throw new Exception("压栈失败,栈已满!");}// 程序能够走到这里,说明栈没满// 向栈中加1个元素,栈帧向上移动一个位置。index++;elements[index] = obj;System.out.println("压栈" + obj + "元素成功,栈帧指向" + index);}/*** 弹栈的方法,从数组中往外取元素。每取出一个元素,栈帧向下移动一位。* @return*/public Object pop() throws Exception {if (index < 0) {//方式1://System.out.println("弹栈失败,栈已空!");//return;//方式2:throw new Exception("弹栈失败,栈已空!");}// 程序能够执行到此处说明栈没有空。Object obj = elements[index];System.out.print("弹栈" + obj + "元素成功,");elements[index] = null;// 栈帧向下移动一位。index--;return obj;}// set和get也许用不上,但是你必须写上,这是规矩。你使用IDEA生成就行了。// 封装:第一步:属性私有化,第二步:对外提供set和get方法。public Object[] getElements() {return elements;}public void setElements(Object[] elements) {this.elements = elements;}public int getIndex() {return index;}public void setIndex(int index) {this.index = index;}
}

5、队列

  • 队列(Queue)是只允许在一端进行插入,而在另一端进行删除的运算受限的线性表。

  • 队列是逻辑结构,其物理结构可以是数组,也可以是链表。

  • 队列的修改原则:队列的修改是依先进先出(FIFO)的原则进行的。新来的成员总是加入队尾(即不允许"加塞"),每次离开的成员总是队列头上的(不允许中途离队),即当前"最老的"成员离队。

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

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

相关文章

【Word自动化办公】使用python-docx对Word进行操作

目录 一、环境安装 二、文档各组成结构获取 2.1 组成结构讲解 2.2 段落run对象的切分标准 三、获取整篇文档内容 四、写入指定样式的数据 4.1 通过add_paragraph与add_run参数添加样式 4.2 单独设置文本样式 五、添加标题 六、换行符&换页符 七、添加图片数据 …

Go 版本 Etcd 客户端操作 Etcd(附完整 Demo)

Etcd 相关参考资料 Etcd 的介绍与使用&#xff1a;Etcd 介绍与使用&#xff08;入门篇&#xff09;-CSDN博客 Etcd Raft 协议&#xff1a;Etcd Raft 协议&#xff08;进阶篇&#xff09;-CSDN博客 本文诣在使用 Go 客户端操作 Etcd&#xff0c;并实现元数据的写入&#xff0…

迷宫(一)(DFS BFS)

//新生训练 #include <bits/stdc.h> using namespace std; int n, m; bool f; char mp[15][15]; int vis[15][15]; int dir[4][2] {{-1, 0}, {0, 1}, {1, 0}, {0, -1}}; bool in(int x, int y) {return 0 < x && x < n && 0 < y && y …

2.1-如何配置华三网络设备的远程登录?(ssh,telnet,https)

&#xff08;1&#xff09;华三网络设备远程登录的配置实验 1.实验目标 目标&#xff1a;实现远程ssh登录基本配置(http,https登录&#xff09; 要求&#xff1a;配置后&#xff0c;即可使用远程工具进行远程登录。 配置过程&#xff1a; 1.创建交换机的本地帐号。&#xff0…

NFT交易市场-后端开发

首先我们需要配置好我们的ipfs&#xff0c;参考官方文档 1.https://docs.ipfs.tech/install/command-line/#system-requirementshttps://docs.ipfs.tech/how-to/command-line-quick-start/#initialize-the-repository 首先新建一个文件夹 然后在终端输入npm init -y命令进行初…

深入理解Redis的Sentinel机制

Sentinel简述 Sentinel为了解决什么问题&#xff1f; Sentinel&#xff08;哨岗、哨兵&#xff09;是Redis的高可用性&#xff08;high availability&#xff09;解决方案。 我们知道Redis 的主从复制模式可以将主节点的数据改变同步给从节点&#xff0c;这样从节点就可以起…

docker 和K8S知识分享

docker知识&#xff1a; 比如写了个项目&#xff0c;并且在本地调试没有任务问题&#xff0c;这时候你想在另外一台电脑或者服务器运行&#xff0c;那么你需要在另外一台电脑或者服务器配置相同的软件&#xff0c;比如数据库&#xff0c;web服务器&#xff0c;必要的插件和库等…

吴恩达机器学习笔记 二十七 决策树中连续值特征的选择 回归树

还是猫狗分类的案例&#xff0c;假如再增加一个特征weight&#xff0c;该值是一个连续的值&#xff0c;如何在决策树中使用该特征&#xff1f; 如下图所示&#xff0c;尝试不同的阈值&#xff0c;如 weight<9 , 此时左边有四个样本&#xff0c;都为猫&#xff0c;右边有六个…

分布式搜索引擎ES-RestClient查询文档快速入门

RestClient查询文档快速入门 文章目录 RestClient查询文档快速入门1.1、match_all1.2、全文检索查询1.3、精确查询1.4、复合查询-boolean query1.5、排序和分页1.6、高亮&#xff08;解析查询高亮结果&#xff09; 1.1、match_all package cn.mannor.hotel;import org.apache.…

C#,图论与图算法,计算图(Graph)的岛(Island)数量的算法与源程序

1 孤岛数 给定一个布尔矩阵,求孤岛数。一组相连的1形成一个岛。例如,下面的矩阵包含5个岛: 在讨论问题之前,让我们先了解什么是连接组件。无向图的连通分量是一个子图,其中每两个顶点通过一条路径相互连接,并且不与子图外的其他顶点连接。 所有顶点相互连接的图只有一个…

备战蓝桥杯(前缀和、差分篇)

Acwing 562.壁画 题目大意&#xff1a; 墙壁为一行&#xff0c; n n n个格子&#xff0c;每个时刻开始&#xff0c;先涂染料&#xff0c;再崩坏&#xff0c;涂过染料的格子不会崩坏&#xff0c;并且涂格子只能涂相邻未涂过的。 解题思路&#xff1a; 因为是先涂染料&#xff…

Tomcat 下载以及安装

Tomcat安装及配置教程主要分为四步&#xff1a; 步骤一&#xff1a;首先确认自己是否已经安装JDK 1. cmd&#xff1a;查看java的版本 步骤二&#xff1a;下载安装Tomcat 1. 下载tomcat :Apache Tomcat - Welcome! 2. 选择对应的tomcat版本&#xff1a; 3. 进行安装&#…

Uibot6.0 (RPA财务机器人师资培训第3天 )财务招聘信息抓取机器人案例实战

训练网站&#xff1a;泓江科技 (lessonplan.cn)https://laiye.lessonplan.cn/list/ec0f5080-e1de-11ee-a1d8-3f479df4d981https://laiye.lessonplan.cn/list/ec0f5080-e1de-11ee-a1d8-3f479df4d981https://laiye.lessonplan.cn/list/ec0f5080-e1de-11ee-a1d8-3f479df4d981(本博…

鸿蒙一次开发,多端部署(十四)一多开发实例(短信)

本章从系统预置的应用中&#xff0c;选择短信应用作为典型的案例&#xff0c;从页面开发和工程结构的角度&#xff0c;介绍"一多"的具体实践。系统的产品形态在不断丰富中&#xff0c;当前主要有默认设备和平板两种产品形态&#xff0c;本章的具体实践也将围绕这两种…

ALPHA开发板中CAN硬件图

一. 简介 前面文章学习了 IMX6ULL芯片的 CAN总线协议&#xff0c;CAN传输速率。 本文来搜索 ALPHA开发板中CAN硬件原理图&#xff0c;以及CAN设备节点信息。这里主要是CAN控制器的驱动&#xff0c;属于IMX6ULL芯片内部的驱动&#xff0c;NXP官方已经写好。 CAN控制器的驱动…

使用Python抓取抖音直播间数据的简易指南【第152篇—抓取数据】

&#x1f47d;发现宝藏 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。【点击进入巨牛的人工智能学习网站】。 使用Python抓取抖音直播间数据的简易指南 说明&#xff1a;本文已脱敏&#xff0c;隐去地址…

Keepalive与idle监测及性能优化

Keepalive 与 idle监测 Keepalive&#xff08;保活&#xff09;: Keepalive 是一种机制&#xff0c;通常用于TCP/IP网络。它的目的是确保连接双方都知道对方仍然存在并且连接是活动的。这是通过定期发送控制消息&#xff08;称为keepalive消息&#xff09;实现的。如果在预定时…

鸿蒙Harmony应用开发—ArkTS-if/else:条件渲染

ArkTS提供了渲染控制的能力。条件渲染可根据应用的不同状态&#xff0c;使用if、else和else if渲染对应状态下的UI内容。 说明&#xff1a; 从API version 9开始&#xff0c;该接口支持在ArkTS卡片中使用。 使用规则 支持if、else和else if语句。 if、else if后跟随的条件语句…

算法-双指针

目录 1、双指针遍历分割:避免开空间&#xff0c;原地处理 2、快慢指针&#xff1a;循环条件下的判断 3、左右指针&#xff08;对撞指针&#xff09;&#xff1a;分析具有单调性&#xff0c;避免重复计算 双指针又分为双指针遍历分割&#xff0c;快慢指针和左右指针 1、双指…

【leetcode热题】 位1的个数

编写一个函数&#xff0c;输入是一个无符号整数&#xff08;以二进制串的形式&#xff09;&#xff0c;返回其二进制表达式中数字位数为 1 的个数&#xff08;也被称为汉明重量&#xff09;。 提示&#xff1a; 请注意&#xff0c;在某些语言&#xff08;如 Java&#xff09;中…