二叉树是一种非常重要的数据结构,它同时具有数组和链表各自的特点:它可以像数组一样快速查找,也可以像链表一样快速添加。但是他也有自己的缺点:删除操作复杂。

虽然二叉排序树的最坏效率是O(n),但它支持动态查找,且有很多改进版的二叉排序树可以使树高为O(logn),如AVL、红黑树等。

对于排序二叉树,若按中序遍历就可以得到由小到大的有序序列。

我们先介绍一些关于二叉树的概念名词。

二叉树:是每个结点最多有两个子树的有序树,在使用二叉树的时候,数据并不是随便插入到节点中的,一个节点的左子节点的关键值必须小于此节点,右子节点的关键值必须大于或者是等于此节点,所以又称二叉查找树、二叉排序树、二叉搜索树。

完全二叉树:若设二叉树的高度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第h层有叶子结点,并且叶子结点都是从左到右依次排布,这就是完全二叉树。

满二叉树——除了叶结点外每一个结点都有左右子叶且叶子结点都处在最底层的二叉树。

深度——二叉树的层数,就是深度。

二叉树的特点总结:

(1)树执行查找、删除、插入的时间复杂度都是O(logN)

(2)遍历二叉树的方法包括前序、中序、后序

(3)非平衡树指的是根的左右两边的子节点的数量不一致

(4) 在非空二叉树中,第i层的结点总数不超过 , i>=1;

(5)深度为h的二叉树最多有个结点(h>=1),最少有h个结点;

(6)对于任意一棵二叉树,如果其叶结点数为N0,而度数为2的结点总数为N2,则N0=N2+1;

排序二叉树 定义

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

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

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

3. 它的左、右子树也分别为二叉排序树。

二叉排序树通常采用二叉链表作为存储结构。中序遍历二叉排序树可得到一个依据关键字的有序序列,一个无序序列可以通过构造一棵二叉排序树变成一个有序序列,构造树的过程即是对无序序列进行排序的过程。每次插入的新的结点都是二叉排序树上新的叶子结点,在进行插入操作时,不必移动其它结点,只需改动某个结点的指针,由空变为非空即可。

搜索、插入、删除的时间复杂度等于树高,期望O(logn),最坏O(n)(数列有序,树退化成线性表,如右斜树)。

/* 二叉树的二叉链表结点结构定义 */
typedef struct BiTNode /* 结点结构 */
{
int data; /* 结点数据 */
struct BiTNode *lchild, *rchild; /* 左右孩子指针 */
} BiTNode, *BiTree;

虽然二叉排序树的最坏效率是O(n),但它支持动态查找,且有很多改进版的二叉排序树可以使树高为O(logn),如AVL、红黑树等。

对于排序二叉树,若按中序遍历就可以得到由小到大的有序序列。

插入创建算法

创建排序二叉树的步骤就是不断像排序二叉树中添加新节点(p)的过程:

(1)以根节点(root)为当前节点(current)开始搜索;

(2)用新节点p的值和current的值进行比较;

(3)如果p.data>current.data,则current=current.right;若p.data<current.data,则current=current.left;

(4)重复(2)(3),直到找到合适的叶子节点位置;

(5)将p添加到上面找到的合适位置,若新节点更大,则添加为右子节点;否则,加为左子节点

查找算法

在二元排序树b中查找x的过程为:

1.若b是空树,则搜索失败,否则:

2.若x等于b的根节点的数据域之值,则查找成功;否则:

3.若x小于b的根节点的数据域之值,则搜索左子树;否则:

4.查找右子树。

删除算法:

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

1.若*p结点为叶子结点,即PL(左子树)和PR(右子树)均为空树。由于删去叶子结点不破坏整棵树的结构,则只需修改其双亲结点的指针即可。

2.若*p结点只有左子树PL或右子树PR,此时只要令PL或PR直接成为其双亲结点*f的左子树(当*p是左子树)或右子树(当*p是右子树)即可,作此修改也不破坏二叉排序树的特性。

3.若*p结点的左子树和右子树均不空。在删去*p之后,为保持其它元素之间的相对位置不变,可按中序遍历保持有序进行调整。比较好的做法是,找到*p的直接前驱(或直接后继)*s,用*s来替换结点*p,然后再删除结点*s。

代码:

