折半查找与二叉查找树

在生活当中,我们可能每天都要进行查找工作,字典中查找,搜索引擎中查找,数据库中进行查找。在这个信息的时代下,我们每天都要从互联网上接触到很多信息。这些信息从哪里来,当然是保存在数据库中。提到数据库,大家首先想到的肯定是索引,是的数据库的优劣很大是与索引相关。而索引是为了什么,就是为了方便查找,快速从数据库中提取我们想要的数据,对于索引的优化也是一个难点。下面笔者就来介绍一下折半查找和建立二叉查找树进行查找。

折半查找:

大家知道,顺序查找(线性查找)是从线性表中的一端到另一端逐个进行比较,当数据非常大时,查找效率非常低,时间复杂度为O(n),但是它对记录的存储没有任何要求(记录不必有序)。

折半查找:必须要求记录有序,采用顺序存储,利用这个特点,所以折半查找的效率也比顺序查找高,对于数量非常大时,非常快,时间复杂度为O(logN)。

基本思想:(1)在有序数据中,去中间的记录作为比较对象,若给定的值与中间记录相等,则查找成功。  

    (2) 若给定的值小于中间记录,则在中间记录的左半区进行查找。

    (3) 若给定的值大于中间记录,则在中间记录的右半区进行查找。

    (4) 重复以上步骤,直到查找成功,若查找的区域无记录,则查找失败。

下面笔者定义了一个折半查找的类: 使用数字进行存储数据。

	private Object[] array;
	private int size;  // 数组的大小

	private BinarySearch(int max) {  //通过构造函数初始化数组的大小
		array = new Object[max];
		size = 0;
	}


插入数据: 由于折半查找要求数据必须有序,并且插入操作往往比查找操作频率更加小。所以在插入时就对数据进行排序。

	/**  插入元素
	 */
	@SuppressWarnings("unchecked")
	public void insert(T value) {
		int pos = 0;
		while(pos < size){
			if (((Comparable<? super T>) array[pos]).compareTo(value) > 0)
				break;
			pos++;
		}
		
		for (int k = size; k > pos; k--) {  //将比插入值大的元素后移
			array[k] = array[k - 1];
		}
		array[pos] = value;
		size++;
	}


折半查找: 这里使用的是
递归进行查找,时间复杂度为O(logN)。

	public int find(T searchKey) {
		return reFind(searchKey, 0, size - 1);
	}

	/**
	 * 通过将关键字与查找部分的中间元素比较,并调用自身实现递归调用,直到找到关键字
	 * 
	 * @param searchKey 需要寻找的关键字
	 * @param lower 查找部分的开始
	 * @param upper 查找部分的结尾
	 * @return  成功找到返回关键字位置,失败则返回 -1
	 */
	@SuppressWarnings("unchecked")
	public int reFind(T searchKey, int lower, int upper) {
		int current;
		current = (lower + upper) / 2;
		if (array[current] == searchKey) { // 找到关键字直接返回关键字位置
			return current;
		} else if (lower > upper) { // lower、upper交错,不能找到,直接返回数组大小
			return -1;
		} else {
			if (((Comparable<? super T>) array[current]).compareTo(searchKey) < 0) { // 关键字在小于 array[current] 的一半查找
				return reFind(searchKey, current + 1, upper);
			} else {
				return reFind(searchKey, lower, current - 1); // 关键字在大于array[current]的一半查找
			}
		}
	}


测试:

package org.TT.Recursion;

import java.util.Scanner;

/** 使用递归进行二分查找。
 *    递归的二分查找代码简洁,时间复杂度为O(logN)
 */
public class BinarySearch<T extends Comparable<? super T>> {

	// 这里的数组需要已经排好序,在插入时就直接排序
	private Object[] array;
	private int size;  // 数组的大小

	private BinarySearch(int max) {  //通过构造函数初始化数组的大小
		array = new Object[max];
		size = 0;
	}

	/** 取得查找数组的大小
	 */
	public int size() {
		return size;
	}

	public int find(T searchKey) {
		return reFind(searchKey, 0, size - 1);
	}

