赞
踩
[TOC]
数组(Array) 是一种很常见的数据结构。它由相同类型的元素(element)组成,并且是使用一块连续的内存来存储。
我们直接可以利用元素的索引(index)可以计算出该元素对应的存储地址。
数组的特点是:提供随机访问 并且容量有限。
链表(LinkedList) 虽然是一种线性表,但是并不会按线性的顺序存储数据,使用的不是连续的内存空间来存储数据。
链表的插入和删除操作的复杂度为 O(1) ,只需要知道目标位置元素的上一个元素即可。但是,在查找一个节点或者访问特定位置的节点的时候复杂度为 O(n) 。
常见链表分类:
循环链表
双向循环链表
构造链表节点
- package com.suanfa.DataStructure;
-
- import lombok.Data;
-
- /**
- * @Author CaiNiao
- * @Description TODO
- * @Date 2023/1/14 21:24
- * @Version 1.0
- */
- @Data
- public class LinkedNode {
- //数据存放
- public Object data;
- //下一个元素
- public LinkedNode next;
- //上一个元素
- public LinkedNode prev;
- public LinkedNode(Object data) {
- this.data = data;
- }
- }
构造链表测试
- package com.suanfa.DataStructure;
-
- /**
- * @Author CaiNiao
- * @Description TODO
- * @Date 2023/1/14 21:30
- * @Version 1.0
- */
- public class LinkedNodeTest {
- public static void main(String[] args) {
- //构建一个三个节点的双向链表
- LinkedNode linkedNode1 = new LinkedNode("linkedNode1");
- LinkedNode linkedNode2 = new LinkedNode("linkedNode2");
- LinkedNode linkedNode3 = new LinkedNode("linkedNode3");
- linkedNode1.next = linkedNode2;
- linkedNode2.prev = linkedNode1;
- linkedNode2.next = linkedNode3;
- linkedNode3.prev = linkedNode2;
- //测试 拿到第一个节点的下一个节点测试链表
- System.out.println(linkedNode1.getNext().data);
- System.out.println(linkedNode1.getNext().getNext().data);
- }
- }
栈 (stack)只允许在有序的线性数据集合的一端(称为栈顶 top)进行加入数据(push)和移除数据(pop)。因而按照 后进先出(LIFO, Last In First Out) 的原理运作。在栈中,push 和 pop 的操作都发生在栈顶。
栈常用一维数组或链表来实现,用数组实现的栈叫作 顺序栈 ,用链表实现的栈叫作 链式栈 。
当我们我们要处理的数据只涉及在一端插入和删除数据,并且满足 后进先出(LIFO, Last In First Out) 的特性时,我们就可以使用栈这个数据结构。
JVM中就使用了栈数据结构, 虚拟机栈。
浏览器也是一个很好的例子,浏览页面是点击返回上一个页面就是弹栈,点击下一个页面就是压栈,符合LIFO特性。
构建栈
- package com.suanfa.DataStructure;
-
- import java.util.Arrays;
-
- /**
- * @Author CaiNiao
- * @Description TODO
- * @Date 2023/1/14 21:57
- * @Version 1.0
- */
- public class Stack {
- private int[] storage;//存放栈中元素的数组
- private int capacity;//栈的容量
- private int count;//栈中元素数量
- private static final int GROW_FACTOR = 2;
-
- //不带初始容量的构造方法。默认容量为8
- public Stack() {
- this.capacity = 8;
- this.storage=new int[8];
- this.count = 0;
- }
-
- //带初始容量的构造方法
- public Stack(int initialCapacity) {
- if (initialCapacity < 1)
- throw new IllegalArgumentException("Capacity too small.");
-
- this.capacity = initialCapacity;
- this.storage = new int[initialCapacity];
- this.count = 0;
- }
-
- //入栈
- public void push(int value) {
- if (count == capacity) {
- ensureCapacity();
- }
- storage[count++] = value;
- }
-
- //确保容量大小
- private void ensureCapacity() {
- int newCapacity = capacity * GROW_FACTOR;
- storage = Arrays.copyOf(storage, newCapacity);
- capacity = newCapacity;
- }
-
- //返回栈顶元素并出栈
- private int pop() {
- if (count == 0)
- throw new IllegalArgumentException("Stack is empty.");
- count--;
- return storage[count];
- }
-
- //返回栈顶元素不出栈
- private int peek() {
- if (count == 0){
- throw new IllegalArgumentException("Stack is empty.");
- }else {
- return storage[count-1];
- }
- }
-
- //判断栈是否为空
- private boolean isEmpty() {
- return count == 0;
- }
-
- //返回栈中元素的个数
- private int size() {
- return count;
- }
- }
单队列就是常见的队列, 每次添加元素时,都是添加到队尾。单队列又分为 顺序队列(数组实现) 和 链式队列(链表实现)。
front可以标记出队列的头在哪里;
rear指向队列最后的一个元素的下一个位置;
循环队列可以解决顺序队列的假溢出和越界问题。解决办法就是:从头开始,这样也就会形成头尾相接的循环,这也就是循环队列名字的由来。
当我们需要按照一定顺序来处理数据的时候可以考虑使用队列这个数据结构。
阻塞队列: 阻塞队列可以看成在队列基础上加了阻塞操作的队列。当队列为空的时候,出队操作阻塞,当队列满的时候,入队操作阻塞。使用阻塞队列我们可以很容易实现“生产者 - 消费者“模型。
线程池中的请求/任务队列:
比如 :FixedThreadPool 使用无界队列 LinkedBlockingQueue。但是有界队列就不一样了,当队列满的话后面再有任务/请求就会拒绝,在 Java 中的体现就是会抛出java.util.concurrent.RejectedExecutionException 异常。
Linux 内核进程队列(按优先级排队)
现实生活中的派对,播放器上的播放列表;
消息队列
其他数据结构会在后续博客中写,希望大家多多支持,你的鼓励就是我创作的动力。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。