/* 若二叉排序树T中存在关键字等于key的数据元素时,则删除该数据元素结点, */
/* 并返回TRUE;否则返回FALSE。 */
Status DeleteBST(BiTree *T,int key)
{
if(!*T) /* 不存在关键字等于key的数据元素 */
return FALSE;
else
{
if (key==(*T)->data) /* 找到关键字等于key的数据元素 */
return Delete(T);
else if (key<(*T)->data)
return DeleteBST(&(*T)->lchild,key);
else
return DeleteBST(&(*T)->rchild,key); }
} /* 从二叉排序树中删除结点p,并重接它的左或右子树。 */
Status Delete(BiTree *p)
{
BiTree q,s;
if((*p)->rchild==NULL) /* 右子树空则只需重接它的左子树(待删结点是叶子也走此分支) */
{
q=*p; *p=(*p)->lchild; free(q);
}
else if((*p)->lchild==NULL) /* 只需重接它的右子树 */
{
q=*p; *p=(*p)->rchild; free(q);
}
else /* 左右子树均不空 */
{
q=*p; s=(*p)->lchild;
while(s->rchild) /* 转左,然后向右到尽头(找待删结点的前驱) */
{
q=s;
s=s->rchild;
}
(*p)->data=s->data; /* s指向被删结点的直接前驱(将被删结点前驱的值取代被删结点的值) */
if(q!=*p)
q->rchild=s->lchild; /* 重接q的右子树 */
else
q->lchild=s->lchild; /* 重接q的左子树 */
free(s);
}
return TRUE;
}

二叉排序树性能分析

每个结点的Ci为该结点的层次数。最好的情况是二叉排序树的形态和折半查找的判定树相同,其平均查找长度和logn成正比(O(log2(n)))。最坏情况下,当先后插入的关键字有序时,构成的二叉排序树为一棵斜树,树的深度为n,其平均查找长度为(n + 1) / 2。也就是时间复杂度为O(n),等同于顺序查找。因此,如果希望对一个集合按二叉排序树查找,最好是把它构建成一棵平衡的二叉排序树(平衡二叉树)。

参考:二叉排序树

package com.liuhao.DataStructures;  

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.List;
import java.util.Queue; public class SortedBinTree <T extends Comparable>{ static class Node{
Object data;
Node parent;
Node left;
Node right; public Node(Object data, Node parent, Node left, Node right) {
this.data = data;
this.parent = parent;
this.left = left;
this.right = right;
} public String toString(){
return "[data=" + data + "]";
} public boolean equals(Object obj){
if(this == obj){
return true;
} if(obj.getClass() == Node.class){
Node target = (Node) obj;
return data.equals(target.data) && left == target.left
&& right == target.right && parent == target.parent;
} return false;
} } private Node root; public SortedBinTree(){
root = null;
} public SortedBinTree(T o){
root = new Node(o, null, null, null);
} /**
* 添加节点
* @param ele 新节点的元素
*/
public void add(T ele){
if(root == null){
root = new Node(ele, null, null, null);
}
else{
Node current = root;
Node parent = null;
int cmp; //搜索合适的叶子节点,以该叶子节点为父节点添加新节点
do{
parent = current;
cmp = ele.compareTo(current.data); //如果新节点的值大于当前节点的值
if(cmp > 0){
//以当前节点的右子节点作为当前节点
current = current.right;
}else{
current = current.left;
}
}while(current != null); //创建新节点
Node newNode = new Node(ele, parent, null, null); //如果新节点的值大于父节点的值
if(cmp > 0){
parent.right = newNode;
}else{
parent.left = newNode;
}
}
} /**
* 删除节点
* @param ele
*/
public void remove(T ele){
Node target = getNode(ele); if(target == null){
return;
} //左右子树都为空
if(target.left == null && target.right == null){
if(target == root){
root = null;
}
else{
//被删除节点是父节点的左子节点
if(target == target.parent.left){
//将target的父节点的left设为null
target.parent.left = null;
}else{
target.parent.right = null;
} target.parent = null;
}
} //左空右不空
else if(target.left == null && target.right != null){
if(target == root){
root = target.right;
}
else{
//被删除节点是父节点的左子节点
if(target == target.parent.left){
target.parent.left = target.right;
}
else{
target.parent.right = target.right;
} //让target的右子树的parent指向target的parent
target.right.parent = target.parent;
}
} else if(target.left != null && target.right == null){
if(target == root){
root = target.left;
}
else{
//被删除节点是父节点的左子节点
if(target == target.parent.left){
target.parent.left = target.left;
}
else{
target.parent.right = target.left;
} //让target的右子树的parent指向target的parent
target.left.parent = target.parent;
}
} //左右都不为空
else{
//leftMaxNode:target的左子树中值最大的节点
Node leftMaxNode = target.left; //搜索target的左子树中值最大的节点
while(leftMaxNode.right != null){
leftMaxNode = leftMaxNode.right;
} //从原来的子树中删除leftMaxNode节点
leftMaxNode.parent.right = null; leftMaxNode.parent = target.parent; if(target == target.parent.left){
target.parent.left = leftMaxNode;
}
else{
target.parent.right = leftMaxNode;
} leftMaxNode.left = target.left;
leftMaxNode.right = target.right;
target.parent = target.left = target.right = null;
}
} /**
* 根据指定值搜索节点
* @param ele 指定值
* @return 节点
*/
public Node getNode(T ele){
//从根节点开始搜索
Node p = root;
while(p != null){
int cmp = ele.compareTo(p.data); if(cmp < 0){
p = p.left;
}
else if(cmp > 0){
p = p.right;
}
else{
return p;
}
} return null;
} /**
* 广度优先遍历
* @return
*/
public List<Node> breadthFirst(){
Queue<Node> queue = new ArrayDeque<Node>();
List<Node> list = new ArrayList<Node>(); if(root != null){
queue.offer(root);
} while(!queue.isEmpty()){
//将该队列的“队尾”元素添加到List中
list.add(queue.peek());
//弹出队尾节点
Node p = queue.poll(); //如果左子节点不为null,将它加入“队列”
if(p.left != null){
queue.offer(p.left);
} if(p.right != null){
queue.offer(p.right);
}
} return list;
} /**
* 中序遍历
* @return
*/
public List<Node> inIterator(){
return inIterator(root);
} private List<Node> inIterator(Node node){
List<Node> list = new ArrayList<Node>(); //递归处理左子树
if(node.left != null){
list.addAll(inIterator(node.left));
} //处理根节点
list.add(node); //递归处理右子树
if(node.right != null){
list.addAll(inIterator(node.right));
} return list;
}
}

