二叉树 排序二叉树-可以通过中序遍历得到排序的数据 二叉排序树时间复杂度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); } }
参考:二叉排序树
本文来自博客园,作者:aspirant,转载请注明原文链接:https://www.cnblogs.com/aspirant/p/3557241.html