二叉查找(排序)树的分析与实现

二叉排序树(Binary Sort Tree)又称二叉查找树(Binary Search Tree),亦称二叉搜索树

图from baike

二叉排序树或者是一棵空树,或者是具有下列性质的二叉树

(1)若左子树不空,则左子树上所有结点的值均小于它的根结点的值;

(2)若右子树不空,则右子树上所有结点的值均大于或等于它的根结点的值;

(3)左、右子树也分别为二叉排序树;

(4)没有键值相等的节点。

步骤:
若根结点的关键字值等于查找的关键字,成功。
否则,若小于根结点的关键字值,递归查左子树。
若大于根结点的关键字值,递归查右子树。
若子树为空,查找不成功。
平均情况分析(在成功查找两种的情况下):
在一般情况下,设 P(n,i)为它的左子树的结点个数为 i 时的平均查找长度。如图的结点个数为 n = 6 且 i = 3; 则 P(n,i)= P(6, 3) = [ 1+ ( P(3) + 1) * 3 + ( P(2) + 1) * 2 ] / 6= [ 1+ ( 5/3 + 1) * 3 + ( 3/2 + 1) * 2 ] / 6
注意:这里 P(3)、P(2) 是具有 3 个结点、2 个结点的二叉分类树的平均查找长度。 在一般情况,P(i)为具有 i 个结点二叉分类树的平均查找长度。
P(3) = (1+2+2)/ 3 = 5/3
P(2) = (1+2)/ 2 = 3/2∴ P(n,i)= [ 1+ ( P(i) + 1) * i + ( P(n-i-1) + 1) * (n-i-1) ] / n
∴ P(n)= P(n,i)/ n <= 2(1+I/n)lnn
因为 2(1+I/n)lnn≈1.38logn 故P(n)=O(logn)

与次优二叉树相对,二叉排序树是一种动态树表。其特点是:树的结构通常不是一次生成的,而是在查找过程中,当树中不存在关键字等于给定值的节点时再进行插入。新插入的结点一定是一个新添加的叶子节点,并且是查找不成功时查找路径上访问的最后一个结点的左孩子或右孩子结点。

插入算法

首先执行查找算法,找出被插结点的父亲结点。

判断被插结点是其父亲结点的左、右儿子。将被插结点作为叶子结点插入。

二叉树为空。则首先单独生成根结点。

注意:新插入的结点总是叶子结点。

删除结点

在二叉排序树删去一个结点,分三种情况讨论:

  1. 若p结点为叶子结点,即PL(左子树)和PR(右子树)均为空树。由于删去叶子结点不破坏整棵树的结构,则可以直接删除此子结点。
  2. 若p结点只有左子树PL或右子树PR,此时只要令PL或PR直接成为其双亲结点*f的左子树(当p是左子树)或右子树(当p是右子树)即可,作此修改也不破坏二叉排序树的特性。
  3. 若p结点的左子树和右子树均不空。在删去p之后,为保持其它元素之间的相对位置不变,可按中序遍历保持有序进行调整,可以有两种做法:

    其一是令p的左子树为f的左/右(依p是f的左子树还是右子树而定)子树,s为p左子树的最右下的结点,而p的右子树为s的右子树;

    其二是令p的直接前驱(或直接后继)替代p,然后再从二叉排序树中删去它的直接前驱(或直接后继)-即让f的左子树(如果有的话)成为p左子树的最左下结点(如果有的话),再让f成为p的左右结点的父结点。

以下是基于Java语言的实现与测试:

package com.wxisme.binarysearchtree;

import java.util.Scanner;
/**
 * 二叉查找(排序)树的实现
 * @time 2015/8/16 12:02
 * @author wxisme
 * @param <T>
 */
public class BinarySearchTree<T extends Comparable> {

	private class TreeNode {
		private T data;
		private TreeNode left;
		private TreeNode right;
		private TreeNode parent;

		public TreeNode() {}

		public TreeNode(T data) {
			this.data = data;
			this.left = null;
			this.right = null;
			this.parent = null;
		}

	}

	private TreeNode root;
	private int nodeCount;
	private int depth;
	public BinarySearchTree() {}

	public BinarySearchTree(T data) {
		root = new TreeNode(data);
	}
	/**
	 * 添加元素
	 * @param data
	 */
	public void add(T data) {
		TreeNode node = new TreeNode(data);
		if(root == null) {
			root = node;
		}
		else {
			addSearch(node, root);
		}
		nodeCount ++;
	}