自己写的排序二叉树的创建和排序

package com.binary_tree;

public class SortBinTree {

    SortBinTree left;
SortBinTree right;
int value; SortBinTree(int value) {
left = null;
right = null;
this.value = value; } static SortBinTree insert(SortBinTree node, int value) { if (node == null) {
node = new SortBinTree(value);
} else { if (node.value > value) {
node.left = insert(node.left, value);
} else {
node.right = insert(node.right, value);
}
}
return node;
} static SortBinTree find(SortBinTree root, int key) { if (root == null)
return null; if (root.value == key) {
return root;
} else {
if (root.value > key) {
return find(root.left, key);
} else {
return find(root.right, key);
}
} } static void printMiddleOrder(SortBinTree root) {
if (root == null)
return; printMiddleOrder(root.left);
System.out.println(root.value);
printMiddleOrder(root.right); } public static void main(String[] args) {
// TODO Auto-generated method stub SortBinTree root = null;
int value[] = { 11, 12, 7, 4, 3, 2, 6, 15, 8, 9 };
for (int i = 0; i < value.length; i++) {
root = insert(root, value[i]);
} printMiddleOrder(root); SortBinTree findTree = find(root, 3);
System.out.println("ss"); printMiddleOrder(findTree);
} }

参考:二叉排序树

参考:二叉树的Java实现及特点总结

参考:排序二叉树及其Java实现

参考:一步一图一代码之排序二叉树

