简介
不了解相关概念可先阅读 数据结构-树定义及分类
树集合了数组(查找速度快)和链表(插入、删除速度快)的优点;
二叉搜索树的效率:
树的大部分操作需要从上至下一层层的查找树的节点,对于一棵满树,大约有一半的节点处于最底层(最底层节点数 = 其它层节点数的和 + 1),故节点操作大约有一半需要找到最底层节点,大约有四分之一的节点处于倒数第二层,故节点操作大约有四分之一需要找到倒数第二层节点,依此类推;查找过程中,需要访问每一层的节点,故只要知道了查找的层数,就能知道操作所需的时间,如果节点总数为N,层数为L,L=log2(N+1);
如果为查找操作或删除操作,被操作的节点可能是是树的任意节点,故查找操作或删除操作的时间复杂度为:1/21log2(N+1) + 1/22log2(N/2+1) + … + 1/2N*1
如果为插入操作,由于每次都在树的最低层插入新的节点,故插入操作的时间复杂度为:log2(N+1)
总的来说可以认为二叉搜索树操作的时间复杂度为为O(logN)
查找二叉树:在插入时需要将插入的元素按照一定的顺序排列好,否则可能会成为链表结构,就失去了二叉查找树的优势了;
二叉排序树或者是一棵空树,或者是具有下列性质的二叉树:
(1)若左子树不空,则左子树上所有结点的值均小于或等于它的根结点的值;
(2)若右子树不空,则右子树上所有结点的值均大于或等于它的根结点的值;
(3)左、右子树也分别为二叉排序树;
插入
1)非递归方法插入
public void createTree(List<Integer> list) {
if (list == null || list.isEmpty()) {
return;
}
for (int i : list) {
insertNode(i);
}
}
/**
* 添加一个节点;如果该树中不存在该节点,直接添加;已存在,直接返回对应值的节点;
*
* @param item
* @return
*/
TreeNode insertNode(int item) {
if (isEmptyTree()) {
TreeNode node = new TreeNode(item, null);
root = node;
return node;
}
TreeNode node = root;
TreeNode parentNode = node;
//node == null 就是要插入的位置,parentNode就是node的双亲节点
while (node != null) {
parentNode = node;
if (node.item > item) {
node = node.getLiftChild();
} else if (node.item < item) {
node = node.getRightChild();
} else {
return node;
}
}
node = new TreeNode(item, parentNode);
//判断是左节点还是右节点;
if (parentNode.item > item) {
parentNode.setLiftChild(node);
} else if (parentNode.item < item) {
parentNode.setRightChild(node);
}
return node;
}
2)递归方法插入
void insertNodeRec(int item) {
if (isEmptyTree()) {
root = new TreeNode(item, null);
return;
}
insertNodeRec(item, root);
}
/**
* 递归的方法插入
*
* @param item
*/
void insertNodeRec(int item, TreeNode parentNode) {
TreeNode treeNode = parentNode;
if (treeNode.getItem() > item) {
if (treeNode.getLiftChild() != null) {
treeNode = treeNode.getLiftChild();
insertNodeRec(item, treeNode);
} else {
TreeNode node = new TreeNode(item, treeNode);
treeNode.setLiftChild(node);
}
} else if (treeNode.getItem() < item) {
if (treeNode.getRightChild() != null) {
treeNode = treeNode.getRightChild();
insertNodeRec(item, treeNode);
} else {
TreeNode node = new TreeNode(item, treeNode);
treeNode.setRightChild(node);
}
} else {
//存在该元素
}
}
搜索
-
深度优先搜索算法:深度优先搜索算法(Depth First Search),是搜索算法的一种。是沿着树的深度遍历树的节点,尽可能深的搜索树的分支;分为前序遍历(先中 再左 后右),中序遍历(先左 再中 后右),后序遍历(先左 再右 后中);
递归的方式,不断往下找;
非递归的方式,采用栈的思想:先进后出;
1)递归的方式
public void preOderSearch(TreeNode node) {
if (node == null) {
return;
}
Log.e(TAG, node.getItem() + "");
preOderSearch(node.getLiftChild());
preOderSearch(node.getRightChild());
}
2)非递归的方式
public void preNotRecSearch(TreeNode node) {
TreeNode treeNode = node;
Stack<TreeNode> stack = new Stack<>();
while (treeNode != null || !stack.isEmpty()) {
//遍历 找到该节点的左子树的左节点,并且加入栈中
while (treeNode != null) {
Log.e(TAG, treeNode.getItem() + "");
stack.push(treeNode);
treeNode = treeNode.getLiftChild();
}
//取出栈顶的节点(最新加入的)
if (!stack.isEmpty()) {
//此时treeNode可能为null,但是stack依然有元素,要在最外层while加上!stack.isEmpty();
treeNode = stack.pop().getRightChild();
}
}
}
-
广度优先搜索算法:是从根节点开始,沿着树的宽度遍历树的节点。如果所有节点均被访问,则算法中止。借助队列数据结构,由于队列是先进先出的顺序,因此可以先将左子树入队,然后再将右子树入队。
public void breadthFirstSearch(TreeNode node) { if (node == null) { return; } LinkedBlockingQueue<TreeNode> queue = new LinkedBlockingQueue<>(); //现将开始节点 入队 queue.offer(node); while (!queue.isEmpty()) { //出队 TreeNode poll = queue.poll(); Log.e(TAG, poll.getItem() + ""); //把出队节点的左右子节点加入到队列中 if (poll.getLiftChild() != null) { queue.offer(poll.getLiftChild()); } if (poll.getRightChild() != null) { queue.offer(poll.getRightChild()); } } }
删除
删除对应的节点
1)子节点;直接删除;
2)只有左子节点;删除对应的节点,左子节点顶上;
3)只有右子节点;删除对应的节点,右子节点顶上;
4)左右子节点都有;删除该节点,其后序节点顶上;
public void remove(int item) { TreeNode node = root; //空树 if (node == null) { return; } TreeNode treeNode = getTreeNode(node, item); deleteTreeNode(treeNode); }
1)首先通过 getTreeNode()方法找到对应的节点
/** * 找到对应的节点;如果存在则返回对应的节点,不存在则返回null; * * @param node root节点 * @param item 目标值 * @return */ private TreeNode getTreeNode(TreeNode node, int item) { if (node == null) { return null; } if (node.item > item) { node = getTreeNode(node.getLiftChild(), item); } else if (node.item < item) { node = getTreeNode(node.getRightChild(), item); } return node; }
2)删除节点
private void deleteTreeNode(TreeNode treeNode) { if (treeNode == null) { return; } //删除的节点是叶子节点 if (isLeafNode(treeNode)) { deleteLeafNode(treeNode); } else if (onlyLeftChild(treeNode)) { deleteHasOnlyChildNode(treeNode, true); } else if (onlyRightChild(treeNode)) { deleteHasOnlyChildNode(treeNode, false); } else { deleteHasChildrenNode(treeNode); } } /** * 删除 同时有左子节点,右子节点的节点 * 1:要找到该节点的后序节点:即找到该节点的右子树的最左节点 * 2:对最左节点根据前三种情况判断 * * @param treeNode */ private void deleteHasChildrenNode(TreeNode treeNode) { TreeNode leftNode = getLeftNode(treeNode.getRightChild()); deleteTreeNode(leftNode); treeNode.item = leftNode.item; } /** * 找到该节点的右子树的最左节点 * * @param treeNode */ private TreeNode getLeftNode(TreeNode treeNode) { while (treeNode.getLiftChild() != null) { treeNode = treeNode.getLiftChild(); } return treeNode; } /** * 删除只有一个子节点的节点 * * @param treeNode * @param left */ private void deleteHasOnlyChildNode(TreeNode treeNode, boolean left) { TreeNode parent = treeNode.getParent(); TreeNode childNode; if (left) { childNode = treeNode.getLiftChild(); parent.setLiftChild(childNode); } else { childNode = treeNode.getRightChild(); parent.setRightChild(childNode); } childNode.setParent(parent); treeNode.setParent(null); } /** * 删除叶子结点 * * @param treeNode */ private void deleteLeafNode(TreeNode treeNode) { TreeNode parent = treeNode.getParent(); if (treeNode.item < parent.item) { parent.setLiftChild(null); } else { parent.setRightChild(null); } treeNode.setParent(null); } private boolean onlyLeftChild(TreeNode treeNode) { return (treeNode.getLiftChild() != null && treeNode.getRightChild() == null); } private boolean onlyRightChild(TreeNode treeNode) { return (treeNode.getLiftChild() == null && treeNode.getRightChild() != null); } /** * 当前节点是叶子节点 * * @param treeNode * @return */ private boolean isLeafNode(TreeNode treeNode) { return (treeNode.getRightChild() == null && treeNode.getLiftChild() == null); }
以上就是查找二叉树的主要方法;如有问题,请多指教,谢谢!
点击下载源码