	public void addSearch(TreeNode node, TreeNode root) {
		if(node.data.compareTo(root.data)<=0) {
			if(root.left == null) {
				root.left = node;
				node.parent = root;
				return;
			}
			else {
				addSearch(node, root.left);
			}
		}
		else {
			if(root.right == null) {
				root.right = node;
				node.parent = root;
				return;
			}
			else {
				addSearch(node, root.right);
			}
		}
	}
	/**
	 * 查找节点
	 * @param data
	 * @return
	 */
	public TreeNode search(T data) {
		TreeNode it = root;
		if(it == null) {
			System.out.println("树为空!");
			return null;
		}
		else {
			while(it != null) {
				if(data.compareTo(it.data)<0) {
					it = it.left;
				}
				else if(data.compareTo(it.data)>0) {
					it =it.right;
				}
				else {
					return it;
				}

			}
		}
		return null;
	}
	/**
	 * 删除元素
	 * @param data
	 */
	public void remove(T data) {
		TreeNode node = search(data);
		if(node == null) {
			System.out.println("不存在此节点!");
			return ;
		}
		//左右子树均为空
		if(node.left==null && node.right==null) {
			if(root == node) {
				root = null;
			}
			else {
				if(node == node.parent.left) {
					node.parent.left = null;
				}
				else {
					node.parent.right = null;
				}
				node.parent = null;
			}
		}
		//左子树不为空,右子树为空
		else if(node.left!=null && node.right==null) {
			if(root == node) {
				root = node.left;
				root.parent = null;
			}
			else {
				if(node == node.parent.left) {
					node.parent.left = node.left;
					node.left.parent = node.parent;
					node.left = null;
					node.parent = null;
				}
				else {
					node.parent.right = node.left;
					node.left.parent = node.parent;
					node.left = null;
					node.parent = null;
				}
			}
		}
		//右子树不为空,左子树为空
		else if(node.left==null && node.right!=null) {
			if(root == node) {
				root = node.right;
				root.parent = null;
			}
			else {
				if(node == node.parent.left) {
					node.parent.left = node.right;
					node.right.parent = node.parent;
					node.right = null;
					node.parent = null;
				}
				else {
					node.parent.right = node.right;
					node.right.parent = node.parent;
					node.right = null;
					node.parent = null;
				}
			}
		}
		//左右子树均不为空
		else {
			//左子树中值最大的节点
			TreeNode leftMaxNode = node.left;
			while(leftMaxNode.right != null) {
				leftMaxNode = leftMaxNode.right;
			}
			//将左子树中值最大的节点移到要删除的节点的位置上来。
			leftMaxNode.parent.right = null;
			leftMaxNode.parent = node.parent;
			if(node == node.parent.left) {
				node.parent.left = leftMaxNode;
			}
			else {
				node.parent.right = leftMaxNode;
			}
			leftMaxNode.left = node.left;
			leftMaxNode.right = node.right;
			//释放要删除的节点的引用
			node.parent = node.left = node.right = null;
		}
		nodeCount --;
	}
	/**
	 * 节点的个数
	 * @return
	 */
	public int size() {
		return nodeCount;
	}
	/**
	 * 销毁树
	 */
	public void destroy() {
		root = null;
		nodeCount = 0;
	}
	/**
	 * 中序遍历树
	 */
	public void traversal() {
		System.out.print("中序遍历:");
		inOrderTraversal(root);
		System.out.println();
	}
	public void inOrderTraversal(TreeNode root) {
		if(root == null) {
			return ;
		}
		inOrderTraversal(root.left);
		System.out.print(root.data);
		inOrderTraversal(root.right);
	}
	/**
	 * 测试
	 * @param args
	 */
	public static void main(String[] args) {
		BinarySearchTree<Integer> tree = new BinarySearchTree<Integer>();
		Scanner scan = new Scanner(System.in);
		for(int i=0; i<10; i++) {
			tree.add(scan.nextInt());
		}
		tree.traversal();
		System.out.print("删除节点:");
		tree.remove(scan.nextInt());
		tree.traversal();
		System.out.println("节点的个数:" + tree.size());
		System.out.println("销毁树");
		tree.destroy();
		System.out.println("节点的个数:" + tree.size());
	}
}

测试结果:

5 9 6 3 2 8 7 4 1 0
中序遍历:0123456789
删除节点:7
中序遍历:012345689
节点的个数:9
销毁树
节点的个数:0

时间: 2024-07-31 12:48:36

二叉查找(排序)树的分析与实现的相关文章

树&#183;二叉查找树ADT(二叉搜索树/排序树)