二叉树 排序二叉树-可以通过中序遍历得到排序的数据 二叉排序树时间复杂度O(logn),的更多相关文章

  1. java创建二叉树并实现非递归中序遍历二叉树

    java创建二叉树并递归遍历二叉树前面已有讲解:http://www.cnblogs.com/lixiaolun/p/4658659.html. 在此基础上添加了非递归中序遍历二叉树: 二叉树类的代码 ...

  2. 剑指offer:按之字形打印二叉树(栈|双向队列+中序遍历)

    1. 题目描述 /** 请实现一个函数按照之字形打印二叉树, 即第一行按照从左到右的顺序打印, 第二层按照从右至左的顺序打印, 第三行按照从左到右的顺序打印, 其他行以此类推. */ 2. 双向队列 ...

  3. LeetCode 94. 二叉树的中序遍历(Binary Tree Inorder Traversal)

    94. 二叉树的中序遍历 94. Binary Tree Inorder Traversal 题目描述 给定一个二叉树,返回它的 中序 遍历. LeetCode94. Binary Tree Inor ...

  4. lintcode :前序遍历和中序遍历树构造二叉树

    解题 前序遍历和中序遍历树构造二叉树 根据前序遍历和中序遍历树构造二叉树. 样例 给出中序遍历:[1,2,3]和前序遍历:[2,1,3]. 返回如下的树: 2 / \ 1 3 注意 你可以假设树中不存 ...

  5. [Swift]LeetCode94. 二叉树的中序遍历 | Binary Tree Inorder Traversal

    Given a binary tree, return the inorder traversal of its nodes' values. Example: Input: [1,null,2,3] ...

  6. LeetCode(94):二叉树的中序遍历

    Medium! 题目描述: 给定一个二叉树,返回它的中序 遍历. 示例: 输入: [1,null,2,3] 1 \ 2 / 3 输出: [1,3,2] 进阶: 递归算法很简单,你可以通过迭代算法完成吗 ...

  7. 【LeetCode题解】94_二叉树的中序遍历

    目录 [LeetCode题解]94_二叉树的中序遍历 描述 方法一:递归 Java 代码 Python代码 方法二:非递归 Java 代码 Python 代码 [LeetCode题解]94_二叉树的中 ...

  8. Leetcode 94. 二叉树的中序遍历

    1.问题描述 给定一个二叉树,返回它的中序 遍历. 示例: 输入: [1,null,2,3] 1 \ 2 / 3 输出: [1,3,2] 进阶: 递归算法很简单,你可以通过迭代算法完成吗? 2.解法一 ...

  9. 【遍历二叉树】02二叉树的中序遍历【Binary Tree Inorder Traversal】

    ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 给定一个二叉树,返回他的中序遍历的 ...

随机推荐

  1. Django---MTV和MVC的了解,Django的模版语言变量和逻辑,常见的模板语言过滤器,自定义过滤器,CSRF了解,Django的母版(继承extends,块block,组件include,静态文件的加载load static),自定义simple_tag和inclusion_tag

    Django---MTV和MVC的了解,Django的模版语言变量和逻辑,常见的模板语言过滤器,自定义过滤器,CSRF了解,Django的母版(继承extends,块block,组件include,静 ...

  2. Matlab桥接模式

    桥接模式(Bridge)是一种结构型设计模式.它是用组合关系代替继承关系来实现,可以处理多维度变化的场景(https://blog.csdn.net/qq_31156277/article/detai ...

  3. 一次 Young GC 的优化实践(FinalReference 相关)

    本文转载自公众号:涤生的博客,阅读时间大约需要11分钟.涤生的文章看起来跟破案一样,很精彩,很有启发. 前言 博客已经好久没有更新了,主要原因是 18 年下半年工作比较忙,另外也没有比较有意思的题材, ...

  4. Flask基础之session验证与模板渲染语法(jinja2)

    目录 1.http传输请求头参数 2.Flask中request.data参数处理 3.Flask中request.json参数 4.Flask中的session管理 5.Flask中模板语法(if, ...

  5. css3 -webkit-image-set 设置不同分辨率 背景图片

  6. “IOS11不再信赖WOSIGN证书”公众号运营者如何应对

    ptd->_thandle = (uintptr_t)(-1); {{}/*** 传入需要的参数,设置给*/{}给Fragment添加newInstance方法,将需要的参数传入,设置到bund ...

  7. wordpress调用置顶文章sticky_posts的三种方法

    有时我们在开发wordpress时需要调用置顶文章sticky_posts,怎么调用呢?几种写法,有用到query_post的,有用到WP_Query,也有用到is_sticky(),下面随ytkah ...

  8. MapReduce的初识

    MapReduce是什么 HDFS:分布式存储系统 MapReduce:分布式计算系统 YARN:hadoop 的资源调度系统 Common:以上三大组件的底层支撑组件,主要提供基础工具包和 RPC ...

  9. POJ3616-Milking Time-(dp)

    题意:牛有m个时间段可以挤奶,每个时间段的开始时间,结束时间,挤奶量不尽相同,寄完一次需要休息r时间,求在n时间内如何安排牛挤奶产量最大. 解题: 1.休息r时间,当做结束时间需要+r 2.以结束时间 ...

  10. VS 2015秘钥

    专业版:HMGNV-WCYXV-X7G9W-YCX63-B98R2企业版:HM6NR-QXX7C-DFW2Y-8B82K-WTYJV