表
线性表
链表
什么是链表?
-
链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针连接次序实现的。
-
每一个链表都包含多个节点,节点又包含两个部分,一个是数据域(储存节点含有的信息),一个是引用域(储存下一个节点或者上一个节点的地址)。
-
链表的理解示意图

链表的特点是什么?
-
获取数据麻烦,需要遍历查找,比数组慢
-
方便插入、删除单链表
双向链表 循环链表 双向循环链表
栈
顺序栈
链式栈
队列
Queue: 基本上,一个队列就是一个先入先出(FIFO)的数据结构
Queue接口与List、Set同一级别,都是继承了Collection接口。LinkedList实现了Deque接 口。


普通队列 阻塞队列 并发队列 双端队列
线程(同步)安全的队列
-
BlockingQueue 阻塞算法
-
ConcurrentLinkedQueue,非阻塞算法
在并发编程中,一般推荐使用阻塞队列,这样实现可以尽量地避免程序出现意外的错误。阻塞队列使用最经典的场景就是socket客户端数据的读取和解析,读取数据的线程不断将数据放入队列,然后解析线程不断从队列取数据解析。还有其他类似的场景,只要符合生产者-消费者模型的都可以使用阻塞队列。
使用非阻塞队列,虽然能即时返回结果(消费结果),但必须自行编码解决返回为空的情况处理(以及消费重试等问题)。
另外他们都是线程安全的,不用考虑线程同步问题。
数组
散列表
散列使用一个散列函数,将一个键映射到一个索引上。先回顾一下映射表(map)又称字典(dictionary)、散列表(hash table)或者关联数组(associate array),是一种使用散列实现的数据结构,用来存取条目的容器对象。
Java合集框架定义了java.util.Map接口来对映射表建模,三个具体实现类为java.util.HashMap(散列实现)、java.util.LinkedHashMap(使用LinkedList)、java.util.TreeMap(使用红黑树)。
存储了值的数组称为散列表(hash table),将键映射到散列表中的索引上的函数称为散列函数(hash function)。散列(hashing)是一种无需进行搜素,即可通过从键得到的索引来获取值的技术。
散列函数
冲突解决
链表法
并发寻址
动态扩容
位图
哈希表
树
为什么需要树?
因为它结合了另外两种数据结构的优点: 一种是有序数组,另一种是链表。在树中查找数据项的速度和在有序数组中查找一样快, 并且插入数据项和删除数据项的速度也和链表一样。
在有序数组中插入数据项太慢
在链表中查找太慢
树的遍历
所谓树的遍历(Traversal),就是按照某种次序访问树中的节点,且每个节点恰好访问一次。
也就是说,按照被访问的次序,可以得到由树中所有节点排成的一个序列。
实现树的前序,中序,后序遍历
class TreeNode { int val; TreeNode left; TreeNode right; TreeNode(int x) { val = x; } } public class Solution { public List<Integer> list = new ArrayList<>(); //1.前序遍历-从根节点出发 思路:先根节点->左子树->右子树; public void preorder(TreeNode root){ if(root != null){ list.add(root.val); preorder(root.left); preorder(root.right); } } //前序非递归实现 public List<Integer> preorderTraversal(TreeNode root) { List<Integer> list = new ArrayList<>(); if(root == null) return list; Stack<TreeNode> stack = new Stack<>(); stack.push(root); while(!stack.isEmpty()) { root = stack.pop(); list.add(root.val); if(root.right != null) stack.push(root.right); if(root.left != null) stack.push(root.left); } return list; } //2.1中序遍历,递归实现 思路:先左子树->根节点->右子树; public void inorder1(TreeNode root){ if(root != null){ inorder(root.left); list.add(root.val); inorder(root.right); } } //2.2迭代和栈实现 public void inorder(TreeNode root){ Stack<TreeNode> stack = new Stack<>(); while (root != null || !stack.empty()){ while(root != null){ stack.push(root); root = root.left; } root = stack.pop(); list.add(root.val); root = root.right; } } //3.后序遍历 public void backorder(TreeNode root){ if(root != null){ backorder(root.left); backorder(root.right); list.add(root.val); } } } 广度优先(BFS) 层次遍历 思路:先根节点,然后第二层,第三层,依次往下走,(同层节点从左往右输出); public List<List<Integer>> levelOrder(TreeNode root) { List<List<Integer>> list = new ArrayList<>(); if(root == null) return list; Queue<TreeNode> queue = new LinkedList<>(); queue.add(root); queue.add(null); int num = 0; List<Integer> tempList = new ArrayList<>(); list.add(tempList); while(!queue.isEmpty()) { TreeNode temp = queue.poll(); if(temp != null) { list.get(num).add(temp.val); if(temp.left != null) queue.add(temp.left); if(temp.right != null) queue.add(temp.right); }else { if(!queue.isEmpty()) { num++; tempList = new ArrayList<>(); list.add(tempList); queue.add(null); } } } return list; }
二叉树
二叉树是每个节点最多有两个子节点的树。
二叉树的叶子节点有0个字节点,二叉树的根节点或者内部节点有一个或者两个字节点。
平衡二叉树(AVL Tree)
平衡二叉树的定义
在谈平衡二叉树之前,首先了解一下二叉排序树。空树或者具有以下特性的二叉树就是二叉排序树:
若左子树非空,则左子树上所有结点关键字的值均小于根节点的关键字的值。
若右子树非空,则右子树上所有结点关键字的值均大于根节点的关键字的值。
左右子树本身也分别是一棵二叉排序树。
————————————————
AVL树全称G.M. Adelson-Velsky和E.M. Landis,这是两个人的人名。
AVL树定义:
所有节点的左右子树的高度差小于1的二叉树。
二叉查找树
二叉查找树又叫二叉搜索树,
它或者是一棵空树,或者是具有下列性质的二叉树:
若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
它的左、右子树也分别为二叉搜索树。
平衡二叉查找树
#红黑树
红黑树(Red-Black Tree,简称R-B Tree),它一种特殊的二叉查找树。 红黑树是特殊的二叉查找树,意味着它满足二叉查找树的特征:任意一个节点所包含的键值,大于等于左孩子的键值,小于等于右孩子的键值。 除了具备该特性之外,红黑树还包括许多额外的信息。
红黑树的每个节点上都有存储位表示节点的颜色,颜色是红(Red)或黑(Black)。
(1) 每个节点或者是黑色,或者是红色。
(2) 根节点是黑色。
(3) 每个叶子节点是黑色。 [注意:这里叶子节点,是指为空的叶子节点!] ‘
(4) 如果一个节点是红色的,则它的子节点必须是黑色的。
(5) 从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点。
关于它的特性,需要注意的是:
第一,特性(3)中的叶子节点,是只为空(NIL或null)的节点。
第二,特性(5),确保没有一条路径会比其他路径长出俩倍。因而,红黑树是相对是接近平衡的二叉树。
完全二叉树
满二叉树
多路查找树
B-树
B树
和排序二叉树的搜索很类似,只是换成多叉和多项。
B树定义:
h:代表树的高度,k kk是个自然数,一个B树要么是空的,要么满足以下条件:
1.所有叶子节点到根节点的路径长度相同,即具有相同的高度;
2.每个非叶子和根节点(即内部节点)至少有k + 1 k+1k+1个孩子节点,根至少有2个孩子;
3.每个节点最多有2 k + 1 2k+12k+1个孩子节点。
4.每个节点内的键都是递增的(后文提到)
意思就是:这个B树的每个节点实际上代表了一页(Page),这一页可以看成是一个磁盘块的大小,比如1024,那么就可以存储1024个键,那么这个k就等于512,因为一页最大为2k,最小为k个键,k看来可以是自己定义的,根据你的存储硬件来决定。下图是论文中的一页,可看成是个数组:
B树定义2
但是,大家在网上搜索时会发现定义并不唯一,普遍采用了m阶(代表order,中文翻译为阶)来定义:
每个节点最多有m个孩子。
每个内部节点(除去叶节点和根节点)至少有⌈m/2⌉(向上取整)孩子。
如果根不是叶节点,则根至少有两个孩子。
所有叶子都出现在同一层。
具有k个孩子的非叶节点包含k-1个键,当然节点内的键也是递增的。
如下图(B树的内部节点可以存放数据,类似ZK的中间节点一样。B树不是每个节点都有足够多的子节点)

B+树是从B树衍生而来。
跟B的不同:
1、B+树非叶子节点不存放数据,只存放keys。
2、B+树的叶子节点之间存在指针相连,而且是单链表
如下图(其实B+树上二叉搜索树的扩展,二叉搜索树是每次一分为二,B树是每次一分为多)
现代操作系统中,磁盘的存储结构使用的是B+树机制,mysql的innodb引擎的存储方式也是B+树机制

2-3树
2-3-4树
一、什么是2-3-4树
2-3-4树和红黑树一样,也是平衡树。只不过不是二叉树,它的子节点数目可以达到4个。
每个节点存储的数据项可以达到3个。名字中的2,3,4是指节点可能包含的子节点数目。具体而言:
1、若父节点中存有1个数据项,则必有2个子节点。
2、若父节点中存有2个数据项,则必有3个子节点。
3、若父节点中存有3个数据项,则必有4个子节点。
也就是说子节点的数目是父节点中数据项的数目加一。因为以上三个规则,使得除了叶结点外,其他节点必有2到4个子节点,不可能只有一个子节点。所以不叫1-2-3-4树。而且2-3-4树中所有叶结点总是在同一层。
堆
大/小跟堆
二项堆
优先队列
斐波那契堆
可并堆
本文介绍了数据结构中的重要概念,包括链表的定义、特点和不同类型的链表;队列的类型如普通队列和阻塞队列在并发编程中的应用;栈的两种实现方式;散列表的存储原理和冲突解决方法;以及树的遍历和几种特殊树如二叉查找树、平衡二叉树的概念。

被折叠的 条评论
为什么被折叠?



