数据结构(一):数据结构的分类
本文主要借鉴LeetCode官方
1.1 主要的数据结构类型
线性结构:有序数据元素的集合,数据元素之间的关系是一对一的关系
线性数据结构 :线性表,栈,队列,双队列,数组,串 等
非线性数据结构:各个数据元素不在保持在一个线性序列中,每个数据袁术可能与0个或多个其他数据元素发生联系
非线性数据结构:二维数组,多维数组,广义表,树(二叉树等),图等
1.2 数据类型的具体解释介绍
1.2.1 数组
数组内部的数据长度是不可变的,一旦创建就不能再进行更改长度,数组将数据存储在连续的内存空间,数组的起步是由0开始,故数据的长度是创建时的长度,但是对于索引而言,是长度减一 。
// 初始化一个数组 arr:长度为2
int[] arr = new int[2];
// 元素赋值
arr[0] = 1;
arr[1] = 2;
1.2.2 集合
可以将集合看成一个可变数组,相比较而言,可以动态的对数组进行扩容,不会有长度限制,常用的是ArrayList。
// 初始化一个arraylist集合
List<Integer> arr= new ArrayList<>();
// 向尾部添加元素
arr.add(1);
arr.add(2);
//删除元素[删除指定位置的元素,i 为索引位置]
arr.remove(1);
//用元素替代指定位置的元素[i 为索引位置,e为元素]
arr.set(i,e);
1.2.3 链表
链表是一个个节点数据连接起来得,数据结构区分为头结点,子节点,尾节点的next为null。
//定义一个链表节点
class ListNode {
int val; // 头结点
ListNode next; // 下一个节点
ListNode(int x) {
val = x;
}
}
//节点的使用,以及值的使用
ListNode head= new ListNode (1);
ListNode next= new ListNode (2);
head.next = next;
1.2.4 栈
栈是一种先入后出的数据结构,有压栈和出栈的说法,在代码中实现常用Stack数组实现
//创建一个栈
Stack<Integer> stack = new Stack<>();
//栈的相关操作
//将元素1,2压入栈【push方法】
stack.push(1);
stack.push(2);
//元素出栈 【pop方法】
stack.pop(); //元素1出栈
stack.pop(); //元素2出栈
通常情况下,不推荐使用 Java 的 Vector 以及其子类 Stack ,而一般将 LinkedList 作为栈来使用
因为Vector是线程安全的,每个可能出现线程安全的方法上加了synchronized关键字,所以效率低。
-
Vector只能在尾部进行插入和删除操作,效率更低。
-
Vector空间满了之后,扩容是一倍,ArrayList仅仅是一半。
-
Vector分配内存的时候需要连续的存储空间,如果数据太多,容易分配内存失败。
LinkedList<Integer> stack = new LinkedList<>();
stack.addLast(1); // 元素 1 入栈
stack.addLast(2); // 元素 2 入栈
stack.removeLast(); // 出栈 -> 元素 2
stack.removeLast(); // 出栈 -> 元素 1
1.2.5 队列
队列是一种先入先出的数据结构,可以使用链表来进行实现需求操作
Queue<Integer> queue = new LinkedList<>();
//offer方法,将数据写入队列
queue.offer(1); // 元素 1 入队
queue.offer(2); // 元素 2 入队
queue.poll(); // 出队 -> 元素 1
queue.poll(); // 出队 -> 元素 2
1.2.6 树
树是一种非线性数据结构,节点区分为根节点和子节点,又分为左子节点和右子节点。存储的是具有“一对多”关系的数据元素的集合
结点:使用树结构存储的每一个数据元素都被称为“结点
父结点(双亲结点)、子结点和兄弟结点:对于图中的结点 A、B、C、D 来说,A 是 B、C、D 结点的父结点(也称为“双亲结点”),而 B、C、D 都是 A 结点的子结点(也称“孩子结点”)。对于 B、C、D 来说,它们都有相同的父结点,所以它们互为兄弟结点。
树根结点(简称“根结点”):每一个非空树都有且只有一个被称为根的结点
叶子结点:如果结点没有任何子结点,那么此结点称为叶子结点(叶结点)。例如图 中,结点 K、L、F、G、M、I、J 都是这棵树的叶子结点。
更详细请参考:树存储结构详解
//树节点的创建
class TreeNode {
int val; // 节点值
TreeNode left; // 左子节点
TreeNode right; // 右子节点
TreeNode(int x) { val = x; }
}
//树节点的使用和搭建树
// 初始化节点
TreeNode n1 = new TreeNode(3); // 根节点 root
TreeNode n2 = new TreeNode(4);
TreeNode n3 = new TreeNode(5);
TreeNode n4 = new TreeNode(1);
TreeNode n5 = new TreeNode(2);
// 构建引用指向
n1.left = n2;
n1.right = n3;
n2.left = n4;
n2.right = n5;
1.2.7 图
图是一种非线性数据结构,图可分为 有向图 和 无向图。
表示图的方法通常有两种:
邻接矩阵: 使用数组 verticesvertices 存储顶点,邻接矩阵 edgesedges 存储边; edges[i][j]edges[i][j] 代表节点 i+1 和 节点 j+1 之间是否有边。
int[] vertices = {1, 2, 3, 4, 5};
int[][] edges = {{0, 1, 1, 1, 1},
{1, 0, 0, 1, 0},
{1, 0, 0, 0, 1},
{1, 1, 0, 0, 1},
{1, 0, 1, 1, 0}};
邻接表: 使用数组 verticesvertices 存储顶点,邻接表 edgesedges 存储边。 edgesedges 为一个二维容器,第一维 ii 代表顶点索引,第二维 edges[i]存储此顶点对应的边集和;例如 edges[0] = [1, 2, 3, 4] 代表 vertices[0]的边集合为 [1, 2, 3, 4]。
int[] vertices = {1, 2, 3, 4, 5};
List<List<Integer>> edges = new ArrayList<>();
List<Integer> edge_1 = new ArrayList<>(Arrays.asList(1, 2, 3, 4));
List<Integer> edge_2 = new ArrayList<>(Arrays.asList(0, 3));
List<Integer> edge_3 = new ArrayList<>(Arrays.asList(0, 4));
List<Integer> edge_4 = new ArrayList<>(Arrays.asList(0, 1, 4));
List<Integer> edge_5 = new ArrayList<>(Arrays.asList(0, 2, 3));
edges.add(edge_1);
edges.add(edge_2);
edges.add(edge_3);
edges.add(edge_4);
edges.add(edge_5);
1.2.8 堆
堆是一种基于「完全二叉树」的数据结构,可使用数组实现。以堆为原理的排序算法称为「堆排序」,基于堆实现的数据结构为「优先队列」。堆分为「大顶堆」和「小顶堆」,大(小)顶堆:任意节点的值不大于(小于)其父节点的值。
-
大顶堆就是根节点最大
-
小顶堆就是根节点最小
// 初始化小顶堆
Queue<Integer> heap = new PriorityQueue<>();
// 元素入堆
heap.add(1);
heap.add(4);
heap.add(2);
heap.add(6);
heap.add(8);
// 元素出堆(从小到大)
heap.poll(); // -> 1
heap.poll(); // -> 2
heap.poll(); // -> 4
heap.poll(); // -> 6
heap.poll(); // -> 8