哈希表、映射、集合
哈希表:根据关键码值(Key value)直接进行访问的数据结构。把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做哈希函数,存放记录的数组就叫做哈希表。
集合:SET,不重复元素的集合
哈希冲突:经过哈希函数以后,得到的哈希值是一样的。冲突解决方法有两类, 开放寻址法(open addressing) 和链表法(chaining) 。
装载因子:表示散列表中一定比例的空闲槽位
散列表的装载因子=填入表中的元素个数/散列表的长度
树、图、二叉树、二叉搜索树
二叉搜索树:
二叉搜索树,也称二叉搜索树、有序二叉树(Ordered Binary Tree)、 排序二叉树(Sorted Binary Tree),是指一棵空树或者具有下列性质的二叉树:
中序遍历后得到升序排列
二叉树的常见遍历:
二叉树遍历模板:
# 前序遍历
def preorder(self, root):
if root:
# 根结点
self.arr.append(root.val)
# 左子树
self.preorder(root.left)
# 右子树
self.preorder(root.right)
# 中序遍历
def inorder(self, root):
if root:
# 左子树
self.inorder(root.left)
# 根结点
self.arr.append(root.val)
# 右子树
self.inorder(root.right)
# 后序遍历
def postorder(self, root):
if root:
# 左子树
self.postorder(root.left)
# 右子树
self.postorder(root.right)
# 根结点
self.arr.append(root.val)
分治、回溯和递归
分治算法的核心思想是分而治之,就是将原问题划分为n个规模较小,并且结构与原问题相似的子问题,递归解决这些子问题,然后再合并其结果,就是原问题的解。
分治算法的递归实现中,每一层递归都涉及这样三个操作:
回溯法采用试错的思想,它尝试分步的去解决一个问题,在尝试过程中如果发现不对,就回溯到上一步甚至上几步,通常用递归方式实现。对于提前知道必然错误的分支,可以提前剪支。
递归模板:
def recursion(level, param1, param2, ...):
# 递归终止条件
if level > MAX_LEVEL:
process_result
return
# 处理当前层逻辑
process(level, data...)
# 往下一层
self.recursion(level + 1, p1, ...)
# 清理当前层
分治模板:
def divide_conquer(problem, param1, param2, ...):
# 递归终止条件
if problem is None:
print_result
return
# 处理当前层逻辑(分解子问题)
data = prepare_data(problem)
subproblems = split_problem(problem, data)
# 往下一层(解决子问题)
subresult1 = self.divide_conquer(subproblems[0], p1, ...)
subresult2 = self.divide_conquer(subproblems[1], p1, ...)
subresult3 = self.divide_conquer(subproblems[2], p1, ...)
…
# 合并子结果(合并结果)
result = process_result(subresult1, subresult2, subresult3, …)
# 清理当前层
HashMap 是一个散列表,它存储的内容是键值对(key-value)映射。
HashMap 继承于AbstractMap,实现了Map、Cloneable、java.io.Serializable接口。
HashMap 的实现不是同步的,这意味着它不是线程安全的。
openjdk-10_src
put(K key, V value)
public V put(K key, V value) {
// 对key的hashCode()做hash
return putVal(hash(key), key, value, false, true);
}
hash(Object key)
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); //对key的hashCode()做hash
}
putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict)
put函数大致的思路为:
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict) {
Node<K,V>[] tab; //创建table的索引
Node<K,V> p; //p是散列到位置的元素
int n, i;
// tab为空则创建 注意 resize() 函数
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
// 计算index,并对null做处理
if ((p = tab[i = (n - 1) & hash]) == null)
//创建新的结点
tab[i] = newNode(hash, key, value, null);
else {
// Node结点e,用来遍历链表或者数
Node<K,V> e; K k;
// 节点存在
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
// 该节点链为树(红黑树)
else if (p instanceof TreeNode)
// 就是往树种加元素
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {
//循环链表
for (int binCount = 0; ; ++binCount) {
//将新元素加入链表尾
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
// 当链表长度 大于 TREEIFY_THRESHOLD - 1 进行树化,把链表转化为红黑树
// static final int TREEIFY_THRESHOLD = 8;
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
// //如果存在的元素值和插入的元素值相同,那就直接退出
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
// //下标移动
p = e;
}
}
// e不为空,e已经添加到表中
if (e != null) { // existing mapping for key
// 记录下e的value的值
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
// 节点访问后处理
afterNodeAccess(e);
// /返回旧值
return oldValue;
}
}
//修改次数++
++modCount;
// 添加了一个元素后如果比阈值大就要扩容
if (++size > threshold)
resize();
// 节点插入后
afterNodeInsertion(evict);
return null;
}
openjdk-10_src
public V get(Object key) {
Node<K,V> e;
return (e = getNode(hash(key), key)) == null ? null : e.value;
}
get函数大致的思路为:
final Node<K,V> getNode(int hash, Object key) {
Node<K,V>[] tab;
Node<K,V> first, e;
int n; K k;
// table!=null,也就是说数组内是有记录的,否则直接null;
if ((tab = table) != null && (n = tab.length) > 0 &&
(first = tab[(n - 1) & hash]) != null) {
// //如果头结点就直接是我们要找的数据就直接返回
if (first.hash == hash && // always check first node
((k = first.key) == key || (key != null && key.equals(k))))
return first;
if ((e = first.next) != null) {
//如果是红黑树,就用树的查询方式
if (first instanceof TreeNode)
return ((TreeNode<K,V>)first).getTreeNode(hash, key);
do { // 链表查询
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
return e;
} while ((e = e.next) != null);
}
}
return null;
}