
数据结构 [Java]
乌鲁木齐001号程序员
这个作者很懒,什么都没留下…
展开
专栏收录文章
- 默认排序
- 最新发布
- 最早发布
- 最多阅读
- 最少阅读
-
堆 01 基础
堆 - Heap堆也是一种树结构,最典型的实现就是基于二叉树实现的堆,称为二叉堆; 二叉堆是一颗完全二叉树; 完全二叉树不一定是满二叉树,不满的部分一定是在整个树的右下侧,即把元素一层一层的码在二叉树上; 堆中某个节点的值总是不大于其父节点的值,这样的堆称为最大堆(相应的也可以定义出最小堆); 在堆中,上层节点的值不一定大于下层节点的值; 由于二叉堆的节点是一层一层码在二叉树上的,一...原创 2019-02-12 11:24:00 · 95 阅读 · 0 评论 -
线段树 06 Leetcode中的307号问题
307. 区域和检索 - 数组可修改线段树解法public class NumArray { private interface Merger<E> { E merge(E a, E b); } private class SegmentTree<E> { private E[] tree; ...原创 2019-02-14 14:45:00 · 112 阅读 · 0 评论 -
字典树 01 字典树基础
Trie - 字典树字典树这种数据结构最典型的用例就是存单词; 相较于普通的树结构O(logn)的时间复杂度,其时间复杂度为O(w),w为单词的长度;基础代码Trie中的节点Node是不存储字符的,字符作为“边”,存储在父节点的映射next的key中; Trie作为树结构,一种高级的数据结构,和链表,二叉树一样,本身只是个壳,里面是要封装一个头指针的,作为拎起整棵树的拎手; 在向T...原创 2019-02-14 19:26:00 · 151 阅读 · 0 评论 -
字典树 02 在字典树中查询
查询单词word是否在Trie中如果Trie中有word,那么word的每个字母,依次都能在Trie的每一行中找到(从Trie的第二行开始算起);// 查询单词word是否在Trie中public boolean contains(String word){ Node cur = root; for(int i = 0 ; i < word.length() ; i...原创 2019-02-15 00:30:00 · 96 阅读 · 0 评论 -
字典树 04 字典树中的前缀问题
查询是否在Trie中有单词以prefix为前缀prefix在Trie中都找到的情况下,不用再判断prefix.charAt(prefix.length - 1)所对应的节点是否是单词,直接返回true即可;// 查询是否在Trie中有单词以prefix为前缀public boolean isPrefix(String prefix){ Node cur = root; ...原创 2019-02-15 00:41:00 · 144 阅读 · 0 评论 -
字典树 04 Leetcode中的211号问题
211. 添加与搜索单词 - 数据结构设计查看Trie中是否存在单词word,从操作过程看,就是依次拿出word的每个字母,去Trie中与其对应的层中查找,而这一层的节点所表示的字母并不存储在该层的节点中,而是存储在该层父节点的边(父节点映射的key,父节点映射的value就是该层的节点)中,这层的节点中只存储指向其的边是否是一个单词的终点; 从递归的角度看就是:在Trie的节点node中,...原创 2019-02-15 13:00:00 · 127 阅读 · 0 评论 -
字典树 05 Leetcode中的677号问题
677. 键值映射class MapSum { private class Node { public int value; public TreeMap<Character, Node> next; public Node(int value) { this.value = value; ...原创 2019-02-16 11:54:00 · 201 阅读 · 0 评论 -
并查集 01 并查集基础
并查集并查集指的是在一个集合中的两种操作:并和查; 并:将集合S中的两个子集S1和S2合并;S1和S2是没有交集的; 查:查看集合S中的元素a是否存在于S的某个子集中; 并查集的初始状态:每个元素构成一个集合,所有集合之间是不相交的;并查集涉及2个操作void unionElements(int p, int q) - 并 将元素p和元素q所在的集合合并成一个集合; p和q指...原创 2019-02-17 13:28:00 · 158 阅读 · 0 评论 -
并查集 02 快查
并查集的初始化在初始化的时候指定并查集中一共有多少元素,就将盛放集合中元素的数组开辟成多大,然后将数组的每一格的值赋成其索引号,即初始化的时候,每个元素作为一个集合,和其他任何集合是没有交集的; 用数组表示集合中的所有元素,数组的索引代表集合中的每个元素,数组中的值表示该索引位置的元素所在的集合的编号; 在初始化的时候,数组中每个元素的值和其索引号一致,表示每个元素处在只有它构成的集合中;...原创 2019-02-17 13:59:00 · 118 阅读 · 0 评论 -
线段树 05 在线段树中更新单个元素
在线段树中更新单个元素在线段树中更新一个元素的过程分2步: 在data中更新; 由于data的更新导致在tree上也要更新; // 将index位置的值,更新为epublic void set(int index, E e){ if(index < 0 || index >= data.length) throw new IllegalArg...原创 2019-02-14 13:08:00 · 109 阅读 · 0 评论 -
线段树 04 Leetcode中的303号问题
303. 区域和检索 - 数组不可变线段树解法class NumArray { private interface Merger<E> { E merge(E a, E b); } private class SegmentTree<E> { private E[] tree; p...原创 2019-02-14 00:12:00 · 309 阅读 · 0 评论 -
线段树 03 在线段树中查询
查询区间 [queryL, queryR] 上被赋予的意义将问题转化为一个递归问题:在以treeIndex为根的对应区间为 [l, r] 的线段树中,查询区间 [queryL, queryR]被赋予的意义;// 返回区间[queryL, queryR]的值public E query(int queryL, int queryR){ if(queryL < 0 || qu...原创 2019-02-13 23:56:00 · 100 阅读 · 0 评论 -
堆 02 添加 & 上浮
void add(E e) - 向最大堆中添加元素从二叉树的角度看,向最大堆中添加元素就是接着在完全二叉树的下一个位置码上元素; 从数据的角度看,就是在数组的最后添加一个元素; 从堆的角度看,新添加的元素不能比它的父元素大,如果比它的父元素大,就涉及到上浮的动作;// 向堆中添加元素public void add(E e){ data.addLast(e); sift...原创 2019-02-12 12:07:00 · 125 阅读 · 0 评论 -
堆 03 提取 & 下沉
E extractMax() - 提取堆中最大的元素堆中最大的元素: 堆中最大的元素就是堆顶的元素; 从二叉树的角度看,堆中最大的元素就是二叉树的根节点; 从数组的角度看,堆中最大的元素就是数组中索引为0的元素; 提取堆中最大的元素: 找到堆中最大的元素并返回很简单,但提取涉及到要把最大的元素从堆中删除(即逻辑上删除二叉树的根,物理上删除数组索引为0的元素),就要重组剩余元素...原创 2019-02-12 13:33:00 · 147 阅读 · 0 评论 -
堆 04 替换堆顶元素 & 堆化
Replace - 取出堆中最大的元素,并替换成新元素 O(logn)找出堆顶元素(即取出数组中索引为0的元素); 用新元素替换堆顶元素(用新元素替换数组中索引为0的元素); 下沉新放入堆顶的元素; 返回最开始取出的堆顶元素;// 取出堆中的最大元素,并且替换成元素epublic E replace(E e){ E ret = findMax(); data.set...原创 2019-02-12 14:40:00 · 276 阅读 · 0 评论 -
优先队列 01 基于最大堆的优先队列
基于最大堆的优先队列从队列的角度来看,优先队列,入队的时候,优先级高的元素会插队,插到它该排的位置,整个队列从队首到队尾,优先级一次降低;public class PriorityQueue<E extends Comparable<E>> implements Queue<E> { private MaxHeap<E> maxHe...原创 2019-02-12 14:49:00 · 125 阅读 · 0 评论 -
优先队列 02 Leetcode中的347号问题Ⅰ
347. 前K个高频元素使用自己实现的数据结构完成; 注意,队列的循环不能用for循环,要用while循环;import java.util.ArrayList;import java.util.List;import java.util.TreeMap;public class Solution { private class Array<E> { ...原创 2019-02-12 22:25:00 · 103 阅读 · 0 评论 -
优先队列 03 Leetcode中的347号问题Ⅱ
解法1向PriorityQueue传入可比较的Freq; Java中提供的PriorityQueue底层维护的是个最小堆,这会影响到Freq对Comparable接口中compareTo(Freq another)方法的实现;package leetcode._347;import java.util.ArrayList;import java.util.List;import ...原创 2019-02-12 23:45:00 · 126 阅读 · 0 评论 -
线段树 01 线段树基础
线段树在逻辑上的语义堆对数组的树化,是对数组中每一个元素的树化,是逻辑上对数组中每一个元素拓扑结构的改变,由一维变二维,从而将遍历的时间复杂度从O(n)降到O(logn); 线段树对数组的树化,是对数组长度的意义的树化,而对数组长度的切割规则是二分法,所以线段树是在二叉树的结构下组织的; 线段树从逻辑上看由1个数组和一棵线段树组成,从物理上看由2个数组组成: 逻辑上: 1个数组是线段...原创 2019-02-13 17:59:00 · 299 阅读 · 0 评论 -
线段树 02 构建线段树
构建线段树线段树的每个节点除了天然的对应一段长度外,不一定赋予其上的意义就是区间元素的和,所以两个节点向上汇聚成父节点的时候,不一定是加法,故定义此接口,表示线段树中两个节点的汇聚;public interface Merger<E> { E merge(E a, E b);}递归方法:在treeIndex的位置创建表示区间 [l, r] 的线段树(在脑子里想成...原创 2019-02-13 19:51:00 · 204 阅读 · 0 评论 -
并查集 03 快并
并查集的初始化虽然这一版的并查集在内部还是使用了数组存储元素所在的集合,但在逻辑上不像第一版的时候是一维的,这一版的数组parent表示的是每个元素的父元素在哪个位置上,逻辑上使用一棵棵树表示一个个集合,总体上数组parent表示的是一片森林(多棵树),从而可以利用树结构遍历时O(logn)的时间复杂度的优势; parent表示的森林中的树都是由孩子指向父亲的;// 我们的第二版Unio...原创 2019-02-17 14:35:00 · 106 阅读 · 0 评论 -
并查集 04 基于子集合大小的优化
基于每棵树大小的优化在并查集中增加了一个数组sz,用于表示并查集中以每个元素为根的树的大小,依次作为合并时谁合并于谁的依据; 节点少的树往节点大的树合并,更不容易增加合并后树的深度; 两棵树在合并的时候,如果不加考虑,那么最后合并成的树有可能退化成一个链表;public class UnionFind3 implements UF{ private int[] parent;...原创 2019-02-17 17:05:00 · 132 阅读 · 0 评论 -
并查集 05 基于子集合rank的优化
基于Rank优化的并查集Rank就是每棵子树的高度; 在基于size的优化的并查集中有一个问题:size小的子树的高度并一定就一定小于size大的树,size大的子树的高度可能就是2,size小的子树的高度也可能大于2; 合并时的优化的目的是合并后的树的高度最小,树的高度越小,查找时的路劲就越短,速度响应也越快; 因此用表示每棵子树的高度的数组rank代替表示每棵子树节点多少的数组siz...原创 2019-02-17 17:50:00 · 169 阅读 · 0 评论 -
红黑树 04 添加元素(三)添加新的元素
在红黑树中添加新的元素红黑树中添加新元素.png在红黑树中添加元素一共有以下4种情况: 新添加的元素要融合进2节点的左边; 新添加的元素要融合进3节点的中间; 新添加的元素要融合进3节点的左边; 新添加的元素要融合进3节点的右边; 这几种情况并不是互斥的,要依次进行,因为进行了一种变化后,可能达成另一种状态,还可以继续变化;添加元素代码如果直接添加到2节点左边...原创 2019-02-23 22:44:00 · 437 阅读 · 0 评论 -
红黑树 05 性能测试
AVL树 v.s 红黑树数据顺序添加;import java.util.ArrayList;public class Main3 { public static void main(String[] args) { int n = 20000000; ArrayList<Integer> testData = new ArrayLi...原创 2019-02-23 23:21:00 · 241 阅读 · 0 评论 -
哈希表 01 哈希表基础
哈希表哈希函数:将“键”转换为“索引”,键可能是字符串,浮点数,日期等; 通常情况下很难保证每一个“键”通过哈希函数的转换对应不同的“索引”; 哈希冲突:两个不同的“键”通过哈希函数得到一相同的“索引”; 哈希表上最复杂的操作就是解决哈希冲突和哈希函数的设计; 哈希表充分体现了算法设计领域的经典思想:空间换时间; 如果我们有1的空间,我们只能用O(n)的时间复杂度完成各项操作(线性表...原创 2019-02-24 15:35:00 · 194 阅读 · 0 评论 -
哈希表 02 哈希函数的设计
哈希函数设计“键”通过哈希函数得到的“索引”分布越均匀越好; 对于一些特殊领域,有特殊领域的哈希函数设计方式,甚至有专门的论文;一般的哈希函数设计原则整型 小范围正整数直接使用; 小范围负整数进行偏移,比如 -100 ~ 0 -> 0 ~ 100; 大整数 通常做法:取模,比如取后4位,等同于mod 10000,但取模不能利用大整数中所有有用的信息,一个简单的解决办...原创 2019-02-24 16:12:00 · 584 阅读 · 0 评论 -
哈希表 03 Java中的hashCode
在Java中,如果不显示的覆盖类的hashCode()方法,Java默认的hashCode()方法是根据对象在内存中的地址返回了一个整数值; 也就是说,相同的对象(每个属性值都相等),hashCode可能是不一样的;public class Student { int grade; int cls; String firstName; String last...原创 2019-02-24 19:01:00 · 116 阅读 · 0 评论 -
哈希表 04 哈希冲突的处理 链地址法
连地址法概述一开始开辟一个M的空间的数组; 将要存储的对象转换成整数,用M取模,结果会对应到数组中的一个索引; 如果发生哈希冲突,取模的结果会指向数组中同一个索引; 将产生哈希冲突的对象链入其对应索引挂接的对象的后面,形成一条链表; 数组中的每个索引后都挂有一条链表,这就是所谓的链地址法(Separate Chain); 当链表的长度超过一定程度后,Java会将其转换以红黑树,Jav...原创 2019-02-24 19:32:00 · 260 阅读 · 0 评论 -
哈希表 05 实现自己的哈希表
实现思路维护一个M个元素的数组,数组的每一格挂一棵红黑树; 一个元素进来,先根据key计算出其要挂载到哪棵红黑树中; 确定了要挂载的红黑树后,问题就变成了在红黑树中添加,删除等操作了; M越小,哈希冲突的概率就越大,所以M的大小很大程度的影响了哈希表的效率;import java.util.TreeMap;public class HashTable<K, V> {...原创 2019-02-24 20:17:00 · 149 阅读 · 0 评论 -
哈希表 06 时间复杂度分析 & 动态扩容
哈希表的时间复杂度分析n个元素平均的分布在M棵红黑树中,每棵红黑树容纳的元素平均为 n/M ,即现有的哈希表的时间复杂度为O(log(n/M)); 说好的O(1)呢? M越大,元素分散的越平均,时间复杂度越低,M足够大,时间复杂度理论上就可以达到O(1);动态扩容代码触发扩容,缩容的上界和哈希表的初始容量;private static final int upperTol = 1...原创 2019-02-25 10:56:00 · 1031 阅读 · 0 评论 -
红黑树 03 添加元素(二)颜色翻转 & 右旋转
颜色翻转颜色翻转过程概述: 颜色翻转的触发场景:向2-3树中的3节点添加元素,新添加的元素要添加到3节点的最右侧; 对应到红黑树中的情形是:新添加的红节点在黑节点右侧,黑节点的左侧还有一个红节点,红黑树中的这种形态对应到2-3树中就是一个临时的4节点; 2-3树中临时的4节点要拆成3个2节点,这种形态对应到红黑树中就是3个黑节点; 2-3树在拆成3个2节点后,要向上融合,3个2节...原创 2019-02-23 21:17:00 · 177 阅读 · 0 评论 -
红黑树 02 添加元素(一)保持根节点为黑色 & 左旋转
保持根节点为黑色红黑树的性质之一就是:根节点为黑色; 添加元素后,可能导致红黑树根节点的变动,因此要维护根节点为黑色的属性;// 向红黑树中添加新的元素(key, value)public void add(K key, V value){ root = add(root, key, value); root.color = BLACK; // 最终根节点为黑色节点}...原创 2019-02-23 17:34:00 · 235 阅读 · 0 评论 -
红黑树 01 红黑树和2-3树的等价性
2-3树2-3树是一种绝对平衡的二分搜索树; 2-3树中存在2种节点:2节点和3节点; 2节点:每个节点存1个元素,有2个孩子; 3节点:每个节点存2个元素,有3个孩子; 新添加的元素都要融合进叶子节点,或者融合成3节点,或者融合成临时的4节点; 融合成临时的4节点后,就要将4节点拆成3个2节点,然后将中间的节点向上融合到其父节点,融合后的结构如果任然是4节点,则继续拆;红黑树...原创 2019-02-22 20:43:00 · 123 阅读 · 0 评论 -
并查集 06 路径压缩
基于路径压缩的并查集把rank小的树合并到rank大的树中,这已经是合并时的优化行为了,但是,随着合并次数的增多,树会越来越多,其实树的形状是有进一步优化的空间的,让树的高度尽可能的矮,最理想的情况,所有的树高度为2; 具体做法是:find的时候,p即代表待查询的元素在集合中的位置,也是找待查询元素的根节点的一个游标,p作为游标是一直往树根移动的,每次找p的根节点的时候,只要游标p还没指向根...原创 2019-02-17 19:30:00 · 120 阅读 · 0 评论 -
并查集 07 递归的路径压缩
递归的路径压缩算法递归的路劲压缩算法要做的是:一个find操作,p及其所有父元素都移到索引为1(0 - Based)的层去;相对于不使用递归的路径压缩算法,能更快的达到整棵树的层数为2; 方法 int find(int p) 的语义是:返回元素p的根节点;其查询路径是沿着其父节点一路查到根节点,单看这一条查询路径,其实就是一个链表,那么链表就具有了天然的递归性; 在一个链表中查它的根,其实...原创 2019-02-17 22:19:00 · 294 阅读 · 0 评论 -
AVL树 01 AVL树基础
AVL树AVL树是这样定义的一棵平衡二叉树:对任意节点,左子树和右子树的高度差不能超过1; AVL树中的每个节点标注了节点的高度; AVL树中的每个节点都有一个平衡因子,平衡因子指的是左右子树的高度差;AVLTree基础代码AVLTree的代码是基于之前BSTMap的代码改造的,每个节点中key和value,节点的可比较性体现在key上; 相较于BSTMap,AVLTree中的节点...原创 2019-02-20 13:37:00 · 155 阅读 · 0 评论 -
AVL树 02 平衡检查 & 是否满足二分搜索树的检查
检查AVLTree是否是一棵BST中序遍历AVLTree,如果满足BST的定义,其结果是从小到大的;// 判断该二叉树是否是一棵二分搜索树public boolean isBST(){ ArrayList<K> keys = new ArrayList<>(); inOrder(root, keys); for(int i = 1 ; i...原创 2019-02-20 14:00:00 · 141 阅读 · 0 评论 -
AVL树 03 左孩子的左侧 & 右孩子的右侧
关于平衡因子的大小叶子节点的平衡因子 == 0,但平衡因子为0的节点不一定是叶子节点,也可能是左右子树高度相等的节点; 平衡因子 > 0,左子树的高度 > 右子树的高度; 平衡因子 < 0,左子树的高度 < 右子树的高度;右旋转LL(Left's Left):新插入节点在不平衡节点(Y)左孩子的左侧; 此时不平衡节点(Y)做右旋转; 右旋转代码:// ...原创 2019-02-20 19:12:00 · 154 阅读 · 0 评论 -
AVL树 04 左孩子的右侧 & 右孩子的左侧
平衡的维护LR:新添加的节点在不平衡节点的左孩子的右侧; 先让不平衡节点(Y)的左孩子(X)左旋转,变成LL的情况,Z成为Y的左孩子; 再让不平衡节点(Y)右旋转; RL:新添加的节点在不平衡节点的右孩子的左侧; 先让不平衡节点(Y)的右孩子(X)右旋转,变成RR的情况,Z成为Y的右孩子; 再让不平衡节点(Y)左旋转; // 向二分搜索树中添加新的元素(key, val...原创 2019-02-21 12:54:00 · 139 阅读 · 0 评论