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

虽然二叉排序树的最坏效率是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);elsereturn 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的右子树 */ elseq->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实现

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

转载于:https://www.cnblogs.com/aspirant/p/3557241.html

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

  1. 图解二叉树非递归版的中序遍历算法

    你会学到什么 讨论的问题是什么 这个问题相关的概念和理论 非递归版中序遍历算法 代码思考 算法技巧 实现代码 快照 评价算法 总结 欢迎关注算法思考与应用公众号 你会学到什么? 树的递归遍历算法很容易 ...

  2. leetcode 106. 从中序与后序遍历序列构造二叉树 105. 从前序与中序遍历序列构造二叉树思考分析

    目录 1.106题目 2.参考思路:递归切割数组 3.105题目 4.同样思路的代码 1.106题目 2.参考思路:递归切割数组 代码参考:公众号:代码随想录 后序数组+中序数组 以 后序数组(左右中 ...

  3. 新手学习算法----二叉树(将一个二叉查找树按照中序遍历转换成双向链表)

    题目:将一个二叉查找树按照中序遍历转换成双向链表. 给定一个二叉查找树: 4/ \2 5/ \ 1 3 返回 1<->2<->3<->4<->5. 思路 ...

  4. 【二叉树】根据后续和中序遍历输出前序遍历 [建树+非建树做法]

    F . 案例 4-1.1:根据后续和中序遍历输出前序遍历 Description 本题要求根据给定的一棵二叉树的后序遍历和中序遍历结果,输出该树的先序遍历结果. Input 第一行给出正整数N (≤3 ...

  5. 【必拿下系列】106. 从中序与后序遍历序列构造二叉树105从前序与中序遍历序列构造二叉树

    两题各自的链接放这里了: 链接: 106 链接: 105 106.从中序与后序遍历序列构造二叉树 如果你是不知道理论的,那就得仔细分析了, 举个例子: 输入:inorder = [9,3,15,20, ...

  6. 剑指offer 07重建二叉树(根据前序、中序遍历)草真tm难

    /*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode ...

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

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

  8. 刻意练习:LeetCode实战 -- Task22. 二叉树的中序遍历

    背景 本篇图文是LSGO软件技术团队组织的 第二期基础算法(Leetcode)刻意练习训练营 的打卡任务.本期训练营采用分类别练习的模式,即选择了五个知识点(数组.链表.字符串.树.贪心算法),每个知 ...

  9. 算法--------二叉树的中序遍历

    题目: 给定一个二叉树,返回它的中序 遍历.示例:输入: [1,null,2,3]1\2/3输出: [1,3,2] 程序入口: public static void main(String[] arg ...

最新文章

  1. Python基础知识(五)--数据类型
  2. vue 获取当前位置 高德_vue高德地图获取当前位置
  3. C语言使用递归算法实现Sudoku Solver算法(附完整源码)
  4. 第01课:中文自然语言处理的完整机器处理流程
  5. java script 添加控件,【更新】GLG工具包Visualization and HMI Toolkit更新至v3.6,支持Java Script...
  6. 任正非:不向美国人民学习他们的伟大,就永远战胜不了美国
  7. GStreamer基础教程04 - 动态连接Pipeline
  8. 在码云上如何创建仓库
  9. Maxthon3资源嗅探器给力 MP3下载地址得来全不费功夫!
  10. 微服务系列:分布式日志 ELK 搭建指南
  11. web前端网页设计期末课程大作业:中华传统文化题材网页源码——基于HTML实现中国水墨风书画艺术网站(12个页面)
  12. 华为云学院-人人学loT学习笔记- 第三章 窄带无线,宽带互联
  13. 2021全国特种设备-R1快开门式压力容器充装模拟考试题库一[安考星]
  14. 机器学习 | 吴恩达斯坦福课程笔记整理之(二)逻辑回归
  15. SCCM部署系统任务序列失败错误代码0x80070002
  16. android 盒子刷机,一加5刷机盒子
  17. TIA博途中如何为PLC分配IP地址?
  18. 依存句法分析与语义依存分析的区别
  19. id returned 1 existed:让人发疯的devc++报错
  20. 网络协议对应的端口号

热门文章

  1. 【狮子数学】chapter6-02-正项级数的敛散性判别(第92-94讲)
  2. 2021-02-01:如何快速分辨真假5G全网通PAD平板
  3. sim插拔识别时间_一种sim卡检测装置及其检测sim卡插拔的方法
  4. 嵌入式基于Flash上的文件系统
  5. 『Python开发实战菜鸟教程』实战篇:爬虫快速入门——统计分析CSDN与博客园博客阅读数据
  6. OpenGL ES 开篇
  7. 华为手机删视频后显示无服务器,华为手机总出现一些不明照片咋回事,原来你没关闭这个默认设置...
  8. 类与对象01_旺旺老师Java学习解决方案_第一季
  9. 企航驾校理论考试模拟系统发布
  10. 当 ChatGPT 比你更会写代码,程序员还能干什么?