本文共 12383 字,大约阅读时间需要 41 分钟。
文章目录
1、概念
1.1 Java
Java一般用来做服务器开发
拿一个淘宝网站举例子: 本质上我们所看到的网页, 是一些数据的集合 我们在我们的电脑上看到这些数据, 都是从淘宝服务器获得的。Java代码 .java - .class – 根据。Class文件产生的对象才是实际有意义的
数据存储在数据库MySql 管控数据(外存)Java本质工作核心: 攒数据
Java要不要临时存储数据// java为了更好更优秀的处理数据(不在完全通过我们手动操作)java提供了一些class-----》 java的集合类
java 最终怎么临时存储数据 = 数组 + 对象1.2 数据结构和JAVA有什么关系?
(数据结构在JAVA上的表现) 数据结构本身和JAVA没有任何关系, 只不过JAVA中有多种集合、数组、等集合性质的多对象存储结构, 有时候我们希望更便捷,更具有逻辑性的操作这些集合或者数组数据 所以,我们根据数据结构的组织方式, 构建了一些特殊的JAVA集合类 用于描述了一些JAVA对象的底层数据的组成关系
Java的集合类底层组织数据的方式, 是参照某些数据结构(逻辑、思想)来进行的。 Java需要临时存储数据, --》 提供集合类 —》 数组 + 链表1.3 数据结构的种类和JAVA中的集合类别有哪些?
数据结构: 抽象概念/逻辑概念
集合: 一堆数据 线性表: 有序的序列(操作受限的线性表: 栈:先进后出 队列: 先进先出) Y = ax + b 树: 一对多的数据关系, 国家, 族谱 图: 多对多的关系: 理论层级 集合类分类: 1, Collection 2, Map- 为什么需要集合类? 很多情况下,我们需要对一组对象进行操作。而且很可能事先并不知道到底有多少个对象。为了解决这个问题呢,Java 就提供了集合类供我们使用。(存储更多类型问题, 扩容问题, 内存空间浪费问题, 数据查找问题, 数据删除问题等等)
- 集合类的特点: a. 只能存储引用数据类型 b. 可以自动地调整自己的大小
- 数组和集合类都是容器,它们有何不同? a. 数组可以存储基本数据类型的数据,集合不可以。 b. 数组的长度是固定的,集合可以自动调整自己的大小。 c. 数组的效率高,相对来说集合效率比较低。 d. 数组没有API,集合有丰富的API。
数据结构是相互之间存在一种或多种特定关系的数据元素的集合。 公式:数据结构=数据+结构
数据结构用来抽象的描述数据的组织关系 数据:用类型表示 结构:在任何问题中,数据元素都不是孤立存在的,它们之间都存在着某种关系,这种数据元素相互之间的关系称为结构。 元素之间,通常有以下四种基本结构: 集合 线性结构 树形结构 图结构 前面分类中定义的关系,描述的是数据元素间的逻辑关系,因此又称为逻辑结构。 但是仅仅知道数据元素间的逻辑关系是不够的,因为我们得实现自己的数据结构。我们需要用具体的java代码, 来模拟或者来表示数据结构
因此,我们得关注数据结构在计算机底层是如何表示的? 数据结构在计算机中的表示,称为数据的物理结构,又称为存储结构或者映像。 结构的表示可以分为两种:顺序存储结构 (顺序映像) 和 链式存储结构 (非顺序映像)。 顺序映像:借助元素在存储器中的相对位置来表示数据元素之间的逻辑关系。(数组) 非顺序映像:借助指示元素存储地址的”指针”,来表示数据元素的逻辑关系。(链表)2 数组
2.1 数组
Q1: 数组我们都很熟悉,那你理解的数组是什么样的呢?它的最主要特点是什么呢?
数组是连续存储 -->随机访问 Q2: 为什么数组的索引是一般都是从0开始的呢? 第一个层面: 历史遗留问题. 硬件资源, 上世纪40-50年代 微型机, 存储数据 第二个层面: 方便计算. (下标-1) * 单个元素空间 + 基础位置 Q3: 为什么数组的效率比链表高? 链表: 链表是非连续存储, 如果查找一个元素需要一步一步遍历 数组: 是连续存储, 可以根据下标一步计算出存储位置2.2 数组的操作
2.2.1 数组的基本操作
(1)添加 (保证元素的顺序)
最好情况:O(1) 最坏情况:移动n个元素,O(n) 平均情况:移动 n/2 个元素,O(n) (2) 删除 (保证元素的顺序) 最好情况:O(1) 最坏情况:移动n-1个元素,O(n) 平均情况:移动(n-1)/2个元素,O(n) (3)查找 a. 根据索引查找元素:O(1) b. 查找数组中与特定值相等的元素 ①大小无序:O(n) ②大小有序:O(log2n) (4)总结 数组: 添加和删除慢 o(n) 查找快: 尤其根据下标查找 -->大小有序的折半查找3 链表
3.1 什么是链表
Java中什么是链表, 一个对象持有另一个对象的引用, 另一个对象持有之后的一个对象的引用,…… 构成一个链表
Java的角度,链表的本质: 是java对象的相互持有和引用
Node zs = new Node("zs", null);Node ls = new Node("ls", null);Node wu = new Node("wu", null);Node zl = new Node("zl", null);zs.next = ls;ls.next = wu;wu.next = zl;
得到一个链表如下:zs -> ls ->wu ->zl
形象地说,链表就是用一串链子将结点串联起来。 结点:包含数据域和指针域。 数据域:数据 指针域:下一个结点的地址3.2 链表的分类
(1)单链表: 在链表中的每一个节点, 都有一个子结点, (尾结点没有子结点)
(2)循环单链表: 单链表的改进, 让单链表的尾结点的下一个结点, 指向头结点
(3)双向链表: 双向链表也是单链表的改进, 让结点不仅可以指向之后的元素, 也可以指向之前的元素
(4)双向循环链表: 双向链表的一个改进, 头元素的之前指向尾元素, 尾元素的之后指向头元素
3.3 单链表操作
单链表:
(1)单链表操作: 查找o(n), 添加和删除o(1)3.4 双向链表的操作
双向链表操作:查找o(n), 添加和删除o(1)
(1)很容易验证,前面那些操作,双向链表和单链表的时间复杂度是一样的。那为什么在工程上,我们用的一般是双向链表而不是单链表呢?
因为在实际操作过程中, 双向链表根据下标查找的效率, 是高于单链表.--> 隐性的结论, 在实际工作中, 根据下标查找这个操作相对添加和删除比较频繁
思想: 用空间换时间
(2)求链表的中间元素 判断链表中是否有环(circle)
public class Ex1 { public static void main(String[] args) { Node1 node1 = new Node1("1", null); Node1 node2 = new Node1("2", node1); Node1 node3 = new Node1("3", node2); Node1 node4 = new Node1("4", node3); Node1 node5 = new Node1("5", node4); Node1 node6 = new Node1("6", node5); Node1 node7 = new Node1("7", node6); // 7 -- 6 --5 --4 --3 --2 --1 // 快慢指针: Node1 f = node7;// 快指针 Node1 l = node7;// 慢指针 // 判断快指针的下一个元素, 以及下下个元素, 是不是null, 不是null向后继续移动 while (f.next != null && f.next.next != null){ f = f.next.next;// 移动两步 l = l.next; // 移动一步 } // f接近于于尾部的时候, 跳出上述循环 System.out.println(l.value); }}class Node1{ String value; Node1 next; public Node1(String value, Node1 next) { this.value = value; this.next = next; }}
(3)反转单链表
public class Ex2 { public static void main(String[] args) { Node2 node1 = new Node2("1", null); Node2 node2 = new Node2("2", node1); Node2 node3 = new Node2("3", node2); Node2 node4 = new Node2("4", node3); Node2 node5 = new Node2("5", node4); Node2 node6 = new Node2("6", node5); Node2 node7 = new Node2("7", node6); // 7 -- 6 --5 --4 --3 --2 --1 node1.next = node5; Node2 f = node7; Node2 l = node7; while (f.next != null && f.next.next != null){ f = f.next.next; l = l.next; if (f == l){ // 快指针和慢指针相遇, 必定意味着有环 break; } } if (f.next != null && f.next.next != null){ System.out.println("有环"); } else { System.out.println("没有环"); } }}class Node2{ String value; Node2 next; public Node2(String value, Node2 next) { this.value = value; this.next = next; }}
3.5 使用链表/数组实现一个线性表
自己手动实现一个集合类 这个集合类: 从集合类使用的角度: 就是一个数据容器(添加, 删除, 查找) 底层结构: 链表 数据结构角度: 模拟一个线性表
线性表: 有序序列: 一个序列除了头尾元素以外, 每一个元素都有唯一的前驱和后继
---->线性表一般伴随着有序, 伴随着可以通过下标操作public class MyLinked { private Node head; // 这个集合类底层所维护链表的头结点 private int size; // 用来标记这个链表/集合类存储了多少数据 public boolean add(String str){ // if (str == null) throw new IllegalArgumentException("parame is null"); // 原本的链表是否为空// if (head == null || size == 0){} if (size == 0){ // 意味着 链表为空, 新添加的元素, 作为头结点 head = new Node(str, null); size++; return true; } // 链表原本不空 // 添加到尾部: 先找到尾部 Node mid = head; // 用mid标记遍历结点, 最终找到尾结点 while (mid.next != null){ mid = mid.next; } // mid.next == null : mid是原链表的尾结点 mid.next = new Node(str, null); size++; return true; } public boolean remove(String str){ if (size == 0) throw new RuntimeException("linked is empty"); if (str == null){ // 如皋要删除的元素是null, ==比较 if (str == head.value){ // 判断头结点是不是我们要删除的元素 head = head.next; size--; return true; } // 删除不是头结点 Node mid = head; // mid的下一个元素不是null // mid的下一个元素 存储的值也不是要找的 while (mid.next != null && str != mid.next.value ){ mid = mid.next;// mid向后遍历 } // 两种结果 // 1, 没找到, mid.next == null // 2, 找到了, if (mid.next == null){ // 没找到 return false; } // 找到了, 要删除的是mid的next mid.next = mid.next.next;// 删除mid的next size--; return true; }else { // 如果要删除的元素不是null, equals比较 if (str.equals(head.value)){ // 判断头结点是不是我们要删除的元素 head = head.next; size--; return true; } // 删除不是头结点 Node mid = head; // mid的下一个元素不是null // mid的下一个元素 存储的值也不是要找的 while (mid.next != null && !str.equals(mid.next.value) ){ mid = mid.next;// mid向后遍历 } // 两种结果 // 1, 没找到, mid.next == null // 2, 找到了, if (mid.next == null){ // 没找到 return false; } // 找到了, 要删除的是mid的next mid.next = mid.next.next;// 删除mid的next size--; return true; } } public boolean contains(String str){ if (isEmpty()) throw new RuntimeException("linked is empty"); if (str == null){ // 如皋要查找的元素是null, ==比较 if (str == head.value){ // 头结点就是要查找的结点 return true; } // 删除不是头结点 Node mid = head; // mid的下一个元素不是null // mid的下一个元素 存储的值也不是要找的 while (mid.next != null ){ mid = mid.next;// mid向后遍历 if (mid.value == str){ // 判断遍历过程中的元素, 是否要查找的元素 return true; } } } else { // 如果要查找的元素不是null, equals比较 if (str.equals(head.value)){ // 头结点就是要查找的结点 return true; } // 删除不是头结点 Node mid = head; // mid的下一个元素不是null // mid的下一个元素 存储的值也不是要找的 while (mid.next != null ){ mid = mid.next;// mid向后遍历 if (str.equals(mid.value)){ // 判断遍历过程中的元素, 是否要查找的元素 return true; } } } return false; } public boolean set(String oldValue, String newValue){ if (isEmpty()) throw new RuntimeException("linked is empty"); // if (oldValue == null){ // 要替换的值是null Node mid = head; // 如果遍历的结点不是null, 并且这个结点存储的值也不是null // 接着向后遍历 while (mid != null && oldValue != mid.value){ mid = mid.next; } // 1, mid == null: 没有 // 2, oldValue == mid.value: 找到了mid就是要改变的结点 if (mid == null){ // 就没有这个旧值 return false; } // 有 mid.value = newValue; return true; }else { // 要替换的值不是null Node mid = head; // 如果遍历的结点不是null, 并且这个结点存储的值也不是要替换的结点 // 接着向后遍历 while (mid != null && !oldValue.equals(mid.value)){ mid = mid.next; } // 1, mid == null: 没有 // 2, oldValue == mid.value: 找到了mid就是要改变的结点 if (mid == null){ // 就没有这个旧值 return false; } // 有 mid.value = newValue; return true; } } // 增删改查 // 增删查 // 改: /** * 实现一个添加方法 * @param index : 要添加的位置 * @param str : 要添加的内容 * @return : 是否添加成功 */ public boolean add(int index, String str){ // 判断给的下标是否合法 if (index < 0 || index > size) throw new IllegalArgumentException("size = " + size ); // zs - ls - wu - zl // 头结点: index = 0 if (index == 0){ // 添加的位置就是头结点 head = new Node(str, head); size++; return true; } // 添加的不是头位置 int tag = 1; // 位置标记 Node mid = head; while (tag != index){ mid = mid.next; tag++; } // zs ls wu zl // mid代表要添加位置的之前 mid.next = new Node(str, mid.next); size++; return true; } public String remove(int index){ // 判断给的下标是否合法 if (index < 0 || index >= size) throw new IllegalArgumentException("size = " + size ); // 删除是头元素 if (index == 0){ String oldValue = head.value; head = head.next; size--; return oldValue; } // 删除的不是头元素 int tag = 1; // 位置标记 Node mid = head; while (tag != index){ mid = mid.next; tag++; } // mid 删除元素前一个 String oldValue = mid.next.value; // 删除 mid.next = mid.next.next; size--; return oldValue; } /** * 查找方法: 根据下标查找这个下标所对应存储的内容 * @param index : 提供的下标 * @return : 返回的内容 */ public String get(int index){ // 判断给的下标是否合法 if (index < 0 || index >= size) throw new IllegalArgumentException("size = " + size ); int tag = 0; Node mid = head; while (tag != index){ mid = mid.next; tag++; } //mid 就是要查找的位置 return mid.value; }// myLinked.add(i, str);// myLinked.remove(i);// myLinked.get(i);// 根据下标获取这个下标的元素内容 /** * 根据下标替换新内容 * @param index * @param newValue * @return */ public String set(int index, String newValue){ // 判断给的下标是否合法 if (index < 0 || index >= size) throw new IllegalArgumentException("size = " + size ); int tag = 0; Node mid = head; while (tag != index){ mid = mid.next; tag++; } // mid 就是你给定的查找位置 String value = mid.value; mid.value = newValue; return value; } // 判断这个集合类存储的数据是否为空 public boolean isEmpty(){ return size == 0; } public int size(){ return size; } class Node{ String value; Node next; public Node(String value, Node next) { this.value = value; this.next = next; } }}
转载地址:https://blog.csdn.net/weixin_43510391/article/details/116211295 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!