1.二叉树
- 二叉树是每个节点最多有两个子树的树结构
- 满二叉树:除叶子节点外,所有节点的度都为2
完全二叉树:叶子结点只能出现在最下两层;最下层的叶子一定集中在左部连续位置;倒数二层,若有叶子结点,一定都在右部连续位置;如果结点度为1 ,则该结点只有左孩子,即不存在只有右子树的情况;同样结点数的二叉树,完全二叉树的深度最小。
哈夫曼树是一种带权路径长度最短的二叉树,也称为最优二叉树
2.前中序遍历的代码实现(递归与非递归)
//创建二叉树(前序遍历)定义字符串
int index = 0;
public Node init(String s) {
if (index >= s.length()) {
return null;
}
char ch = s.charAt(index++);
if (ch == '#') {
return null;
}
Node node = new Node(ch);
node.setLchild(init(s));
node.setRchild(init(s));
this.root = node;
return root;
}
//递归前序遍历
public void preOrderTraverse(Node root) {
if (root == null) {
return;
}
System.out.print(root.getData()+" ");
preOrderTraverse(root.getLchild());
preOrderTraverse(root.getRchild());
}
//递归中序遍历
public void inOrderTraverse(Node root) {
if (root == null ) {
return;
}
inOrderTraverse(root.getLchild());
System.out.print(root.getData()+" ");
inOrderTraverse(root.getRchild());
}
//递归后序遍历
public void postOrderTraverse(Node root) {
if (root == null) {
return;
}
postOrderTraverse(root.getLchild());
postOrderTraverse(root.getRchild());
System.out.print(root.getData()+" ");
}
//非递归方式前序遍历
public void preOrder(Node root) {
Stack<Node> stack = new Stack<>();
while (root != null || !stack.isEmpty()) {
while (root != null) {
System.out.print(root.getData()+" ");
stack.push(root);
root = root .getLchild();
}
if (!stack.isEmpty()) {
root = stack.pop();
root = root.getRchild();
}
}
}
//非递归中序遍历
public void inOrder(Node root) {
Stack<Node> stack = new Stack<>();
while (root != null || !stack.isEmpty()) {
while (root != null) {
stack.push(root);
root = root.getLchild();
}
if (!stack.isEmpty()) {
root = stack.pop();
System.out.print(root.getData()+" ");
root = root.getRchild();
}
}
}
//非递归后序遍历一
public void postOrder(Node root){
Node preNode = null;//记录之前遍历的右结点
Stack<Node> stack = new Stack<Node>();
while(root != null || !stack.isEmpty()){
//左子树一直入栈
while(root != null){
stack.push(root);
root = root.getLchild();
}
if (!stack.isEmpty()) {
root = stack.peek();//获得栈顶节点但不出栈
//如果右结点为空,或者右结点之前遍历过,打印根结点
if (root.getRchild() == null || root.getRchild() == preNode) {
System.out.print(root.getData() + " ");
root = stack.pop();
preNode = root;
root = null;
} else {
root = root.getRchild();
}
}
}
}
//非递归遍历二
public void postOrder2(Node root) {
Node pre = null;
Stack<Node> stack = new Stack<>();
stack.push(root);
while (!stack.isEmpty()) {
root = stack.peek();
if ((root.getLchild() == null && root.getRchild() == null) ||
((pre != null)&&(root.getRchild() == pre || root.getLchild() == pre))) {
System.out.print(root.getData()+" ");
pre = stack.pop();
}else {
if (root.getRchild() != null) {
stack.push(root.getRchild());
}
if (root.getLchild() != null) {
stack.push(root.getLchild());
}
}
}
}
3.AVL树
平衡二叉树是一种高度平衡的二叉排序树(父节点大于左子树、小于右子树),它的每一个节点的左子树和右子树的高度差至多等于1 ,二叉树上结点的左子树深度减去右子树深度的值称为平衡因子BF ,取值为-1,0,或1。最小不平衡子树:距离插入结点最近的,且平衡因子的绝对值大于1 的结点为根的子树。
单左旋与单右旋代码实现
//单左旋
public Node leftRotation(Node root) {
Node p = root.right;
root.right = p.left;
p.left = root;
return p;
}
//单右旋
public Node rightRotation(Node root) {
Node p = root.left;
root.left = p.right;
p.right = root;
return p;
}
4.B 树与 B+树
B树又称为多路平衡查找树,它是一个节点可以拥有多于2个子节点的二叉查找树。可以保持数据排序,它能够存储数据、对其进行排序并允许以O(log n)的时间复杂度运行进行查找、顺序读取、插入和删除的数据结构。运用在数据库和文件系统。
m阶B 树的性质:树中每个结点最多含有m个孩子(m>=2);若根结点不是叶子结点,则至少有2个孩子;除根结点和叶子结点外,其它每个结点至少有[ceil(m / 2)]个孩子;所有叶子结点都出现在同一层,叶子结点不包含任何关键字信息;每个非终端结点中包含有n个关键字信息,并且以升序排列。
B+树与B树区别
- B+树中有n棵子树的结点中含有n个关键字,而B 树是n棵子树有n-1个关键字
- B+树所有的叶子结点中包含了全部关键字的信息,及指向含有这些关键字记录的指针,所有的叶子结点和相连的节点使用链表按从小到大的顺序相连,便于区间查找和遍历。而B 树的叶子节点并没有包括全部需要查找的信息。
- B+树所有的非终端结点可以看成是索引部分,结点中仅含有其子树根结点中最大(或最小)关键字。 (而B 树的非终节点也包含需要查找的有效信息)
- B+树的叶子结点都是相链的,因此对整棵树的便利只需要一次线性遍历叶子结点即可。而B树则需要进行每一层的递归遍历。相邻的元素可能在内存中不相邻,所以缓存命中性没有B+树好。
- B+树在内部节点上不包含数据信息,因此在内存页中能够存放更多的key。 数据存放的更加紧密,具有更好的空间局部性。因此访问叶子几点上关联的数据也具有更好的缓存命中率。B树的优点:由于B树的每一个节点都包含key和value,因此经常访问的元素可能离根节点更近,因此访问也更迅速
B*树在B+ 树的非根和非叶子结点再增加指向兄弟的指针
5.红黑树
红黑树是2-3树的一种简单高效的实现,他巧妙地使用颜色标记来替代2-3树中比较难处理的3-node节点问题。统计性能要好于AV树。红黑树的查找,插入(三种情况)和删除(四种情况)都是O(logN)的,原因就是整个红黑树的高度是logN,查找从根到叶,走过的路径是树的高度
红黑树的性质:节点是红色或黑色。根节点是黑色。所有叶子节点都是黑色(叶子是NIL节点)。每个红色节点的子节点和父节点都是黑色。从任一节点到其每个叶子的所有简单路径 都包含相同数目的黑色节点。
RBT的左旋与右旋
//左旋转
private Node RotateLeft(Node root)
{
Node p = root.Right;
//将p的左节点复制给root右节点
root.Right = p.Left;
//将root复制给p右节点
p.Left = root;
p.Color = root.Color;
root.Color = RED;
return p;
}
//右旋转
private Node RotateRight(Node root)
{
Node p = root.Left;
root.Left = p.Right;
p.Right = root;
p.Color = root.Color;
root.Color = RED;
return p;
}
6.hash冲突
散列技术是在记录的存储位置和它的关键字之间建立一个确定的对应关系f,使得每个关键字key 对应一个存储位置f (key),对应关系f 称为散列函数, 将记录存储在一块连续的存储空间中,这块连续存储空间称为散列表
散列函数的构造方法:直接定址法、数字分析法、平方取中法、折叠法、随机数法、除留取余法。
处理hash冲突的方法:开放定址法、再散列函数法、公共溢出区法、链地址法。
hash算法:加、乘、除、位运算、查表、混合。
本人才疏学浅,若有错,请指出,谢谢!
如果你有更好的建议,可以留言我们一起讨论,共同进步!
衷心的感谢您能耐心的读完本篇博文!