	/**
	 * 通过将关键字与查找部分的中间元素比较,并调用自身实现递归调用,直到找到关键字
	 * 
	 * @param searchKey 需要寻找的关键字
	 * @param lower 查找部分的开始
	 * @param upper 查找部分的结尾
	 * @return  成功找到返回关键字位置,失败则返回 -1
	 */
	@SuppressWarnings("unchecked")
	public int reFind(T searchKey, int lower, int upper) {
		int current;
		current = (lower + upper) / 2;
		if (array[current] == searchKey) { // 找到关键字直接返回关键字位置
			return current;
		} else if (lower > upper) { // lower、upper交错,不能找到,直接返回数组大小
			return -1;
		} else {
			if (((Comparable<? super T>) array[current]).compareTo(searchKey) < 0) { // 关键字在小于 array[current] 的一半查找
				return reFind(searchKey, current + 1, upper);
			} else {
				return reFind(searchKey, lower, current - 1); // 关键字在大于array[current]的一半查找
			}
		}
	}

	/**  插入元素
	 */
	@SuppressWarnings("unchecked")
	public void insert(T value) {
		int pos = 0;
		while(pos < size){
			if (((Comparable<? super T>) array[pos]).compareTo(value) > 0)
				break;
			pos++;
		}
		
		for (int k = size; k > pos; k--) {  //将比插入值大的元素后移
			array[k] = array[k - 1];
		}
		array[pos] = value;
		size++;
	}

	/**
	 *  展示需要查找的所有数组值
	 */
	public void dispaly() {
		for (int i = 0; i < size; i++) {
			System.out.print(array[i] + " ");
		}
	}

	@SuppressWarnings("resource")
	public static void main(String[] args) {
		Scanner in = new Scanner(System.in);

		BinarySearch<Integer> search = new BinarySearch<>(10);
		search.insert(10);
		search.insert(90);
		search.insert(80);
		search.insert(50);
		search.insert(30);
		search.insert(20);
		search.insert(40);
		search.insert(700);
		
		System.out.print("数组元素为: ");
		search.dispaly();

		System.out.println();
		System.out.print("请输入需要查找的元素:");
		int searchNum = in.nextInt();
		if (search.find(searchNum) != -1) {

			System.out.println("Found: " + searchNum);
		} else {
			System.out.println("Can't find " + searchNum);
		}
	}

}

《折半查找与二叉查找树》


建立二叉查找树进行查找


二叉查找树:
性质: 若它的左子树不为空,则左子树上所有节点的值均小于根节点;   若它的子树不为空,则子树上所有节点的值均小于根节点; 它的左右子树都是二叉查找树。

首先构造一个结点类,存储数据,并且有左孩子,右孩子的引用。在这里属性不设置成私有主要是为了便于访问,若想设为私有属性,可以设置get 、set方法访问。

package org.TT.BinarySeachTree;


/**结点数据结构*/  
public class BinaryNode<T>{  
       T data;  
       BinaryNode<T> left;  
       BinaryNode<T> right;  
       public BinaryNode(T data) {  
           this(data,null,null);  
       }  
       public BinaryNode( T data, BinaryNode<T> left, BinaryNode<T> right) {  
           this.data =data;  
           this.left = left;  
           this.right =right;  
       }  
 }

查找树: 只已知一个根节点,构造函数初始化一棵空树。

判断是否为空:直接判断根节点是否为空。

 清除树,直接将根节点的应用置为空即可,虚拟机可进行垃圾回收是发现引用为空,将进行垃圾回收。

<span style="white-space:pre">	</span>private BinaryNode<T> rootTree;


<span style="white-space:pre">	</span>/** 构造一颗空的二叉查找树 */
<span style="white-space:pre">	</span>public BinarySearchTree() {
<span style="white-space:pre">		</span>rootTree = null;
<span style="white-space:pre">	</span>}


<span style="white-space:pre">	</span>/** 清空二叉查找树 */
<span style="white-space:pre">	</span>public void clear() {
<span style="white-space:pre">		</span>rootTree = null;
<span style="white-space:pre">	</span>}


<span style="white-space:pre">	</span>/** 判断是否为空 */
<span style="white-space:pre">	</span>public boolean isEmpty() {
<span style="white-space:pre">		</span>return rootTree == null;
<span style="white-space:pre">	</span>}

插入: 每次插入寻找正确的位置并插入,保证插入后还是一棵二叉查找树。

	/** 插入元素 */
	public void insert(T t) {
		rootTree = insert(t, rootTree); // 第一次插入时,直接创建新节点并赋值给根节点
	}

	/** 在某个位置开始判断插入元素,采用递归实现不断寻找插入位置,代码简洁*/
	public BinaryNode<T> insert(T t, BinaryNode<T> parent) {
		if (parent == null) {
			return new BinaryNode<T>(t, null, null);  // 创建一个新节点
		}
		int result = t.compareTo(parent.data);  //若 t 小于 parent 返回负数, 大于返回正数,等于返回0
		if (result < 0)   
			parent.left = insert(t, parent.left);
		else if (result > 0)
			parent.right = insert(t, parent.right);
		else
			;// 若两个元素相等,什么也做,如果有需要可以在节点记录里附加域来指示重复元素插入的信息
		return parent;
	}


