package com.interview.basics.model.tree;
import java.util.ArrayList;
import java.util.List;
public class BinarySearchTree<T extends Comparable> extends BinaryTree<T> {
public BinarySearchTree(T[] values) {
for(T element : values) insert(element);
}
public BinarySearchTree(BinaryTreeNode root) { super(root);}
public BinarySearchTree(){
}
@Override
public void insert(T element){
root = insert(root, element);
}
protected BinaryTreeNode<T> insert(BinaryTreeNode<T> node, T element){
if(node == null) return new BinaryTreeNode<>(element);
int cmp = node.value.compareTo(element);
if(cmp == 0) node.count++;
else if(cmp > 0) node.setLeft(insert(node.left, element));
else node.setRight(insert(node.right, element));
node.size++;
return node;
}
public BinaryTreeNode search(T value){
return search(root, value);
}
protected BinaryTreeNode<T> search(BinaryTreeNode<T> node, T value){
if(node == null) return null;
int cmp = node.value.compareTo(value);
if(cmp == 0) return node;
else if(cmp > 0) return search(node.left, value);
else return search(node.right, value);
}
public List<T> searchRange(T key1, T key2){
List<T> elements = new ArrayList<>();
if(root == null) return elements;
if(key1.compareTo(key2) < 0) return searchRange(key2, key1);
searchRange(root, key1, key2, elements);
return elements;
}
private void searchRange(BinaryTreeNode<T> node, T key1, T key2, List<T> elements){
if(node == null) return;
if(key1.compareTo(node.value) <= 0) searchRange(node.left, key1, key2, elements);
if(node.value.compareTo(key1) >= 0 && node.value.compareTo(key2) <= 0) elements.add(node.value);
if(key2.compareTo(node.value) >= 0) searchRange(node.right, key1, key2, elements);
}
public BinaryTreeNode<T> max(){
return root == null? null : max(this.root);
}
protected BinaryTreeNode<T> max(BinaryTreeNode<T> node){
while(node.right != null) node = node.right;
return node;
}
public BinaryTreeNode<T> min() {
return root == null? null : min(this.root);
}
protected BinaryTreeNode<T> min(BinaryTreeNode<T> node){
while(node.left != null) node = node.left;
return node;
}
public BinaryTreeNode<T> successor(BinaryTreeNode<T> node) {
if(node == null) return null;
if(node.right != null) return min(node.right);
BinaryTreeNode<T> parent = node.parent;
while(parent != null && parent.right == node) {
node = parent;
parent = node.parent;
}
return parent;
}
public BinaryTreeNode<T> predecessor(BinaryTreeNode node) {
if(node == null) return null;
if(node.left != null) return max(node.left);
BinaryTreeNode<T> parent = node.parent;
while(parent != null && parent.left == node){
node = parent;
parent = node.parent;
}
return parent;
}
public int rank(T element) {
return rank(root, element);
}
protected int rank(BinaryTreeNode<T> node, T element){
if(node == null) return 0;
int cmp = node.value.compareTo(element);
if(cmp == 0) return node.left == null? 0 : node.left.size;
else if(cmp > 0) return rank(node.left, element);
else return rank(node.right, element) + (node.left == null? 0 : node.left.size) + node.count;
}
public BinaryTreeNode<T> select(int k){
return select(this.root, k);
}
protected BinaryTreeNode<T> select(BinaryTreeNode<T> node, int k) {
if(node == null || k > node.size) return null;
int left = node.left == null ? 0 : node.left.size;
if(k > left && k - left <= node.count) return node;
else if (k <= left) return select(node.left, k);
else return select(node.right, k - left - node.count);
}
public BinaryTreeNode<T> floor(T k){
return floor(this.root, k);
}
protected BinaryTreeNode<T> floor(BinaryTreeNode<T> node, T k) {
if(node == null) return null;
int cmp = node.value.compareTo(k);
if(cmp == 0) return node;
else if(cmp > 0) return floor(node.left, k);
else {
BinaryTreeNode<T> floor = floor(node.right, k);
return floor != null? floor : node;
}
}
public BinaryTreeNode<T> ceil(T k){
return ceil(this.root, k);
}
protected BinaryTreeNode<T> ceil(BinaryTreeNode<T> node, T k){
if(node == null) return null;
int cmp = node.value.compareTo(k);
if(cmp == 0) return node;
else if(cmp < 0) return ceil(node.right, k);
else {
BinaryTreeNode<T> ceil = ceil(node.left, k);
return ceil != null? ceil : node;
}
}
public void deleteMin(){
root = deleteMin(this.root);
}
protected BinaryTreeNode<T> deleteMin(BinaryTreeNode<T> node){
if(node.left == null) {
if(node.count == 1) return node.right;
else node.count--;
} else {
node.setLeft(deleteMin(node.left));
}
node.size--;
return node;
}
public void deleteMax(){
root = deleteMax(this.root);
}
protected BinaryTreeNode<T> deleteMax(BinaryTreeNode<T> node){
if(node.right == null){
if(node.count == 1) return node.left;
else node.count--;
} else {
node.setRight(deleteMax(node.right));
}
node.size--;
return node;
}
public void delete(T element){
delete(this.root, element);
}
protected BinaryTreeNode<T> delete(BinaryTreeNode<T> node, T element){
if(node == null) return null;
int cmp = node.value.compareTo(element);
if(cmp > 0) node.setLeft(delete(node.left, element));
else if(cmp < 0) node.setRight(delete(node.right, element));
else {
if(node.count > 1) node.count--;
else {
if(node.left == null) return node.right;
if(node.right == null) return node.left;
BinaryTreeNode<T> rightMin = min(node.right);
rightMin.setRight(deleteMin(node.right));
rightMin.setLeft(node.left);
return rightMin;
}
}
node.size--;
return node;
}
}