线性表,全名为线性存储结构。使用线性表存储数据的方式可以理解理解为:把所有数据按照顺序(线性)的存储结构方式,存储在物理空间。
顺序存储结构:数据依次存储在连续的物理空间中(顺序表);
链式存储结构:数据分散存储在不同的物理空间中,通过某种指向关系,来维持它们之间的逻辑关系(链表);
顺序表,就是顺序存储结构,是线性表的一种。顺序表对数据的物理存储结构有明确的要求:顺序表存储数据时,会提前申请一块足够大小的内存,然后将数据依次存储起来,元素的存储空间在内存中是连续存在的。
优点:1.内存地址连续,数组元素进行遍历时,速度快。
2.根据下标,查找指定位置的元素时,速度快。
缺点:1.长度固定,使用前余姚提前预估长度。
2.插入和删除元素时,时间复杂度相对较高。
时间复杂度:读的时候是 O(1) 插入和删除的时候是O(n)
链表,全名是链式存储结构,也是线性表的一种。链表不限制数据的物理存储位置,使用链表存储的数据元素,其物理存储位置是随机的。由于链表节点之间根本无法体现出各数据之间的逻辑关系。
链表的组成:
数据元素本身,其所在的区域称为数据域;
指向直接后继元素的指针,所在的区域称为指针域;
优点:使用链表结构,不需要提前预估长度,可以克服数组需要预先知道数据长度的缺点
链表使用不连续的内存空间,可以充分利用计算机内存空间,实现灵活的内存动态管理
缺点:链表相比于数组会占用更多的空间,因为他不仅要存储数据本身还需要存储指针域。
不能随机的读取元素
遍历和查找元素更慢了。
时间复杂度:插入和删除时O(1),遍历查找时O (n);
链表又有很多种类:单向链表,双向链表,循环链表,双向循环链表。。。
- Linked.Node node1 = new Linked.Node(1);
-
- Linked.Node node2 = new Linked.Node(2);
-
- Linked.Node node3 = new Linked.Node(3);
-
- Linked.Node node4 = new Linked.Node(4);
-
- Linked.Node node5 = new Linked.Node(5);
-
-
-
- node1.next = node2;
-
- node2.next = node3;
-
- node3.next = node4;
-
- node4.next = node5;
-
- node5.next = node3; // 链表产生环
- private static boolean hasCycle(Node node) {
-
- // 定义Set集合,保存链表中的所有节点
-
- Set
nodeSet = new HashSet(); -
- // 遍历链表中的每个节点
-
- while(node != null) {
-
- // 判断Set中是否存在该节点
-
- // 如果存在,则代表该链表有环
-
- if(nodeSet.contains(node)) {
-
- return true; // 链表有环
-
- }
-
- // 如果不存在,则将节点加入Set集合,用于后续的判断
-
- nodeSet.add(node);
-
- // 移动链表节点
-
- node = node.next;
-
- }
-
- return false; // 链表无环
-
- }
- private static boolean hasCycle(Node head) {
-
- if (head == null) {
-
- return false;
-
- }
-
-
-
- // 定义快指针和慢指针,从head头节点开始
-
- Node fast = head;
-
- Node slow = head;
-
- while (fast != null && fast.next != null && slow != null) {
-
- fast = fast.next.next; // 快指针移动2步
-
- slow = slow.next; // 慢指针移动1步
-
-
-
- // 判断fast和slow快慢指针指向的内存地址相同,如果相同,则代表碰面相遇
-
- if (fast == slow) {
-
- // 如果碰面,就代表链表有环
-
- return true;
-
- }
-
- }
-
- return false;
-
- }
- public static boolean isIntersect1(Linked link1, Linked link2) {
-
- for (Node p = link1.first; p != null; p = p.next) {
-
- for (Node q = link2.first; q != null; q = q.next) {
-
- if (p == q) {
-
- return true;
-
- }
-
- }
-
- }
-
- return false;
-
- }
- public static boolean isIntersect2(Linked link1, Linked link2) {
- // 安全检测
- if (link1 == null || link2 == null) {
- return false;
- }
-
- // p 指向长链表的第一个结点
- // q 指向短链表的第一个结点
- Node p = link1.size() > link2.size() ? link1.first : link2.first;
- Node q = link1.size() > link2.size() ? link2.first : link1.first;
-
- // 求两个链表长度差
- int diff = Math.abs(link1.size() - link2.size());
-
- // p先往后移动diff个结点
- while (diff-- > 0) {
- p = p.next;
- }
-
- // p 和 q 同时往后移动
- while (p != q) {
- p = p.next;
- q = q.next;
- }
-
- // 如果p(q)不为null,则两个链表相交,否则不相交
- if (p != null) {
- return true;
- } else {
- return false;
- }
- }
栈(stack)是一种特殊的线性数据集合,只允许在栈顶top进行加入数据(push)贺移动数据(pop),按照后进先出LIFO的规则进行操作,也可以理解为先入后出FILO;
栈的实现方式:
栈的实现结构可以是一维数组或链表来实现,用数组实现的栈叫作顺序栈 ,用链表实现的栈叫作链式栈 。在Java中,顺序栈使用java.util.Stack类实现,链式栈使用java.util.LinkedList类实现。
时间复杂度:访问指定元素:O(n) 入栈和出栈:O(1)
入栈:入栈操作就是把虚拟的元素放入栈中,只允许从栈顶一侧放入元素,新元素将会成为栈顶。
出栈:出栈操作就是把元素从栈中弹出,只有栈顶元素才允许出栈,出栈元素的前一个元素将会成为新的栈顶。
队列是一种线性数据节后,特点类似:行驶车辆的单向隧道
队列中的元素按照先入先出的规则操作
队列的出口端叫做队头,队列的入口端叫做队尾
队列只允许在队头进行出队poll操作(删除)
队列只允许在队尾进行入队offer操作(添加)
数组实现的队列叫作顺序队列
用链表实现的队列叫作链式队列
假设队列中有n个元素。
●访问指定元素的时间复杂度是O(n):最坏情况下,遍历整个队列
●插入删除元素的时间复杂度是O(1):只需要操作队头或队尾元素