二叉查找树(Binary Search Tree)

二叉查找树Binary Search Tree),或者是一棵空树,或者是具有下列性质的二叉树

  1. 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
  2. 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
  3. 它的左、右子树也分别为二叉排序树。

二叉查找树的表示:
class Node {
    int data;
    Node leftChild = null;
    Node rightChild = null;
}

二叉查找树的常用方法:
1. 查找(lookup)
/*
Given a binary tree, return true if a node with the target data is found in the tree. Recurs down the tree, chooses the left or right branch by comparing the target to each node.
*/
static boolean lookup(Node rootNode, int target) {
    // 1. Base case == empty tree
    // in that case, the target is not found so return false
    if (rootNode == null) return false;
    else if (target == rootNode.data) return true;
    else if (target < rootNode.data) return(lookup(rootNode.leftChild, target));
    else return(lookup(rootNode.rightChild, target));
}
2. 最大(max)
	/*
	return the node with maximum key value. It should be last right child of the tree, or the root if the root does not have any child
	*/
	
	public Node maxNode(Node rootNode) {
		while (rootNode.righttChild != null) {
			rootNode = rootNode.rightChild;
		}
		return rootNode;
	}

3. 最小 (min)
	/*
	return the node with minimum key value. It should be last left child of the tree, or the root if the root does not have any child
	*/
	
	public Node minNode(Node rootNode) {
		while (rootNode.leftChild != null) {
			rootNode = rootNode.leftChild;
		}
		return rootNode;
	}

4. 插入(insert)
public static Node insert(Node root, int data) {
	// 1. If the tree is empty, the new node is the root
	if (root == null) {
		return new Node(data);
	}
	else {
		// 2. Otherwise, recur down the tree
		if (data <= root.data) root.leftChild = insert(root.leftChild, data);
		else root.rightChild = insert(root.rightChild, data);
	}
	
	return root;
}


5. in-order tree walk
public void inorder(Node rootNode) {
		if (rootNode != null) {
			inorder(rootNode.leftChild);
			print(nodeNode.data);
			inorder(rootNode.rightChild);
		}
	}

6. successor and predecessor
一个node的successor可以通过in-order walk看出来,因为in-order walk实际上是把二叉查找树做了一个排序。 predecesor 刚好和successor 相反。

Search consists of two cases.
1) If node x has a non-empty right subtree, then x’s successor is the minimum in the right subtree of x.
2) If node x has an empty right subtree, then as long as we move to the left up the tree (move up through right children), we are visiting smaller keys. x’s successor y is the node that x is the predecessor of (x is the maximum in y’s left subtree). In other words, x’s successor y, is the lowest ancestor of x whose left child is also an ancestor of x.
public Node Successor(Node node) {
		if (node.rightChild != null) {
			return minNode(node.rightChild);
		}
		
		Node parentNode = node.parent;
		while (parentNode != null && node == parentNode.rightChild) {
			node = parentNode;
			parentNode = parentNode.parent;
		}
		return parentNode;

	}

If node has two children, its predecessor is the maximum value in its left subtree. If it does not have a left child a node's predecessor is its first left ancestor.
public Node Successor(Node node) {
		if (node.rightChild != null) {
			return minNode(node.rightChild);
		}
		
		Node parentNode = node.parent;
		while (parentNode != null && node == parentNode.leftChild) {
			node = parentNode;
			parentNode = parentNode.parent;
		}
		return parentNode;

	}

7.  删除 (delete)

删除一个node x 需要考虑下面几种情况:
case 1: if x has no children,  then remove x;
case 2: if x has one child, then make p[x] point to child;
case 3: if x has two children (subtrees) , then swap x with its successor, perform case 0 or case 1 to delete it.

伪代码:

delete(x)
    if x.left = NIL and x.right = NIL //case 1
        if x.parent.left = x
           x.parent.left = NIL
        else
           x.parent.right = NIL
    else if x.left = NIL //case 2a
            connect x.parent to x.right
    else if x.right = NIL //case 2b
            connect x.parent to x.left
    else //case 3
            y = successor(x)
	        connect y.parent to y.right
            replace x with y

8. 求节点的个数(size)
/*
Compute the number of nodes in a tree.
*/
int size(Node rootNode) {
      if (node == NULL) {
      return(0);
      } else {
             return(size(rootNode.leftChild) + 1 + size(rootNode.rightChild));
      }
}

9. 把一个二叉树转成它的“镜像”(mirror)
private void mirror(Node rootNode) {
    if (rootNode != null) {
         // do the sub-trees
         mirror(rootNode.leftChild);
         mirror(rootNode.rightChild);
         // swap the left/right pointers
         Node temp = rootNode.leftChild;
         rootNode.leftChild = rootNode.rightChild;
         rootNode.rightChild = temp;
   }
}
10. check balanced (isBalanced)
To check a binary tree is balanced or not, we can simply convert this question into comparing the maximum depth and minimum depth of the BT, if the diffrence between the maximum depth and minimum depth is larger than 1, the tree is not balanced, otherwise, it is balanced.
int maxDepth(Node rootNode) {
      if (rootNode == null) {
          return 0;
      }

      return 1 + max(maxDepth(rootNode.leftChild), maxDepth(rootNode.rightChild));
}

int minDepth(Node rootNode) {
      if (rootNode == null) {
          return 0;
      }

      return 1 + min(minDepth(rootNode.leftChild), minDepth(rootNode.rightChild));
}

bool isBalanced(Node rootNode) {
      return maxDepth(rootNode) -  minDepth(rootNode) <= 1;
}

