数据结构和算法——二叉树
树
1.树的优点
有序数组: 查找很快,二分法实现的查找所需要的时间为O(logN),遍历也很快,但是在有序数组中插入,删除却需要先 找到位置,
在把数组部分元素后移,效率并不高。
链表: 链表的插入和删除都是很快速的,仅仅需要改变下引用值就行了,时间仅为O(1),但是在链表中查找数据却需要
遍历所有的元素, 这个效率有些慢了。
树的优点: 树结合了有序数组和链表的优点,可以实现快速的查找,也可以快速的删除,查找。
树的一些专用术语:
路径:
顺着连接节点的边从一个节点到另一个节点的,所经过的所有节点的顺序排列就是路径。
根:
根即是树的顶端,一个树有且只有一个根,从根到所有节点的路径有且只有一条。
父节点:
每一个节点的连接的上一个节点即是该节点的父节点。
子节点;
每一个节点的向下连接的节点即是改节点的子节点
子树:
每个节点都可以认为是一个树的根,
叶节点:
就是没有子节点的节点
层:
一个节点的层数,从根节点到该节点有多少代,就是多少层
二叉树:
树种的节点可以有多个节点,二叉树是最多只能有2个节点的树。二叉树的两个节点被称为左子节点和右子节点。
二叉树的节点是最多有2个子节点,但并不是必须有2个子节点。
平衡树和非平衡树:
如果一个树中存在一个或多个的子树,只有右子节点,或左子节点,那么这个树就是非平衡树。
2.二叉搜索树:
根节点的左右2个节点,小于根节点在放在左侧,大于根节点的放在右侧。
<1>插入
<2>查找
<3>遍历
1.中序遍历
(1)调用自身遍历节点的左子树
(2)访问这个节点
(3)调用自身遍历节点的右子树
如上图遍历过程:35,38,40,45,50,67,83
2.前序遍历
(1)访问这个节点
(2)调用自身遍历节点的左子树
(3)调用自身遍历节点的右子树
如上图遍历过程:45,38,35,40,67,50,83
3.后序遍历
(1)调用自身遍历节点的左子树
(2)调用自身遍历节点的右子树
(3)访问这个节点
如上图遍历过程:35,40,38,50,83,67,45
<4>删除
(1)删除叶节点(没有子节点的)
(2)删除节点(一个子节点的)
(3)删除节点(二个子节点的)
<5>查找最大最小值
3.二叉搜索树的代码实现
树(Tree)的代码实现:
Tree 只需要有根节点,即可访问所有的子节点,这里可以简单的定义该类,只有一个变量Root.Root类型为Node(节点对象)
该类可以实现一些操作方法大致如下:
public class Tree { Node root; public Tree() { } /** * 删除节点 * @param key */ public void deldte( int key){ } /** * 查找节点 * * @param key */ public Node find(int key){ return null; } /** * * * @param key 插入值 * @param otherdata 插入的其他数据 */ public void insert( int key,int otherdata){ } /** * 遍历二叉树 */ public void disPlayTree(Node node){ }
节点(Node)的代码实现
Node 需要有数据项,有该类对象的左节点,右节点,还可以包含其他的数据项。实现大致如下:
public class Node { /** * 数据项 */ int data; /** * 其他数据 */ int otherData; /** * 左节点 */ Node leftChild; /** * 右节点 */ Node rightChild; public Node() { } }
Tree和Node实现后,那么便可以实现里面的操作方法了。
find查找过程如图
根据上图可以看出查找一个节点,最多比较次数为Tree的层数,其代码如下:
/** * 查找节点 * * @param key 查找的值,在该代码中为Node.data */ public Node find(int key){ Node current =root; while (current.data!=key){ /** * 小于当前节点的值,去left查找,否则去right */ if(key<current.data){ current=current.leftChild; }else { current=current.rightChild; } /** * 没查找到 */ if(current==null){ return null; } } return current; }
insert插入,插入和查找基本过程差不多,仍然是比较数据项大小,小了放在左侧,大了放其右侧。
其代码如:
/** * @param key 插入值 node.data * @param otherdata 插入的其他数据 node.otherdata */ public void insert( int key,int otherdata){ Node newNode=new Node(); newNode.data=key; newNode.otherData=otherdata; if(root==null){ root=newNode; }else{ Node current=root; Node parent; while (true) { parent=current; if(key<current.data){ current=current.leftChild; if(current==null){ parent.leftChild=newNode; return; } }else{ current=current.rightChild; if(current==null){ parent.rightChild=newNode; return; } } } } }
遍历二叉搜索树
二叉树的中序遍历过程是调用自身左子树,然后访问节点,在调用自身右子树。递归代码如下。而前序和后序的遍历只需要把中序遍历
中的调用自身的递归和访问节点(就是打印那一行)翻翻顺序就ok了。
/** * 递归遍历二叉树(中序) */ public void disPlayTree(Node node){ if(node!=null){ if(node.leftChild!=null){ disPlayTree(node.leftChild); } Log.d("", "二叉树遍历: "+node.data); if(node.rightChild!=null){ disPlayTree(node.rightChild); } } }
删除:delete节点。如果待删除节点是叶节点(没有子节点),值直接把删除节点赋值为null即可。
如果有一个子节点也简单,待删除的节点在删除节点的左侧(右侧),则把待删除节点的子树赋值给待删除节点父节点的
左侧(右侧)。
删除:如果删除节点右2个子节点,则需要先找到待删除节点的后续节点,即是比待删除节点次高的节点。
如图:
删除过程就是:87位后续节点,为50的右节点。62为87的左节点。89位87的右节点。
删除:后续节点在左侧时:
查找到后续节点是77,则50的右节点为77,79变成87的左节点,93还是83的右节点。62变成77的左节点。
删除代码实现:
1.获取后续节点
/** * 获取后序节点 */ public Node getSuccessor(Node delNode){ Node successorParent =delNode; Node successor=delNode; Node current=delNode.rightChild; while(current!=null){ successorParent=successor; successor=current; current=current.leftChild; } if(successor!=delNode.rightChild){ successorParent.leftChild=successor.rightChild; successor.rightChild=delNode.rightChild; } Log.d("二叉树遍历", "getSuccessor: "+successor.data); return successor; }
删除节点:
/** * 删除节点 * @param key */ public boolean deldte( int key){ Node current=root; Node parent=root; boolean isLeftChild=true; /** * 先把删除值的Node找出来 */ while(current.data!=key){ parent=current; if(key<current.data){ isLeftChild=true; current=current.leftChild; }else { isLeftChild=false; current=current.rightChild; } if(current==null){ return false; } } // while结束,查找到删除节点,就是current /** * 如果删除节点是叶节点 */ if(current.leftChild==null&¤t.rightChild==null){ if(current==root){ root=null; }else if(isLeftChild){ parent.leftChild=null; }else{ parent.rightChild=null; } } /** * 如果删除的节点没有rightChild,只有leftChild */ else if(current.rightChild==null){ if(current==root){ root=current.leftChild; } else if(isLeftChild){ parent.leftChild=current.leftChild; } else { parent.rightChild=current.leftChild; } } /** * 如果删除的节点只有rightChild */ else if(current.leftChild==null){ if(current==root){ root=current.rightChild; } else if(isLeftChild){ parent.leftChild=current.rightChild; }else{ parent.rightChild=current.rightChild; } } /** * 如果删除点有2个节点 */ else { /** * 获取后续节点 */ Node successor=getSuccessor(current); if(current==root){ root=successor; }else if( isLeftChild){ parent.leftChild=successor; }else{ parent.rightChild=successor; } successor.leftChild=current.leftChild; } return true; }
找最大最小值:
寻找最大最小值 这个就简单了,从根节点一直找左节点直到没有左子节点,那么这个值就是最小值,反之就是最大值。
4.小结:
代码:
http://pan.baidu.com/s/1miz8ocC
https://github.com/galibujianbusana/MyErChaShu
今天多一点积累,明天少一分烦恼