BST
二叉排序树是一种非常简单的排序树(或者说查找树)
包括两种操作
添加
添加的元素永远是叶子节点
删除
- 叶子节点,直接删除
- 非叶子节点
2.1. 只有左子树或者只有右子树,直接用左子树或者右子树代替待删除节点
2.2. 如果左右子树都存在,则将右子树接到左子树的最右边节点的右子树下,这样就依然保持了有序
import java.util.*;
/* * 二分法查找 */
public class BST {
public static Node root = null;
public static void main(String[] args)
{
System.out.println("Hello World!");
int[] values = createValue();
//int[] values = {10,8,13,4,14,2,6,1,3,5,7};
//int[] values = {1};
//int[] values = {10,8,4,2,6,1,3,5,7};
System.out.println(Arrays.toString(values));
createBST(values);
printBST(root);
System.out.println();
System.out.print("Pls. select a operation(s for search, a for add, d for delete, q for quit,p for print):");
Scanner scan = new Scanner(System.in);
String str = scan.next();
char select = str.charAt(0);
while(true){
if(select == 'q'){
break;
}
int key = 0;
switch (select)
{
case 'a':
System.out.println();
System.out.print("(a)Input a integer:");
key = scan.nextInt();
addNode(key);
printBST(root);
break;
case 'd':
System.out.println();
System.out.print("(d)Input a integer:");
key = scan.nextInt();
deleteNode(key);
printBST(root);
break;
case 'p':
printBST(root);
break;
case 's':
System.out.println();
System.out.print("(s)Input a integer:");
key = scan.nextInt();
if(search(key)){
System.out.println(key + "存在!");
}else{
System.out.println(key + "不存在!");
}
break;
}
System.out.println();
System.out.print("Pls. select a operation(s for search, a for add, d for delete, q for quit,p for print):");
str = scan.next();
select = str.charAt(0);
}
}
/* * 产生随机的整数数组 */
public static int[] createValue(){
int size = new Random().nextInt(20);
while(size == 0){
size = new Random().nextInt(20);
}
int[] values = new int[size];
for(int i = 0; i < size ; i ++ ){
values[i] = new Random().nextInt(20000);
}
return values;
}
/* * 构造BST * */
public static void createBST(int[] value){
for(int i = 0; i < value.length; i ++){
addNode(value[i]);
}
}
/* * 中序遍历BST */
public static void printBST(Node p){
if(p == null){
return;
}else{
printBST(p.left);
System.out.print(p.key+",");
printBST(p.right);
}
//System.out.println();
}
/* * 查找 */
public static boolean search(int key){
Node p = root;
while(p != null){
if(p.key == key){
return true;
}else if(key < p.key){
p = p.left;
}else{
p = p.right;
}
}
return false;
}
/* * 添加节点 */
public static boolean addNode(int key){
//BST 是空树
if(root == null){
root = new Node(key);
}else{
Node p = root;
while(true){
int tmp = p.key;
if(key == tmp ){
return false;
}else if(key < p.key){
if(p.left == null){
Node q = new Node(key);
p.left = q;
return true;
}else{
p = p.left;
}
}else{
if(p.right == null){
Node q = new Node(key);
p.right = q;
return true;
}else{
p = p.right;
}
}
}
}
return true;
}
/* * 删除节点 */
public static boolean deleteNode(int key){
Node q = null;
Node p = root;
//标识当前节点是父节点的左节点(true)
//还是父节点的右节点(false)
boolean flag = false;
while(p != null){
System.out.println("p.key:"+p.key);
if(p.key == key){
//p是叶子节点
if(p.left == null && p.right == null){
if(q == null){
root = null;
}else{
if(flag){
q.left = null;
}else{
q.right = null;
}
}
}
//p的左子树或右子树是空
else if(p.left == null || p.right == null){
if(q == null){
root = (p.left == null ? p.right: p.left);
}else{
if(flag){
q.left = (p.left == null ? p.right: p.left);
}else{
q.right = (p.left == null ? p.right: p.left);
}
}
}
//左右子树都不为空
else{
Node k = p.left;
while(k.right != null){
k = k.right;
}
if(q == null){
k.right = p.right;
root = p.left;
}else{
k.right = p.right;
q.left = p.left;
}
}
return true;
}else if(key < p.key){
q = p;
p = p.left;
flag = true;
}else{
q = p;
p = p.right;
flag = false;
}
}
return false;
}
}
AVL
这是一种比BST高效的平衡二叉树,
高度为 h 的 AVL 树,节点数 N 最多2^h − 1; 最少N(h)=N(h− 1) +N(h− 2) + 1。
查找、插入和删除在平均和最坏情况下都是O(log n)
主要是几个旋转操作
左旋,右旋,双向旋转(先左后右,先右后左)
import java.util.*;
/* * 自平衡二叉查找树 */
public class AVL {
private static Node root = null;
private static int count = 0;
public static void main(String[] args)
{
System.out.println("Hello World!");
int[] values = createData();
System.out.println("原始数组:" + Arrays.toString(values));
createAVL(values);
printAVL();
//System.out.println(height(root.left));
//System.out.println(height(root.right));
}
/* * 创建测试数据 */
public static int[] createData(){
int size = new Random().nextInt(20);
while(size == 0){
size = new Random().nextInt(20);
}
int[] values = new int[size];
for(int i = 0; i < size; i ++){
values[i] = new Random().nextInt(100);
}
return values;
}
/* * 构造树 */
public static void createAVL(int[] values){
for(int i = 0; i < values.length; i ++){
addNode(values[i]);
}
}
/* * 添加节点 */
public static void addNode(int key){
Node tmp = new Node(key);
if(root == null){
root = tmp;
}else{
insert(tmp);
balance(tmp);
}
}
/* * 插入节点 */
public static void insert(Node tmp){
Node q = root;
while(true){
if(tmp.key == q.key){
return;
}else if(tmp.key < q.key){
if(q.left == null){
q.left = tmp;
break;
}
q = q.left;
}else{
if(q.right == null){
q.right = tmp;
break;
}
q = q.right;
}
}
}
/* * 平衡节点 * 由于插入tmp节点,树失去了平衡 */
public static void balance(Node tmp){
//p是离插入节点最近的非平衡节点
Node p = null;
//q用来遍历AVL树
Node q = null;
q = root;
//1. 寻找离tmp最近的非平衡点q
while(q != null){
int hL = height(q.left);
int hR = height(q.right);
if(((hL - hR) == 2) || ((hL - hR)== -2)){
//此节点是非平衡点,用p标记
p = q;
}
if(tmp.key < q.key){
q = q.left;
}else if(tmp.key > q.key){
q = q.right;
}else{
q = null;
}
}
if(p == null){
//不存在非平衡点
return;
}
//2. 判断属于哪种情况
//2.1 在p的左孩子的左子树中插入节点-- 右旋
//2.2 在p的左孩子的右子树中插入节点-- 先左再右
//2.3 在p的右孩子的左子树中插入节点-- 先右再左
//2.4 在p的右孩子的右子树中插入节点-- 左旋
if(tmp.key < p.key){
if(tmp.key < p.left.key){
//右旋
singleRotate(p,false);
//System.out.println("平衡之后:");
//printAVL();
}else{
//左-右,先左旋后右旋
//System.out.println("左-右");
doubleRotate(p,true);
}
}else{
if(tmp.key > p.right.key){
//左旋
singleRotate(p,true);
}else{
//右-左
//System.out.println("右-左");
doubleRotate(p,false);
}
}
}
/* * 将以p为根的树单次旋转 */
public static void singleRotate(Node p,boolean flag){
//左旋
if(flag){
Node r = p.right;
Node tmp = Node.copy(p);
tmp.right = r.left;
p.right = r.right;
p.key = r.key;
p.left = tmp;
}
//右旋
else{
Node left = p.left;
Node tmp = Node.copy(p);
tmp.left = left.right;
p.left = left.left;
p.key = left.key;
p.right = tmp;
}
}
/* * 以p为根的双旋 */
public static void doubleRotate(Node p,boolean flag){
//先左旋后右旋
if(flag){
Node q = p.left;
singleRotate(q,true);
singleRotate(p,false);
}
//先右旋后左旋
else{
Node q = p.right;
singleRotate(q,false);
singleRotate(p,true);
}
}
/* * 二叉树的高度 */
public static int height(Node p){
if(p == null){
return 0;
}
int hL = height(p.left);
int hR = height(p.right);
return (hL > hR ? hL: hR) + 1;
}
/* * 按层次输出树,空节点以"N"表示 */
public static void printAVL(){
Queue<Node> queue = new LinkedList<Node>();
System.out.print("[");
if(root != null){
queue.offer(root);
Node p = null;
while(!queue.isEmpty()){
p = queue.poll();
if(p != null){
System.out.print(p.key+",");
if(p.left != null){
queue.offer(p.left);
}else{
queue.offer(null);
}
if(p.right != null){
queue.offer(p.right);
}else{
queue.offer(null);
}
}
else
System.out.print("N,");
}
}
System.out.println("]");
}
}
class Node
{
/* * balanceFactor=H(left) - H(right) */
int key = 0;
Node left;
Node right;
public Node(int key){
this.key = key;
}
public static Node copy(Node p){
Node tmp = new Node(p.key);
tmp.left = p.left;
tmp.right = p.right;
return tmp;
}
}
BTree
平衡多路查找树
import java.util.*;
/* * m阶的B-树,或为空树,或为满足下列特性的m叉树 * 1. 树中每个节点至多有m棵子树(m-1个关键字) * 2. 若根节点不是叶子节点则至少有两棵子树 * 3. 除根之外的所有非终端节点至少有ceil(m/2)棵子树(ceil(m/2)-1个关键字) * 4. 所有的非终端节点中包含下列信息数据 * (n,A0,K1,A1,K2,A2,...,Kn,An) * 其中:Ki(i=1,...,n)为关键字,且Ki<Ki+1(i=1,...,n-1); * Ai(i=0,...,n)为指向子树根节点的指针, * 且指针Ai-1所指子树中所有节点的关键字均小于Ki(i=1,...,n) * An所指子树中所有节点的关键字均大于Kn,n(ceil(m/2)-1 <= n <= m-1)为关键字的个数 * 5. 所有的叶子节点都出现在同一层次上,并且不带信息(可以看做是外部节 * 点或查找失败的节点,实际上这些节点不存在,指向这些节点的指针为空) * * 分裂时,将(关键字-1)一分为二,然后将中间的放到父节点中 * 分裂函数写的时候,要注意父节点指针的更改 */
public class BTree {
//表示这棵BTree是m阶,每个节点最多M棵子树,M-1个关键字
private static final int M;
//每个节点至少MIN_KEY_NUM个子树,MIN_KEY_NUM - 1 个关键字
private static final int MIN_KEY_NUM;
private static BTreeNode root;
static{
M = 3;
//注意是2.0,而不是2
MIN_KEY_NUM = (int)Math.ceil(M/2.0);
}
public static void main(String[] args)
{
System.out.println("Hello World!");
int count = new Random().nextInt(10);
while(count == 0){
count = new Random().nextInt(10);
}
int[] arr = new int[count];
for(int i = 0; i < count ; i ++){
arr[i] = new Random().nextInt(100);
}
int[] arr1 = {62,65,92};
System.out.println(Arrays.toString(arr));
createBTree(arr);
printBTree();
System.out.println("============删除===============");
while(count > 0){
int index = new Random().nextInt(count);
System.out.println("删除:" + arr[index]);
delete(arr[index]);
printBTree();
for(int i = 0; i < count ; i ++){
if(i > index){
arr[i - 1] = arr[i];
}
}
count--;
}
}
public static void createBTree(int[] arr){
for(int i = 0; i < arr.length ; i ++){
//System.out.println(i);
add(arr[i]);
}
}
public static void add(int key){
//根为空
if(root == null){
root = new BTreeNode();
root.keys[++root.keyNum] = key;
root.parrent = null;
return;
}
BTreeNode p = root;
label:
while(true){
//插入排序
int i = 0;
for(i = p.keyNum; i >= 1 ; i--){
//找寻插入点
if(key > p.keys[i]){
break;
}
if(key == p.keys[i]){
return;
}
}
if(p.childs[i] == null){
//插入点为i+1
for(int j = p.keyNum ; j >= (i + 1); j--){
p.keys[j + 1] = p.keys[j];
}
p.keys[i + 1] = key;
p.keyNum++;
break label;
}else{
p = p.childs[i];
}
}
//将key插入p之后,判断p节点是否符合BTree的条件
//每个节点的关键字最多M-1个
if(p.keyNum > M - 1){
//将p节点分裂
split(p);
}
}
private static void split(BTreeNode p){
BTreeNode lt = new BTreeNode();
BTreeNode gt = new BTreeNode();
//分裂出来的左节点
/* * M = 3 * MIN_KEY_NUM = 2 * keys: * ----------------- * |0 |1 |2 |3 | * ----------------- * childs: * ----------------- * |0 |1 |2 |3 | * ----------------- * index = M 的值作为备用 */
System.arraycopy(p.keys,1,lt.keys,1,MIN_KEY_NUM - 1);
System.arraycopy(p.childs,0,lt.childs,0,MIN_KEY_NUM);
lt.keyNum = MIN_KEY_NUM - 1;
//System.out.println(p.keys[MIN_KEY_NUM] + ":" + Arrays.toString(lt.keys));
System.arraycopy(p.keys,MIN_KEY_NUM + 1,gt.keys,1,M - MIN_KEY_NUM);
System.arraycopy(p.childs,MIN_KEY_NUM,gt.childs,0,M - MIN_KEY_NUM + 1);
gt.keyNum = M - MIN_KEY_NUM;
//System.out.println(p.keys[MIN_KEY_NUM] + ":" + Arrays.toString(gt.keys));
/* * 分隔节点之后,需要更改父节点信息 */
for(int i = 0 ; i <= lt.keyNum ; i ++){
if(lt.childs[i] != null){
lt.childs[i].parrent = lt;
}
}
for(int i = 0 ; i <= gt.keyNum ; i ++){
if(gt.childs[i] != null){
gt.childs[i].parrent = gt;
}
}
BTreeNode parrent = p.parrent;
//root节点分裂
if(parrent == null){
BTreeNode tmp = new BTreeNode();
tmp.keys[++tmp.keyNum] = p.keys[MIN_KEY_NUM];
tmp.parrent = null;
tmp.childs[tmp.keyNum - 1] = lt;
tmp.childs[tmp.keyNum] = gt;
lt.parrent = tmp;
gt.parrent = tmp;
root = tmp;
}else{
int i = 0;
//System.out.println("parrent:"+Arrays.toString(parrent.keys));
//插入排序
for(i = parrent.keyNum; i >= 1 ; i--){
//找寻插入点
if(p.keys[MIN_KEY_NUM] > parrent.keys[i]){
break;
}
}
//插入点为i+1
for(int j = parrent.keyNum ; j >= (i + 1); j--){
parrent.keys[j + 1] = parrent.keys[j];
parrent.childs[j + 1] = parrent.childs[j];
}
parrent.keys[i + 1] = p.keys[MIN_KEY_NUM];
//System.out.println("parrent:"+Arrays.toString(parrent.keys));
parrent.childs[i] = lt;
parrent.childs[i + 1] = gt;
lt.parrent = parrent;
gt.parrent = parrent;
parrent.keyNum++;
//System.out.println("parrent:"+parrent.keyNum);
//是否继续分裂
if(parrent.keyNum > M - 1){
//将p节点分裂
split(parrent);
}
}
}
/* * 删除节点i中的key * 1. 节点i不是最底层非终端节点 * 将Ai子树上的最小值替换key * 问题变为2 * 2. 节点i是最底层非终端节点 * 2.1 节点i的关键字个数 >= MIN_KEY_NUM,直接删除 * 2.2 节点i的关键字个数 = MIN_KEY_NUM - 1 * 2.2.1 兄弟节点的关键字个数 >= MIN_KEY_NUM,将右兄弟最小值(左兄弟最大值)上移至父节点相应位置的数据,然后将父节点被替换的数据下移至被删除节点 * 2.2.2 兄弟节点的关键字个数 = MIN_KEY_NUM - 1,合并(借助父节点) * */
public static void delete(int key){
//System.out.println("deletekey:" + key);
//遍历BTree,查找key所在的节点
BTreeNode p = root;
int index = 0;
label:
while(p != null){
//System.out.println("p.keyNum:" + p.keyNum);
//System.out.println("p:" + Arrays.toString(p.keys));
for(int i = p.keyNum; i >= 1; i --){
if(key == p.keys[i]){
index = i;
break label;
}else if(key > p.keys[i]){
p = p.childs[i];
continue label;
}
}
p = p.childs[0];
}
if(p == null){
return ;
}
//System.out.println("p.keyNum:" + p.keyNum);
//System.out.println("p:" + Arrays.toString(p.keys));
//找到p
if(p.childs[0] == null){
delete(p,key);
}else{
//对于不是最底层的非终端节点,则将别比key小的最大值替换key
BTreeNode q = p.childs[index - 1];
while(q.childs[0] != null){
q = q.childs[q.keyNum];
}
p.keys[index] = q.keys[q.keyNum];
delete(q,q.keys[q.keyNum]);
}
}
public static void delete(BTreeNode p,int key){
if(p.keyNum >= MIN_KEY_NUM){
//删除后节点关键字数满足MIN_KEY_NUM - 1,直接删除
for(int i = 1; i <= p.keyNum; i ++){
if(p.keys[i] > key){
p.keys[i - 1] = p.keys[i];
}
}
p.keys[p.keyNum] = 0;
p.keyNum--;
}else{
//删除后节点关键字数不能够满足MIN_KEY_NUM - 1,可能需要合并
int index = 0;
BTreeNode q = p.parrent;
if(q == null){
//p是root,直接删除,因为root至少两棵子树,所以关键字个数没有限制
for(int i = 1; i <= p.keyNum; i ++){
if(p.keys[i] > key){
p.keys[i - 1] = p.keys[i];
}
}
p.keys[p.keyNum] = 0;
p.keyNum--;
return;
}
for(int i = 0; i <= q.keyNum ; i ++){
if(q.childs[i] == p){
index = i;
break;
}
}
//查询右兄弟并且右兄弟的关键字个数大于MIN_KEY_NUM - 1,
//将父节点中第一个大于key的值移动到删除节点
//将右兄弟中的最小值替换父节点中的第一个大于key的值
if(index < q.keyNum && q.childs[index + 1].keyNum >= MIN_KEY_NUM){
for(int i = 0 ; i <= p.keyNum ; i ++){
if(p.keys[i] > key){
p.keys[i - 1] = p.keys[i];
}
}
p.keys[p.keyNum] = q.keys[index + 1];
q.keys[index + 1] = q.childs[index + 1].keys[1];
for(int i = 2; i <= q.childs[index + 1].keyNum ; i ++){
q.childs[index + 1].keys[i - 1] = q.childs[index + 1].keys[i];
}
q.childs[index + 1].keys[q.childs[index + 1].keyNum] = 0;
q.childs[index + 1].keyNum--;
return;
}
//查询左兄弟并且左兄弟的关键字个数大于MIN_KEY_NUM - 1
//将父节点中最后一个小于key的值移动到删除节点
//将左兄弟中的最大值替换父节点中的最后一个小于key的值
if(index > 0 && q.childs[index - 1].keyNum >= MIN_KEY_NUM){
for(int i = p.keyNum ; i >= 1 ; i --){
if(p.keys[i] < key){
p.keys[i + 1] = p.keys[i];
}
}
p.keys[1] = q.keys[index];
q.keys[index] = q.childs[index - 1].keys[q.childs[index - 1].keyNum];
q.childs[index - 1].keys[q.childs[index - 1].keyNum] = 0;
q.childs[index - 1].keyNum--;
return;
}
//不符合上述条件,则需要有merge操作
//删除key,然后和兄弟合并
for(int i = 1 ; i < p.keyNum; i++){
if(p.keys[i] > key){
p.keys[i - 1] = p.keys[i];
}
}
//将最后一个值赋值为0
p.keys[p.keyNum] = 0;
p.keyNum--;
if(index < q.keyNum){
//将节点与右节点结合
merge(p,true);
}else{
//将节点与左节点结合
merge(p,false);
}
}
}
public static void merge(BTreeNode p,boolean flag){
BTreeNode q = p.parrent;
int index = 0;
for(int i = 0; i <= q.keyNum ; i++){
if(q.childs[i] == p){
index = i;
}
}
if(flag){
//将节点与右节点结合
BTreeNode br = q.childs[index + 1];
//System.out.print("br.keyNum:" + br.keyNum);
//System.out.println("\t\t\t" + Arrays.toString(br.keys));
p.keys[++p.keyNum] = q.keys[index + 1];
for(int i = index + 1; i <= q.keyNum; i++){
q.keys[i] = q.keys[i + 1];
q.childs[i] = q.childs[i + 1];
}
q.keyNum --;
//if(br.keyNum > 0){
System.arraycopy(br.keys,1,p.keys,p.keyNum + 1,br.keyNum);
System.arraycopy(br.childs,0,p.childs,p.keyNum,br.keyNum + 1);
p.keyNum += br.keyNum;
//}
if(q == root){
if(q.keyNum == 0){
root = p;
p.parrent = null;
for(int i = 0; i <= p.keyNum; i ++){
if(p.childs[i] != null){
p.childs[i].parrent = root;
}
}
}
}else{
if(q.keyNum < MIN_KEY_NUM - 1){
BTreeNode q1 = q.parrent;
int index1 = 0;
for(int i = 0; i <= q1.keyNum ; i++){
if(q1.childs[i] == q){
index1 = i;
}
}
if(index1 < q1.keyNum){
merge(q,true);
}else{
merge(q,false);
}
}
}
}else{
//将节点与左节点结合
BTreeNode lr = q.childs[index - 1];
merge(lr,true);
}
}
public static void printBTree(){
Queue<BTreeNode> queue = new LinkedList<BTreeNode>();
queue.offer(root);
while(!queue.isEmpty()){
BTreeNode tmp = queue.poll();
if(tmp != null ){
System.out.print("keyNum:" + tmp.keyNum);
System.out.print("\t\t" + Arrays.toString(tmp.keys));
System.out.println("\t\t\t\t" + tmp.parrent + "------" + tmp);
//System.out.print("\t\t\t\t" + tmp);
//System.out.println("\t\t\t\t\t\t" + tmp.parrent);
for(int i = 0 ; i <= tmp.keyNum; i++){
queue.offer(tmp.childs[i]);
}
}
}
}
static class BTreeNode
{
//指向父节点指针
public BTreeNode parrent;
//本节点的关键字个数
public int keyNum;
//关键字数组,最多M-1个关键字,0号元素不用,1个作为备用
public int[] keys = new int[M + 1];
//子树指针,最多M棵子树
public BTreeNode[] childs = new BTreeNode[M + 1];
}
}
Trie
字典树
import java.util.*;
/* * 字典树 * 1. 统计词频 * 2. 字符串排序 * 3. 最长公共前缀 */
public class Trie {
private TrieNode root;
public Trie() {
root = new TrieNode();
}
public static void main(String[] args)
{
System.out.println("Hello World!");
}
// Inserts a word into the trie.
public void insert(String word) {
TrieNode p = root;
for(int i = 0; i < word.length();i++){
char c = word.charAt(i);
if(p.nodes[c-'a'] == null){
TrieNode tmp = new TrieNode(c);
p.nodes[c - 'a'] = tmp;
p = tmp;
}else{
p = p.nodes[c-'a'];
}
}
p.flag = 1;
}
// Returns if the word is in the trie.
public boolean search(String word) {
TrieNode p = root;
for(int i = 0; i < word.length();i++){
char c = word.charAt(i);
if(p.nodes[c-'a'] == null){
return false;
}else{
p = p.nodes[c-'a'];
}
}
if(p.flag == 1){
return true;
}else{
return false;
}
}
// Returns if there is any word in the trie
// that starts with the given prefix.
public boolean startsWith(String prefix) {
TrieNode p = root;
for(int i = 0; i < prefix.length();i++){
char c = prefix.charAt(i);
if(p.nodes[c-'a'] == null){
return false;
}else{
p = p.nodes[c-'a'];
}
}
return true;
}
}
class TrieNode {
// Initialize your data structure here.
public char key;
public TrieNode[] nodes;
//flag == 1 表示是字符串
//flag == 0 表示是前缀
public int flag;
//public int count;统计词频
public TrieNode() {
key='\0';
nodes = new TrieNode[26];
}
public TrieNode(char c){
key = c;
nodes = new TrieNode[26];
flag = 0;
}
}