插入过程: 《折半查找与二叉查找树》

查找: 每次查找与根结点比较,(1)相等则返回   (2)小于,在左子树中查找   (3)大于,在右子树中查找

	/** 查找指定的元素,默认从根结点出开始查询 */
	public boolean find(T t) {
		return find(t, rootTree);

	}

	/** 从某个结点出开始查找元素,
	 * 用递归实现并不断将关键字与父节点比较,大于往右子树走,小于往左子树走等于返回 */
	public boolean find(T t, BinaryNode<T> node) {
		if (node == null)
			return false;
		int result = t.compareTo(node.data);  
		if (result > 0)
			return find(t, node.right);
		else if (result < 0)
			return find(t, node.left);
		else
			return true;
	}


查找最大值、最小值:  若查找最小值,直接在左子树上查找,一直往左查找。 若查找最大值,直接在右子树上查找,一直往右查找。 

	/** 查找二叉查找树中的最小值 */
	public T findMin() {
		if (isEmpty()) {
			System.out.println("二叉树为空");
			return null;
		} else
			return findMin(rootTree).data;

	}

	/** 查找出最小元素所在的结点 */
	public BinaryNode<T> findMin(BinaryNode<T> node) {
		if (node == null)
			return null;
		else if (node.left == null)
			return node;
		return findMin(node.left);// 尾递归查找
	}

	/** 查找二叉查找树中的最大值 */
	public T findMax() {
		if (isEmpty()) {
			System.out.println("二叉树为空");
			return null;
		} else
			return findMax(rootTree).data;
	}

	/** 查找出最大元素所在的结点 */
	public BinaryNode<T> findMax(BinaryNode<T> node) {
		if (node != null) {
			while (node.right != null)
				node = node.right;
		}
		return node;
	}


删除: 删除结点才用递归实现。

(1) 删除结点是叶子节点,直接删除 。

(2)只有左子树、右子树,只需重新连接。

(3)删除有两个孩子的节点:用右子树最小节点代替删除节点,并删除右子树最小节点

	/** 删除元素 */
	public void remove(T t) {
		rootTree = remove(t, rootTree);
	}

	/** 在某个位置开始判断删除某个结点,同样用递归实现 */
	public BinaryNode<T> remove(T t, BinaryNode<T> node) {
		if (node == null)
			return node;
		int result = t.compareTo(node.data);
		if (result > 0)
			node.right = remove(t, node.right);
		else if (result < 0)
			node.left = remove(t, node.left);
		else if (node.left != null && node.right != null) { 
			// 删除有两个孩子的节点:用右子树最小节点代替删除节点,并删除右子树最小节点
			node.data = findMin(node.right).data;   // 找到删除节点右子树的最小节点
			node.right = remove(node.data, node.right); // 删除右子树最小节点
		} else  
			node = (node.left != null) ? node.left : node.right;
		//删除没有子节点的节点(叶子)和只有一个节点的节点
		return node;
	}


遍历及测试:

package org.TT.BinarySeachTree;

/**
 * 二叉查找树的性质:
 *  1.对于树中的每个节点X,它的左子树中所有项的值小于X,而它的右子树中所有项的值大于X, 
 *  		Java实现二叉查找树(递归的编写二叉查找树的各种操作) 
 *    2.树的所有节点的平均深度为 O(logN) 
 *    3.所有操作的平均运行时间也是 O(logN)
 *    4.此处的操作:插入结点,构造二叉查找树、清空二叉树、判断树是否为空、查找指定结点、删除结点、查找最大值、查找最小值
 */
public class BinarySearchTree<T extends Comparable<? super T>> {

	private BinaryNode<T> rootTree;

	/** 构造一颗空的二叉查找树 */
	public BinarySearchTree() {
		rootTree = null;
	}

	/** 清空二叉查找树 */
	public void clear() {
		rootTree = null;
	}

	/** 判断是否为空 */
	public boolean isEmpty() {
		return rootTree == null;
	}
	
	/** 插入元素 */
	public void insert(T t) {
		rootTree = insert(t, rootTree); // 第一次插入时,直接创建新节点并赋值给根节点
	}

