二叉树 排序二叉树-可以通过中序遍历得到排序的数据 二叉排序树时间复杂度O(logn),
二叉树是一种非常重要的数据结构,它同时具有数组和链表各自的特点:它可以像数组一样快速查找,也可以像链表一样快速添加。但是他也有自己的缺点:删除操作复杂。
虽然二叉排序树的最坏效率是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);
} }
参考:二叉排序树
二叉树 排序二叉树-可以通过中序遍历得到排序的数据 二叉排序树时间复杂度O(logn),的更多相关文章
- java创建二叉树并实现非递归中序遍历二叉树
java创建二叉树并递归遍历二叉树前面已有讲解:http://www.cnblogs.com/lixiaolun/p/4658659.html. 在此基础上添加了非递归中序遍历二叉树: 二叉树类的代码 ...
- 剑指offer:按之字形打印二叉树(栈|双向队列+中序遍历)
1. 题目描述 /** 请实现一个函数按照之字形打印二叉树, 即第一行按照从左到右的顺序打印, 第二层按照从右至左的顺序打印, 第三行按照从左到右的顺序打印, 其他行以此类推. */ 2. 双向队列 ...
- LeetCode 94. 二叉树的中序遍历(Binary Tree Inorder Traversal)
94. 二叉树的中序遍历 94. Binary Tree Inorder Traversal 题目描述 给定一个二叉树,返回它的 中序 遍历. LeetCode94. Binary Tree Inor ...
- lintcode :前序遍历和中序遍历树构造二叉树
解题 前序遍历和中序遍历树构造二叉树 根据前序遍历和中序遍历树构造二叉树. 样例 给出中序遍历:[1,2,3]和前序遍历:[2,1,3]. 返回如下的树: 2 / \ 1 3 注意 你可以假设树中不存 ...
- [Swift]LeetCode94. 二叉树的中序遍历 | Binary Tree Inorder Traversal
Given a binary tree, return the inorder traversal of its nodes' values. Example: Input: [1,null,2,3] ...
- LeetCode(94):二叉树的中序遍历
Medium! 题目描述: 给定一个二叉树,返回它的中序 遍历. 示例: 输入: [1,null,2,3] 1 \ 2 / 3 输出: [1,3,2] 进阶: 递归算法很简单,你可以通过迭代算法完成吗 ...
- 【LeetCode题解】94_二叉树的中序遍历
目录 [LeetCode题解]94_二叉树的中序遍历 描述 方法一:递归 Java 代码 Python代码 方法二:非递归 Java 代码 Python 代码 [LeetCode题解]94_二叉树的中 ...
- Leetcode 94. 二叉树的中序遍历
1.问题描述 给定一个二叉树,返回它的中序 遍历. 示例: 输入: [1,null,2,3] 1 \ 2 / 3 输出: [1,3,2] 进阶: 递归算法很简单,你可以通过迭代算法完成吗? 2.解法一 ...
- 【遍历二叉树】02二叉树的中序遍历【Binary Tree Inorder Traversal】
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 给定一个二叉树,返回他的中序遍历的 ...
随机推荐
- Python进阶----索引原理,mysql常见的索引,索引的使用,索引的优化,不能命中索引的情况,explain执行计划,慢查询和慢日志, 多表联查优化
Python进阶----索引原理,mysql常见的索引,索引的使用,索引的优化,不能命中索引的情况,explain执行计划,慢查询和慢日志, 多表联查优化 一丶索引原理 什么是索引: 索引 ...
- 给基于对话框的MFC程序添加状态栏并实时显示时间
转载自丝雪儿 1.首先在string table 里添加两个字串,ID分别为IDS_INDICATOR_MESSAGE and IDS_INDICATOR_TIME 2.在你的 dlg.h 类里面加个 ...
- JavaScript设计模式与开发实践随笔(二)
多态 多态的实际含义是:同一操作作用于不同的对象上面,可以产生不同的解释和不同的执行结果.换句话说,给不同的对象发送同一个消息的时候,这些对象会根据这个消息分别给出不同的反馈 var makeSoun ...
- Golang Web应用 创建docker镜像笔记(win 平台)
记录的是 本地编译好了再创建容器镜像的方法 ,这样子生成的镜像文件比较小,方便分发部署 win 平台需要设置golang交叉编译 生成linux可执行文件 CMD下: Set GOOS="l ...
- Shell 编程 循环语句
本篇主要写一些shell脚本循环语句的使用. for 循环 指定次数 #!/bin/bash for ((i=1;i<=10;i++)) do echo $i done [root@localh ...
- mac python3 安装mysqlclient
brew install openssl export LIBRARY_PATH=$LIBRARY_PATH:/usr/local/opt/openssl/lib/ pip install mysql ...
- Linux-crontab定时执行脚本配置
crontab是一个可以根据自己配置的命令定时执行脚本的服务 安装crontab(centos) yum install Vixie-cron yum install crontabs vixie-c ...
- CentOS7 编译安装MySQL5.6.38(一)
一.下载MySQL5.6.38安装包 下载地址:https://www.mysql.com/downloads/ 打开网站之后选择Archives 然后再选择开源版本 选择我们要下载的版本: htt ...
- Windows平台 python环境配置
下载python:https://www.python.org/downloads/windows/,一般就下载 executable installer,x86 表示是 32 位机子的,x86-64 ...
- 前端重定向,index.html文件被浏览器缓存,导致整个应用都是旧的
解决方法:https://github.com/ant-design/ant-design-pro/issues/1365#issuecomment-384496088