目录
《【算法笔记】有序表------AVL树》
《【算法笔记】有序表------SB树》
《【算法笔记】有序表------跳表》
《【算法笔记】有序表------相关题目》
1、算法概述
-
跳表:跳表(Skip List)是一种非常巧妙的数据结构,它通过给有序链表增加多级索引的方式,实现了近乎平衡二叉查找树的查询效率。
-
跳表的核心思想:
- 想象一个有序链表,查找一个元素只能从头到尾顺序扫描,时间复杂度是 O(n)。
- 跳表的核心思想非常直观:通过建立多级"索引"来跳过大量不必要的节点,从而加速查找过程。
- 跳表的本质是用空间换时间的方法,用多层级的冗余来跳过不必要的节点,从而加速查找过程。
- 1、一级索引:我们从底层的数据链表开始,每隔一个(或两个)节点就提取一个节点,在上面组成一层稀疏的索引链表。
- 2、多级索引:然后可以再为这层索引建立更稀疏的上一级索引,如此往复。
- 3、查找过程:查找时,从最高层索引开始,像坐地铁一样,先坐快线(高层索引)快速接近目的地,再换乘普线(低层索引)或步行(底层链表)到达精确位置。
- 通过这种"分层跳跃"的策略,搜索的时间复杂度可以降低到 O(log n)。
- 在跳表的结构中,越是底层的,节点数据越完善,最底层包含了所有的节点。
-
跳表的查询操作:
- 查找是跳表所有操作的基础。
- 过程:从跳表的当前最高层 level的头节点开始。
- 1、在当前层向后遍历,如果下一个节点的值小于目标值,则继续向后。
- 2、如果下一个节点的值大于等于目标值,则下降到下一层,继续上述过程。
- 3、直到下降至底层(第0层),此时如果下一个节点的值等于目标值,则查找成功;否则失败。
- 示例:假设要查找元素 36,路径可能是:
- 顶层头节点 -> L2: 20 -> (下降至L1) -> L1: 30 -> L1: (下降至L0) -> L0: 36 。
-
跳表的插入操作:
- 插入操作的关键在于如何决定新节点的"高度"(层数),这通过一个随机化算法来完成。
- 1、确定插入位置:首先执行与查找类似的逻辑,找到新节点在底层链表中的位置,
- 并在搜索过程中记录下每一层中最后一个小于新节点值的节点(通常保存在一个 update数组中)。
- 2、随机生成层数:为新节点随机生成一个层数 level。
- 常用的是"抛硬币"法,即从第1层开始,每次有概率 p(如50%)晋升到下一层,直到晋升失败或达到最大层数限制。
- 3、调整指针:创建新节点,然后对于第 0层到第 level层,将新节点的每层指针指向 update[i]的后继节点,
- 再将 update[i]在该层的指针指向新节点。如果新节点的层数超过了当前跳表的最大层数,则需要更新跳表的最大层数。
-
跳表的删除操作:
-
删除是插入的逆过程。
-
1、查找前驱节点:类似插入,先查找待删除节点,并在过程中记录每一层中待删除节点的前驱节点(存入 update数组)。
-
2、调整指针:找到待删除节点后,对于其出现的每一层,将对应前驱节点(update[i])的指针指向待删除节点的下一个节点。
-
3、更新跳表层数:删除后,如果最高层因删除而变为空链,则需要降低跳表的层数。
-
跳表的性能指标:
- 1、时间复杂度:由于索引的随机分布是均匀的,跳表操作的平均时间复杂度非常高效。最坏情况(虽然概率极低)下会退化为链表。
- 所以查找、插入、删除的平均时间复杂度为O(logn),最坏时间复杂度为O(n)
- 2、空间复杂度:索引节点需要额外空间,但所有索引节点的总和约为 n/(1-p)。当 p=1/2时,总和约为 2n,
- 因此空间复杂度是 O(n),是典型的用空间换时间。
-
跳表的实际应用
- 正是因为其高效的性能和相对简单的实现,跳表在一些著名的开源项目中得到了应用:
- 1、Redis:使用跳表来实现其有序集合(Sorted Set)数据类型。
- 2、LevelDB / RocksDB:使用跳表作为内存中的键值存储(MemTable)。
-
总结
- 跳表是一种优雅而强大的数据结构。它通过多级索引和随机化技术,在有序链表的基础上实现了对数时间复杂度的查找、插入和删除操作。
- 虽然它通过牺牲一定的空间来换取时间,但其原理直观、实现相对简单、并发性能好的特点,使其在实际应用中,尤其是在数据库和缓存系统中,占据了重要的一席之地。
2、利用跳表实现的自定义map
java
/**
* 利用跳表实现的自定义map
*/
public static class SkipListMap<K extends Comparable<K>, V> {
// 平衡概率, 在插入节点时,随机生成的数小于这个值,继续升级层数,大于等于这个值,停止升级层数
private static final double PROBABILITY = 0.5;
// 头结点
private final Node<K, V> head;
private int size;
// 最大的层数
private int maxLevel;
public SkipListMap() {
this.head = new Node<>(null, null);
// 初始化时加入一个为null的节点,代表第0层
head.nextNodes.add(null);
size = 0;
maxLevel = 0;
}
public boolean containsKey(K key) {
if (key == null) {
return false;
}
// 先找到整棵树小于key最右侧的节点
Node<K, V> less = mostRightLessNodeInTree(key);
// 判断下一个节点是不是和key相等
Node<K, V> next = less.nextNodes.get(0);
return next != null && next.isKeyEqual(key);
}
public void put(K key, V value) {
if (key == null) {
return;
}
// 先判断存不存在,找到整棵树小于key的最右侧节点,然后看下一个是不是和key相等
Node<K, V> less = mostRightLessNodeInTree(key);
Node<K, V> find = less != null ? less.nextNodes.get(0) : null;
if (find != null && find.isKeyEqual(key)) {
// 已经存在,更新即可
find.value = value;
return;
}
// 先随机获得增加节点的level
int newNodeLevel = 0;
while (Math.random() < PROBABILITY) {
newNodeLevel++;
}
// 如果新的level大于整棵树的最大level,最大level用新的,并补齐层数
while (newNodeLevel > this.maxLevel) {
head.nextNodes.add(null);
this.maxLevel++;
}
// 创建新的节点,并在新的节点中补齐层数
Node<K, V> newNode = new Node<>(key, value);
for (int i = 0; i <= newNodeLevel; i++) {
newNode.nextNodes.add(null);
}
// 从需要添加层的最高层往下加入节点
// 虽然也能从newNodeLevel开始,但是从最高开始效率更高,因为会利用到跳表的高效查询能力
int level = this.maxLevel;
Node<K, V> pre = this.head;
while (level >= 0) {
// 现在当前层找到最右侧的小于key的节点
pre = mostRightLessNodeInLevel(key, pre, level);
if (level <= newNodeLevel) {
newNode.nextNodes.set(level, pre.nextNodes.get(level));
pre.nextNodes.set(level, newNode);
}
level--;
}
size++;
}
public V get(K key) {
if (key == null) {
return null;
}
// 小于key的最右侧节点
Node<K, V> node = mostRightLessNodeInTree(key);
// 小于key的最右侧节点的下一个节点
node = node.nextNodes.get(0);
return node != null && node.isKeyEqual(key) ? node.value : null;
}
public void remove(K key) {
if (!containsKey(key)) {
return;
}
// 从上往下依次删除节点
int level = this.maxLevel;
Node<K, V> pre = head;
Node<K, V> next;
while (level >= 0) {
// 找到当前层小于key最右侧的节点
pre = mostRightLessNodeInLevel(key, pre, level);
next = pre.nextNodes.get(level);
// pre的下一个next如果和key相同,就是要删除的
if (next != null && next.isKeyEqual(key)) {
// pre设置成下一个的下一个,就是删除当前的next
pre.nextNodes.set(level, next.nextNodes.get(level));
}
// 如果当前层没有节点了,就删除这一层,并整体降低高度
// 在level层只有一个节点了,就是默认节点head
if (level != 0 && pre == head && pre.nextNodes.get(level) == null) {
head.nextNodes.remove(level);
this.maxLevel--;
}
level--;
}
size--;
}
public K firstKey() {
return head.nextNodes.get(0) != null ? head.nextNodes.get(0).key : null;
}
public K lastKey() {
int level = maxLevel;
Node<K, V> cur = head;
// 从最大层一直到底,依次往下跳到第0层的最后一个
while (level >= 0) {
Node<K, V> next = cur.nextNodes.get(level);
while (next != null) {
cur = next;
next = cur.nextNodes.get(level);
}
level--;
}
return cur.key;
}
public K ceilingKey(K key) {
if (key == null) {
return null;
}
Node<K, V> less = mostRightLessNodeInTree(key);
Node<K, V> next = less.nextNodes.get(0);
return next != null ? next.key : null;
}
public K floorKey(K key) {
if (key == null) {
return null;
}
Node<K, V> less = mostRightLessNodeInTree(key);
Node<K, V> next = less.nextNodes.get(0);
return next != null && next.isKeyEqual(key) ? next.key : less.key;
}
public int size() {
return size;
}
/**
* 在当前cur的level层找到小于key最后一个节点
* 现在来到的节点是cur,来到了cur的level层,在level层上,找到<key最后一个节点并返回
*/
private Node<K, V> mostRightLessNodeInLevel(K key, Node<K, V> cur, int level) {
Node<K, V> next = cur.nextNodes.get(level);
while (next != null && next.isKeyLess(key)) {
cur = next;
next = cur.nextNodes.get(level);
}
return cur;
}
/**
* 查找真个树最右侧小于key的节点
* 从最高层开始,一路找下去,一直找到第0层小于key的最右的节点
*/
private Node<K, V> mostRightLessNodeInTree(K key) {
if (key == null) {
return null;
}
// 从最高层往下找
int level = this.maxLevel;
Node<K, V> cur = this.head;
while (level >= 0) {
// 每一层找到小于key最右侧的节点
// 上一层的最右侧的cur,到下一层直接从cur开始,达到的效果是跳过前面到cur的节点,下一层从cur开始查找
// 这种结构里面嵌套链表巧妙的设计,是跳表提升效率的地方
cur = mostRightLessNodeInLevel(key, cur, level--);
}
return cur;
}
/**
* 跳表的节点
*/
static class Node<K extends Comparable<K>, V> {
private final K key;
private V value;
// 下一级的节点
private final ArrayList<Node<K, V>> nextNodes;
public Node(K key, V value) {
this.key = key;
this.value = value;
this.nextNodes = new ArrayList<>();
}
/**
* 当前节点的key是否比otherKey小,true,不是false
*/
public boolean isKeyLess(K otherKey) {
return otherKey != null && (key == null || key.compareTo(otherKey) < 0);
}
/**
* 当前节点的key是否和otherKey相等
*/
public boolean isKeyEqual(K otherKey) {
return (key == null && otherKey == null)
|| (key != null && otherKey != null && key.compareTo(otherKey) == 0);
}
}
}
整体代码和测试:
java
import java.util.ArrayList;
import java.util.Objects;
import java.util.TreeMap;
/**
* 跳表:跳表(Skip List)是一种非常巧妙的数据结构,它通过给有序链表增加多级索引的方式,实现了近乎平衡二叉查找树的查询效率。
* <br>
* 跳表的核心思想:
* 想象一个有序链表,查找一个元素只能从头到尾顺序扫描,时间复杂度是 O(n)。
* 跳表的核心思想非常直观:通过建立多级"索引"来跳过大量不必要的节点,从而加速查找过程。
* 跳表的本质是用空间换时间的方法,用多层级的冗余来跳过不必要的节点,从而加速查找过程。
* 1、一级索引:我们从底层的数据链表开始,每隔一个(或两个)节点就提取一个节点,在上面组成一层稀疏的索引链表。
* 2、多级索引:然后可以再为这层索引建立更稀疏的上一级索引,如此往复。
* 3、查找过程:查找时,从最高层索引开始,像坐地铁一样,先坐快线(高层索引)快速接近目的地,再换乘普线(低层索引)或步行(底层链表)到达精确位置。
* 通过这种"分层跳跃"的策略,搜索的时间复杂度可以降低到 O(log n)。
* 在跳表的结构中,越是底层的,节点数据越完善,最底层包含了所有的节点。
* <br>
* 跳表的查询操作:
* 查找是跳表所有操作的基础。
* 过程:从跳表的当前最高层 level的头节点开始。
* 1、在当前层向后遍历,如果下一个节点的值小于目标值,则继续向后。
* 2、如果下一个节点的值大于等于目标值,则下降到下一层,继续上述过程。
* 3、直到下降至底层(第0层),此时如果下一个节点的值等于目标值,则查找成功;否则失败。
* 示例:假设要查找元素 36,路径可能是:
* 顶层头节点 -> L2: 20 -> (下降至L1) -> L1: 30 -> L1: (下降至L0) -> L0: 36 。
* <br>
* 跳表的插入操作:
* 插入操作的关键在于如何决定新节点的"高度"(层数),这通过一个随机化算法来完成。
* 1、确定插入位置:首先执行与查找类似的逻辑,找到新节点在底层链表中的位置,
* 并在搜索过程中记录下每一层中最后一个小于新节点值的节点(通常保存在一个 update数组中)。
* 2、随机生成层数:为新节点随机生成一个层数 level。
* 常用的是"抛硬币"法,即从第1层开始,每次有概率 p(如50%)晋升到下一层,直到晋升失败或达到最大层数限制。
* 3、调整指针:创建新节点,然后对于第 0层到第 level层,将新节点的每层指针指向 update[i]的后继节点,
* 再将 update[i]在该层的指针指向新节点。如果新节点的层数超过了当前跳表的最大层数,则需要更新跳表的最大层数。
* <br>
* 跳表的删除操作:
* 删除是插入的逆过程。
* 1、查找前驱节点:类似插入,先查找待删除节点,并在过程中记录每一层中待删除节点的前驱节点(存入 update数组)。
* 2、调整指针:找到待删除节点后,对于其出现的每一层,将对应前驱节点(update[i])的指针指向待删除节点的下一个节点。
* 3、更新跳表层数:删除后,如果最高层因删除而变为空链,则需要降低跳表的层数。
* <br>
* 跳表的性能指标:
* 1、时间复杂度:由于索引的随机分布是均匀的,跳表操作的平均时间复杂度非常高效。最坏情况(虽然概率极低)下会退化为链表。
* 所以查找、插入、删除的平均时间复杂度为O(logn),最坏时间复杂度为O(n)
* 2、空间复杂度:索引节点需要额外空间,但所有索引节点的总和约为 n/(1-p)。当 p=1/2时,总和约为 2n,
* 因此空间复杂度是 O(n),是典型的用空间换时间。
* <br>
* 跳表的实际应用
* 正是因为其高效的性能和相对简单的实现,跳表在一些著名的开源项目中得到了应用:
* 1、Redis:使用跳表来实现其有序集合(Sorted Set)数据类型。
* 2、LevelDB / RocksDB:使用跳表作为内存中的键值存储(MemTable)。
* <br>
* 总结
* 跳表是一种优雅而强大的数据结构。它通过多级索引和随机化技术,在有序链表的基础上实现了对数时间复杂度的查找、插入和删除操作。
* 虽然它通过牺牲一定的空间来换取时间,但其原理直观、实现相对简单、并发性能好的特点,使其在实际应用中,尤其是在数据库和缓存系统中,占据了重要的一席之地。
*/
public class SkipList {
/**
* 利用跳表实现的自定义map
*/
public static class SkipListMap<K extends Comparable<K>, V> {
// 平衡概率, 在插入节点时,随机生成的数小于这个值,继续升级层数,大于等于这个值,停止升级层数
private static final double PROBABILITY = 0.5;
// 头结点
private final Node<K, V> head;
private int size;
// 最大的层数
private int maxLevel;
public SkipListMap() {
this.head = new Node<>(null, null);
// 初始化时加入一个为null的节点,代表第0层
head.nextNodes.add(null);
size = 0;
maxLevel = 0;
}
public boolean containsKey(K key) {
if (key == null) {
return false;
}
// 先找到整棵树小于key最右侧的节点
Node<K, V> less = mostRightLessNodeInTree(key);
// 判断下一个节点是不是和key相等
Node<K, V> next = less.nextNodes.get(0);
return next != null && next.isKeyEqual(key);
}
public void put(K key, V value) {
if (key == null) {
return;
}
// 先判断存不存在,找到整棵树小于key的最右侧节点,然后看下一个是不是和key相等
Node<K, V> less = mostRightLessNodeInTree(key);
Node<K, V> find = less != null ? less.nextNodes.get(0) : null;
if (find != null && find.isKeyEqual(key)) {
// 已经存在,更新即可
find.value = value;
return;
}
// 先随机获得增加节点的level
int newNodeLevel = 0;
while (Math.random() < PROBABILITY) {
newNodeLevel++;
}
// 如果新的level大于整棵树的最大level,最大level用新的,并补齐层数
while (newNodeLevel > this.maxLevel) {
head.nextNodes.add(null);
this.maxLevel++;
}
// 创建新的节点,并在新的节点中补齐层数
Node<K, V> newNode = new Node<>(key, value);
for (int i = 0; i <= newNodeLevel; i++) {
newNode.nextNodes.add(null);
}
// 从需要添加层的最高层往下加入节点
// 虽然也能从newNodeLevel开始,但是从最高开始效率更高,因为会利用到跳表的高效查询能力
int level = this.maxLevel;
Node<K, V> pre = this.head;
while (level >= 0) {
// 现在当前层找到最右侧的小于key的节点
pre = mostRightLessNodeInLevel(key, pre, level);
if (level <= newNodeLevel) {
newNode.nextNodes.set(level, pre.nextNodes.get(level));
pre.nextNodes.set(level, newNode);
}
level--;
}
size++;
}
public V get(K key) {
if (key == null) {
return null;
}
// 小于key的最右侧节点
Node<K, V> node = mostRightLessNodeInTree(key);
// 小于key的最右侧节点的下一个节点
node = node.nextNodes.get(0);
return node != null && node.isKeyEqual(key) ? node.value : null;
}
public void remove(K key) {
if (!containsKey(key)) {
return;
}
// 从上往下依次删除节点
int level = this.maxLevel;
Node<K, V> pre = head;
Node<K, V> next;
while (level >= 0) {
// 找到当前层小于key最右侧的节点
pre = mostRightLessNodeInLevel(key, pre, level);
next = pre.nextNodes.get(level);
// pre的下一个next如果和key相同,就是要删除的
if (next != null && next.isKeyEqual(key)) {
// pre设置成下一个的下一个,就是删除当前的next
pre.nextNodes.set(level, next.nextNodes.get(level));
}
// 如果当前层没有节点了,就删除这一层,并整体降低高度
// 在level层只有一个节点了,就是默认节点head
if (level != 0 && pre == head && pre.nextNodes.get(level) == null) {
head.nextNodes.remove(level);
this.maxLevel--;
}
level--;
}
size--;
}
public K firstKey() {
return head.nextNodes.get(0) != null ? head.nextNodes.get(0).key : null;
}
public K lastKey() {
int level = maxLevel;
Node<K, V> cur = head;
// 从最大层一直到底,依次往下跳到第0层的最后一个
while (level >= 0) {
Node<K, V> next = cur.nextNodes.get(level);
while (next != null) {
cur = next;
next = cur.nextNodes.get(level);
}
level--;
}
return cur.key;
}
public K ceilingKey(K key) {
if (key == null) {
return null;
}
Node<K, V> less = mostRightLessNodeInTree(key);
Node<K, V> next = less.nextNodes.get(0);
return next != null ? next.key : null;
}
public K floorKey(K key) {
if (key == null) {
return null;
}
Node<K, V> less = mostRightLessNodeInTree(key);
Node<K, V> next = less.nextNodes.get(0);
return next != null && next.isKeyEqual(key) ? next.key : less.key;
}
public int size() {
return size;
}
/**
* 在当前cur的level层找到小于key最后一个节点
* 现在来到的节点是cur,来到了cur的level层,在level层上,找到<key最后一个节点并返回
*/
private Node<K, V> mostRightLessNodeInLevel(K key, Node<K, V> cur, int level) {
Node<K, V> next = cur.nextNodes.get(level);
while (next != null && next.isKeyLess(key)) {
cur = next;
next = cur.nextNodes.get(level);
}
return cur;
}
/**
* 查找真个树最右侧小于key的节点
* 从最高层开始,一路找下去,一直找到第0层小于key的最右的节点
*/
private Node<K, V> mostRightLessNodeInTree(K key) {
if (key == null) {
return null;
}
// 从最高层往下找
int level = this.maxLevel;
Node<K, V> cur = this.head;
while (level >= 0) {
// 每一层找到小于key最右侧的节点
// 上一层的最右侧的cur,到下一层直接从cur开始,达到的效果是跳过前面到cur的节点,下一层从cur开始查找
// 这种结构里面嵌套链表巧妙的设计,是跳表提升效率的地方
cur = mostRightLessNodeInLevel(key, cur, level--);
}
return cur;
}
/**
* 跳表的节点
*/
static class Node<K extends Comparable<K>, V> {
private final K key;
private V value;
// 下一级的节点
private final ArrayList<Node<K, V>> nextNodes;
public Node(K key, V value) {
this.key = key;
this.value = value;
this.nextNodes = new ArrayList<>();
}
/**
* 当前节点的key是否比otherKey小,true,不是false
*/
public boolean isKeyLess(K otherKey) {
return otherKey != null && (key == null || key.compareTo(otherKey) < 0);
}
/**
* 当前节点的key是否和otherKey相等
*/
public boolean isKeyEqual(K otherKey) {
return (key == null && otherKey == null)
|| (key != null && otherKey != null && key.compareTo(otherKey) == 0);
}
}
}
public static void main(String[] args) {
System.out.println("======");
functionTest("SkipListMap");
System.out.println("======");
performanceTest("SkipListMap");
}
public static void functionTest(String prefix) {
System.out.println(prefix + " 功能测试开始");
TreeMap<Integer, Integer> treeMap = new TreeMap<>();
SkipListMap<Integer, Integer> target = new SkipListMap<>();
int maxK = 500;
int maxV = 50000;
int testTime = 1000000;
boolean success = true;
for (int i = 0; i < testTime; i++) {
int addK = (int) (Math.random() * maxK);
int addV = (int) (Math.random() * maxV);
treeMap.put(addK, addV);
target.put(addK, addV);
int removeK = (int) (Math.random() * maxK);
treeMap.remove(removeK);
target.remove(removeK);
int querryK = (int) (Math.random() * maxK);
boolean treeMapAns = treeMap.containsKey(querryK);
boolean ans = target.containsKey(querryK);
if (treeMapAns != ans) {
System.out.println("containsKey 错误");
System.out.printf("key:%d, treeMapAns:%b, ans:%b\n", querryK, treeMapAns, ans);
success = false;
break;
}
if (treeMap.containsKey(querryK)) {
int v1 = treeMap.get(querryK);
int v2 = target.get(querryK);
if (v1 != v2) {
System.out.println("get 错误");
System.out.printf("key:%d, treeMapAns:%d, ans:%d\n", querryK, v1, v2);
success = false;
break;
}
Integer f1 = treeMap.floorKey(querryK);
Integer f2 = target.floorKey(querryK);
if (!Objects.equals(f1, f2)) {
System.out.println("floorKey 错误");
System.out.printf("key:%d, treeMapAns:%d, ans:%d\n", querryK, f1, f2);
success = false;
break;
}
f1 = treeMap.ceilingKey(querryK);
f2 = target.ceilingKey(querryK);
if (!Objects.equals(f1, f2)) {
System.out.println("ceilingKey 错误");
System.out.printf("key:%d, treeMapAns:%d, ans:%d\n", querryK, f1, f2);
success = false;
break;
}
}
Integer f1 = treeMap.firstKey();
Integer f2 = target.firstKey();
if (!Objects.equals(f1, f2)) {
System.out.println("firstKey 错误");
System.out.printf("key:%d, treeMapAns:%d, ans:%d\n", querryK, f1, f2);
success = false;
break;
}
f1 = treeMap.lastKey();
f2 = target.lastKey();
if (!Objects.equals(f1, f2)) {
System.out.println("lastKey 错误");
System.out.printf("key:%d, treeMapAns:%d, ans:%d\n", querryK, f1, f2);
success = false;
break;
}
int treeMapSize = treeMap.size();
int ansSize = target.size();
if (treeMapSize != ansSize) {
System.out.println("size 错误");
System.out.printf("key:%d, treeMapAns:%d, ans:%d\n", querryK, treeMapSize, ansSize);
success = false;
break;
}
}
if (!success) {
System.out.println("测试失败");
return;
}
System.out.println(prefix + " 功能测试结束");
}
public static void performanceTest(String prefix) {
System.out.println(prefix + " 性能测试开始");
TreeMap<Integer, Integer> treeMap = new TreeMap<>();
SkipListMap<Integer, Integer> target = new SkipListMap<>();
long start;
long end;
int max = 1000000;
System.out.println("顺序递增加入测试,数据规模 : " + max);
start = System.currentTimeMillis();
for (int i = 0; i < max; i++) {
treeMap.put(i, i);
}
end = System.currentTimeMillis();
System.out.println("treeMap 运行时间 : " + (end - start) + "ms");
start = System.currentTimeMillis();
for (int i = 0; i < max; i++) {
target.put(i, i);
}
end = System.currentTimeMillis();
System.out.println(prefix + " 运行时间 : " + (end - start) + "ms");
System.out.println("顺序递增删除测试,数据规模 : " + max);
start = System.currentTimeMillis();
for (int i = 0; i < max; i++) {
treeMap.remove(i);
}
end = System.currentTimeMillis();
System.out.println("treeMap 运行时间 : " + (end - start) + "ms");
start = System.currentTimeMillis();
for (int i = 0; i < max; i++) {
target.remove(i);
}
end = System.currentTimeMillis();
System.out.println(prefix + " 运行时间 : " + (end - start) + "ms");
System.out.println("顺序递减加入测试,数据规模 : " + max);
start = System.currentTimeMillis();
for (int i = max; i >= 0; i--) {
treeMap.put(i, i);
}
end = System.currentTimeMillis();
System.out.println("treeMap 运行时间 : " + (end - start) + "ms");
start = System.currentTimeMillis();
for (int i = max; i >= 0; i--) {
target.put(i, i);
}
end = System.currentTimeMillis();
System.out.println(prefix + " 运行时间 : " + (end - start) + "ms");
System.out.println("顺序递减删除测试,数据规模 : " + max);
start = System.currentTimeMillis();
for (int i = max; i >= 0; i--) {
treeMap.remove(i);
}
end = System.currentTimeMillis();
System.out.println("treeMap 运行时间 : " + (end - start) + "ms");
start = System.currentTimeMillis();
for (int i = max; i >= 0; i--) {
target.remove(i);
}
end = System.currentTimeMillis();
System.out.println(prefix + " 运行时间 : " + (end - start) + "ms");
System.out.println("随机加入测试,数据规模 : " + max);
start = System.currentTimeMillis();
for (int i = 0; i < max; i++) {
treeMap.put((int) (Math.random() * i), i);
}
end = System.currentTimeMillis();
System.out.println("treeMap 运行时间 : " + (end - start) + "ms");
start = System.currentTimeMillis();
for (int i = max; i >= 0; i--) {
target.put((int) (Math.random() * i), i);
}
end = System.currentTimeMillis();
System.out.println(prefix + " 运行时间 : " + (end - start) + "ms");
System.out.println("随机删除测试,数据规模 : " + max);
start = System.currentTimeMillis();
for (int i = 0; i < max; i++) {
treeMap.remove((int) (Math.random() * i));
}
end = System.currentTimeMillis();
System.out.println("treeMap 运行时间 : " + (end - start) + "ms");
start = System.currentTimeMillis();
for (int i = max; i >= 0; i--) {
target.remove((int) (Math.random() * i));
}
end = System.currentTimeMillis();
System.out.println(prefix + " 运行时间 : " + (end - start) + "ms");
System.out.println(prefix + " 性能测试结束");
}
}
后记
个人学习总结笔记,不能保证非常详细,轻喷