11. First common ancestor
查找两个node的最早的公共祖先,分三种情况:
1. 如果两个node在root的两边,那么最早的公共祖先就是root。
2. 如果两个node在root的左边,那么把root.leftChild作为root,再递归。
3. 如果两个node在root的右边,那么把root.rightChild作为root,再递归。
     /*
     * get the first common ancestor of node p and node q
     */
    public static Node commonAncestor(Node rootNode, Node p, Node q) {
    	if (covers(rootNode.leftChild, p) && covers(rootNode.leftChild, q))
    		return commonAncestor(rootNode.leftChild, p, q);
    	if (covers(rootNode.rightChild, p) && covers(rootNode.rightChild, q))
    		return commonAncestor(rootNode.rightChild, p, q);
    	return rootNode;
    	
    }
    
    /*
     * check whether the node n is in the tree
     */
    private static boolean covers(Node rootNode, Node n) {
    	if(rootNode == null) return false;
    	if(rootNode == n) return true;
    	return covers(rootNode.leftChild, n) || covers(rootNode.rightChild, n);
    }

如果这个Binary Tree 是 BST的话,可以利用左子树的值小于根节点的值,右子树的值大于根节点的值进行判断两个节点(p, q)的位置,这样更简单。

public Node LCA(Node root, Node p, Node q) {
  if (root == null || p == null || q == null ) return NULL;	
  if (max(p.data, q.data) < root.data)
    return LCA(root.left, p, q);
  else if (min(p.data, q.data) > root.data)
    return LCA(root.right, p, q);
  else
    return root;
}


这个算法的复杂度是O(h)。

12. ISBST()
Given a plain binary tree, examine the tree to determine if it meets the requirement to be a binary search tree. To be a binary search tree, for every node, all of the nodes in its left tree must be <= the node, and all of the nodes in its right subtree must be > the node.
private boolean isBST(Node node) {
      if (node==null) return(true);
      if (node.left!=null && maxValue(node.left) > node.data)  return(false);
      if (node.right!=null && minValue(node.right) <= node.data)  return(false);
       // check that the subtrees themselves are ok
      return( isBST(node.left) && isBST(node.right) );
}
However, the approach given above is very expensive (O(n*h)), because the maxValue(Node node) function will be called n times, and the complexity of the maxValue(Node node) is h (the height of the tree). 

Because of the properity of BST, we can simply pass down the minvalue and maxvalue after narrowing the range of the minvalue and maxvalue, and the complexity of this algorithm is O(n).  The code below shows the algorithm.
bool isBSTHelper(Node p, int low, int high) {
  if (p == null) return true;
  if (low < p.data && p.data < high)
    return isBSTHelper(p.leftChild, low, p.data) &&
           isBSTHelper(p.rightChild, p.data, high);
  else
    return false;
}
 
bool isBST(Node root) {
  return isBSTHelper(root, INT_MIN, INT_MAX);
}



参考: http://cslibrary.stanford.edu/110/BinaryTrees.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
以下是用C语言实现二叉查找树的插入和删除操作,并用中序遍历输出最后的二叉查找树的代码: ```c #include <stdio.h> #include <stdlib.h> struct TreeNode { int val; struct TreeNode *left; struct TreeNode *right; }; // 二叉查找树的插入操作 struct TreeNode* insert(struct TreeNode* root, int val) { if (!root) { // 如果根节点为空,直接新建一个节点 struct TreeNode* node = (struct TreeNode*)malloc(sizeof(struct TreeNode)); node->val = val; node->left = NULL; node->right = NULL; return node; } if (val < root->val) { // 如果要插入的值小于根节点的值,递归插入到左子树中 root->left = insert(root->left, val); } else if (val > root->val) { // 如果要插入的值大于根节点的值,递归插入到右子树中 root->right = insert(root->right, val); } // 如果要插入的值等于根节点的值,什么也不做 return root; } // 二叉查找树的删除操作 struct TreeNode* delete(struct TreeNode* root, int val) { if (!root) { // 如果根节点为空,直接返回 return NULL; } if (val < root->val) { // 如果要删除的值小于根节点的值,递归删除左子树中的对应节点 root->left = delete(root->left, val); } else if (val > root->val) { // 如果要删除的值大于根节点的值,递归删除右子树中的对应节点 root->right = delete(root->right, val); } else { if (!root->left && !root->right) { // 如果要删除的节点没有左子树和右子树,直接删除 free(root); return NULL; } else if (root->left && !root->right) { // 如果要删除的节点只有左子树,将左子树作为新的根节点 struct TreeNode* node = root->left; free(root); return node; } else if (!root->left && root->right) { // 如果要删除的节点只有右子树,将右子树作为新的根节点 struct TreeNode* node = root->right; free(root); return node; } else { // 如果要删除的节点既有左子树又有右子树,找到右子树的最小值节点,将该节点的值赋给要删除的节点,然后递归删除右子树中的这个最小值节点 struct TreeNode* node = root->right; while (node->left) { node = node->left; } root->val = node->val; root->right = delete(root->right, root->val); } } return root; } // 中序遍历输出二叉查找树 void inorder(struct TreeNode* root) { if (!root) { return; } inorder(root->left); printf("%d ", root->val); inorder(root->right); } int main() { struct TreeNode* root = NULL; int nums[] = {5, 2, 4, 6, 1, 3}; int n = sizeof(nums) / sizeof(int); for (int i = 0; i < n; i++) { root = insert(root, nums[i]); } inorder(root); // 中序遍历输出:1 2 3 4 5 6 root = delete(root, 2); printf("\n"); inorder(root); // 中序遍历输出:1 3 4 5 6 return 0; } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值