1.定义 对于每个节点X,它的左子树中所有的项的值小于X的值,右子树所有项的值大于X的值. 如图:任意一个节点,都满足定义,其左子树的所有值小于它,右子树的所有值大于它. 2.平均深度 在大O模型中,二叉查找树的平均深度是O(logN) . 证明:查找某个节点x的算法深度,即从根出发找到节点x的路径长.所有查找的平均深度,就是平均内部路径长. 假设二叉查找树共N个节点,假设左子树有i个节点,则右子树节点数目:N-i-1. 假设D(N)表示具有N个基点的内部路径长.则N个节点的树的内部路径长:D(

二叉平衡排序树的基本操作

//定义数据类型   #define  MAXSIZE = 100; #define OK  1; #define ERROR 0; typedef int status; typedef int ElemType; //平衡二叉排序树的结构 typedef struct Tnode { ElemType data; struct Tnode *lchild,*rchild; int height;          //以该节点为根的子树的高度 }BBTnode,*BBTree; typede

二叉 查找树 排序树 搜索树

二叉查找树 _ 二叉排序树 _ 二叉搜索树_C++ 一.数据结构背景+代码变量介绍 二叉查找树,又名二叉排序树,亦名二叉搜索树 它满足以下定义: 1.任意节点的子树又是一颗二叉查找树,且左子树的每个节点均小于该节点,右子树的每个节点均大于该节点. 2.由1可推出,任意节点的左孩子小于该节点,右孩子大于该节点 以上讨论的是左(右)孩子(子树)存在的情况 它的中序遍历是一个升序的排序 在参考代码中,我们定义有: 主程序中,k代表插入或删除或查找的节点的值 root,根节点位置:a[i],第 i 号节

从零开始: 二叉查找

1.二叉树 二叉树(Binary Tree)的特点是每个结点至多具有两棵子树(即在二叉树中不存在度大于2的结点),并且子树之间有左右之分. 二叉树的性质: (1).在二叉树的第i层上至多有2i-1个结点(i≥1). (2).深度为k的二叉树至多有2k-1个结点(k≥1). (3).对任何一棵二叉树,如果其终端结点数为n0,度为2的结点数为n2,则n0=n2+1. 一棵深度为k且有2k-1个结点的二叉树称为满二叉树. 可以对满二叉树的结点进行连续编号,约定编号从根结点起,自上而下,自左至右,则由此

平衡二叉搜索树(AVL树,红黑树)数据结构和区别

平衡二叉搜索树(Balanced Binary Search Tree) 经典常见的自平衡的二叉搜索树(Self-balancing Binary Search Tree)有 ① AVL树 :Windows NT 内核中广泛使用 ② 红黑树:C++ STL(比如 map.set )Java 的 TreeMap.TreeSet.HashMap.HashSet  Linux 的进程调度  Ngix 的 timer 管理 1 AVL树  vs  红黑树 ①AVL树 平衡标准比较严格:每个左右子树的高度

第95题:不同的二叉搜素树II

一. 问题描述 给定一个整数 n,生成所有由 1 ... n 为节点所组成的二叉搜索树. 示例: 输入: 3 输出: [ [1,null,3,2], [3,2,null,1], [3,1,null,null,2], [2,1,3], [1,null,2,null,3] ] 解释: 以上的输出对应以下 5 种不同结构的二叉搜索树: 1         3     3      2      1 \       /     /      / \      \ 3     2     1      1

自平衡二叉(查找树/搜索树/排序树) binary search tree

在计算机科学中,AVL树是最先发明的自平衡二叉查找树.AVL树得名于它的发明者 G.M. Adelson-Velsky 和 E.M. Landis,他们在 1962 年的论文 "An algorithm for the organization of information" 中发表了它. 一.AVL树的旋转规律 AVL树的基本操作一般涉及运做同在不平衡的二叉查找树所运做的同样的算法.但是要进行预先或随后做一次或多次所谓的"AVL旋转". 假设由于在二叉排序树上插入

数据结构——二叉查找(排序)树

二叉排序树又称二叉查找树,它可以是一棵空树,若非空时具有以下性质: 若根结点的左子树非空,则左子树上的所有结点的关键字均小于等于根节点的关键字值: 若根结点的右子树非空,则右子树上所有的结点的关键字均大于等于根节点的关键字值: 根结点的左.右子树也是二叉排序树: 什么是前驱和后继: 结点的前驱:是该结点的左子树中的最大结点: 结点的后继:是该结点的右子树中的最小结点: 也就是在中序遍历中,结点前.后的结点,就是结点的前驱和结点的后继. 其它请看这篇精彩博文,不再重复造轮子.

洛谷P2015 二叉苹果树(树状dp)

题目描述 有一棵苹果树,如果树枝有分叉,一定是分2叉(就是说没有只有1个儿子的结点) 这棵树共有N个结点(叶子点或者树枝分叉点),编号为1-N,树根编号一定是1. 我们用一根树枝两端连接的结点的编号来描述一根树枝的位置.下面是一颗有4个树枝的树 2 5 \ / 3 4 \ / 1 现在这颗树枝条太多了,需要剪枝.但是一些树枝上长有苹果. 给定需要保留的树枝数量,求出最多能留住多少苹果. 输入输出格式 输入格式: 第1行2个数,N和Q(1<=Q<= N,1<N<=100). N表示树