	/** 在某个位置开始判断插入元素,采用递归实现不断寻找插入位置,代码简洁*/
	public BinaryNode<T> insert(T t, BinaryNode<T> parent) {
		if (parent == null) {
			return new BinaryNode<T>(t, null, null);  // 创建一个新节点
		}
		int result = t.compareTo(parent.data);  //若 t 小于 parent 返回负数, 大于返回正数,等于返回0
		if (result < 0)   
			parent.left = insert(t, parent.left);
		else if (result > 0)
			parent.right = insert(t, parent.right);
		else
			;// 若两个元素相等,什么也做,如果有需要可以在节点记录里附加域来指示重复元素插入的信息
		return parent;
	}
	
	/** 查找指定的元素,默认从根结点出开始查询 */
	public boolean find(T t) {
		return find(t, rootTree);

	}

	/** 从某个结点出开始查找元素,
	 * 用递归实现并不断将关键字与父节点比较,大于往右子树走,小于往左子树走等于返回 */
	public boolean find(T t, BinaryNode<T> node) {
		if (node == null)
			return false;
		int result = t.compareTo(node.data);  
		if (result > 0)
			return find(t, node.right);
		else if (result < 0)
			return find(t, node.left);
		else
			return true;
	}

	/** 查找二叉查找树中的最小值 */
	public T findMin() {
		if (isEmpty()) {
			System.out.println("二叉树为空");
			return null;
		} else
			return findMin(rootTree).data;

	}

	/** 查找出最小元素所在的结点 */
	public BinaryNode<T> findMin(BinaryNode<T> node) {
		if (node == null)
			return null;
		else if (node.left == null)
			return node;
		return findMin(node.left);// 尾递归查找
	}

	/** 查找二叉查找树中的最大值 */
	public T findMax() {
		if (isEmpty()) {
			System.out.println("二叉树为空");
			return null;
		} else
			return findMax(rootTree).data;
	}

	/** 查找出最大元素所在的结点 */
	public BinaryNode<T> findMax(BinaryNode<T> node) {
		if (node != null) {
			while (node.right != null)
				node = node.right;
		}
		return node;
	}

	/** 删除元素 */
	public void remove(T t) {
		rootTree = remove(t, rootTree);
	}

	/** 在某个位置开始判断删除某个结点,同样用递归实现 */
	public BinaryNode<T> remove(T t, BinaryNode<T> node) {
		if (node == null)
			return node;
		int result = t.compareTo(node.data);
		if (result > 0)
			node.right = remove(t, node.right);
		else if (result < 0)
			node.left = remove(t, node.left);
		else if (node.left != null && node.right != null) { 
			// 删除有两个孩子的节点:用右子树最小节点代替删除节点,并删除右子树最小节点
			node.data = findMin(node.right).data;   // 找到删除节点右子树的最小节点
			node.right = remove(node.data, node.right); // 删除右子树最小节点
		} else  
			node = (node.left != null) ? node.left : node.right;
		//删除没有子节点的节点(叶子)和只有一个节点的节点
		return node;
	}

	/** 前序遍历 */
	public void preOrder(BinaryNode<T> node) {
		if (node != null) {
			System.out.print(node.data + ",");
			preOrder(node.left);
			preOrder(node.right);
		}
	}

	/** 后序遍历 */
	public void postOrder(BinaryNode<T> node) {
		if (node != null) {
			postOrder(node.left);
			postOrder(node.right);
			System.out.print(node.data + ",");
		}
	}

	/** 中序遍历 */
	public void inOrder(BinaryNode<T> node) {
		if (node != null) {
			inOrder(node.left);
			System.out.print(node.data + ",");
			inOrder(node.right);
		}
	}

	public static void main(String[] args) {

		int[] value = { 6,8,3,7,10,1,9 };
		BinarySearchTree<Integer> tree = new BinarySearchTree<>();
		for (int v : value) {
			tree.insert(v);
		}
		System.out.println("前序遍历");
		tree.preOrder(tree.rootTree);
		System.out.println();
		System.out.println("后序遍历");
		tree.postOrder(tree.rootTree);
		System.out.println();
		System.out.println("中序遍历");
		tree.inOrder(tree.rootTree);
		System.out.println();
		System.out.println("最小值==" + tree.findMin());
		System.out.println("最大值==" + tree.findMax());
		System.out.println("查找8: " + tree.find(8));
		System.out.println("是否为空: " + tree.isEmpty());
		System.out.println();

		tree.remove(8);
		System.out.println("删除节点值为8的节点,再中序遍历");
		System.out.println("查找8: " + tree.find(8));
		tree.inOrder(tree.rootTree);
	}

}

《折半查找与二叉查找树》

    原文作者:二叉查找树
    原文地址: https://blog.csdn.net/tanjiquan/article/details/50682017
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