二叉树的深搜和广搜

二叉树是计算机中一个重要的数据结构,在这里主要谈一下二叉树的深度优先搜索(DFS)广度优先搜索(BFS)

所谓DFS,就是沿着树的深度一直往下,一直到达一个叶子节点,然后再返回遍历剩余的节点。根据树的性质,树结构不存在环,因此遍历的时候不需要标记。如果在遍历一个图的时候,因为图中有环的存在,因此需要标记访问过的节点,以防止程序进入死循环。言归正传,树的DFS有三种方式,分别为:前序遍历,中序遍历,和后序遍历。根据这个性质,我们很容易想到用堆栈完成DFS。遍历的时候我们将元素压栈,利用堆栈后进先出的性质来实现不同的遍历。
 

所谓前序遍历,就是每次都先访问根节点,然后依次为左子树,和右子树(根->左->右)。根据前序遍历的规定,左子树先于右子树,依次压栈的时候从右子树开始。代码如下:

public class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;
    TreeNode(int x) { val = x; }
 }

public class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> list = new ArrayList<Integer>();
        Stack<TreeNode> stack = new Stack<TreeNode>();
        if(root == null) return list;
        stack.push(root);

        while(!stack.empty()) {
            TreeNode node = stack.pop();
            list.add(node.val);
            if(node.right != null) 
                stack.push(node.right);
            if(node.left != null)
                stack.push(node.left);
        }
        return list;
    }
}

中序遍历是从左子树开始,然后依次为根节点,右子树(左->根->右),注意这里的根节点是指的每个子树的根结点。代码如下:

public class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;
    TreeNode(int x) { val = x; }
 }

public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> list = new ArrayList<Integer>();
        Stack<TreeNode> stack = new Stack<TreeNode>();
        if (root == null) return list;
        while(!stack.empty() || root != null) {
            if(root != null){
                stack.push(root);
                root = root.left;
            } else {
                root = stack.pop();
                list.add(root.val);
                root = root.right;
            }
        }
        return list;
 }

后序遍历是先遍历左子树,然后依次为右子树,根节点。因为根节点在最后遍历,所以在程序完成时如果按照左右根的方式会比较繁琐。我们不妨换一下思路,在完成前序遍历后,我们发现前序遍历是根->左->右,后序遍历时左->右->根,如果我们按照根->右->左的方式进行遍历,然后把结果倒转,就是左->右->根了。代码如下:

public class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;
    TreeNode(int x) { val = x; }
 }

  public List<Integer> postorderTraversal(TreeNode root) {
        LinkedList<Integer> list = new LinkedList<Integer>();
        Stack<TreeNode> stack = new Stack<TreeNode>();
        if(root == null)
            return list;
        stack.push(root);
        while(!stack.empty()) {
            TreeNode node = stack.pop();
            list.addFirst(node.val);
            if(node.left != null)
                stack.push(node.left);
            if(node.right != null)
                stack.push(node.right);
        }
        return list;
 }

以上我们介绍了二叉树的三种遍历方式,它们都属于深度优先遍历,对于深度优先遍历,我们都用堆栈实现。接下来我们介绍一下二叉树的广度优先遍历。

BFS,即广度优先遍历,它时沿着树的宽度进行遍历,从第一层直到最后一层,一直到遍历完树中所有的节点。对于BFS,我们通常用队列来实现,队列先进先出的性质正好满足BFS的要求。
我们先从根节点开始,然后加入到队列中,依次为左子树,右子树,然后每次从队列中取出一个节点,都一次访问它的左孩子和右孩子,直到遍历完所有的节点。代码如下:

public class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;
    TreeNode(int x) { val = x; }
 }

public void BFS(TreeNode root) {
	List<Integer> list = new ArrayList<Integer>();
        Queue<TreeNode> queue = new LinkedList<TreeNode>();
        if(root == null) return list;
        queue.offer(root);

        while(!queue.isEmpty()) {
            TreeNode node = queue.poll();
            list.add(node.val);
            if(node.left != null)
                queue.offer(node.left);
            if(node.right != null)
                queue.offer(node.right);
        }
        return list;
}

 

遍历分为:
1.深度(Depth)优先搜索DFS:一个递归过程,有回退过程。尽可能“深”地搜索图。在深度优先搜索中,对于最新发现的顶点,如果它还有以此为起点而未探测到的边,就沿此边继续搜索下去。当结点V的所有边都已被探寻过,搜索将回溯到发现结点V有那条边的始结点,则选择其中一个作为源结点并重复以上过程,整个进程反复进行直到所有结点都被发现为止。
2.广度(Breath)优先搜索BFS:一个分层的搜索过程,没有回退过程,是非递归的。只是每次都尽可能地扩展当前节点的邻居节点,之后再向其子结点进行扩展。

应用上的区别:
BFS 常用于找单一的最短路线,它的特点是 “搜到就是最优解”,而 DFS 用于找所有解的问题,它的空间效率高,而且找到的不一定是最优解,必须记录并完成整个搜索,故一般情况下,深搜需要非常高效的剪枝
BFS:对于解决最短或最少问题特别有效,而且寻找深度小,但缺点是内存耗费量大(需要开大量的数组单元用来存储状态)。
DFS:对于解决遍历和求所有问题有效,对于问题搜索深度小的时候处理速度迅速,然而在深度很大的情况下效率不高。

实现时用到的数据结构:
深度优先搜索用栈(stack)来实现,整个过程可以想象成一个倒立的树形:
1、把根节点压入栈中。
2、每次从栈中弹出一个元素,搜索所有在它下一级的元素,把这些元素压入栈中。并把这个元素记为它下一级元素的前驱。
3、找到所要找的元素时结束程序。
4、如果遍历整个树还没有找到,结束程序。
当然DFS也可以直接利用函数进行递归。
广度优先搜索使用队列(queue)来实现,整个过程也可以看做一个倒立的树形:
1、把根节点放到队列的末尾。
2、每次从队列的头部取出一个元素,查看这个元素所有的下一级元素,把它们放到队列的末尾。并把这个元素记为它下一级元素的前驱。
3、找到所要找的元素时结束程序。
4、如果遍历整个树还没有找到,结束程序。
BFS根据实际情况可能要用到优先队列。

 

// 递归中序遍历
public void inOrder(BinaryTreeNode current) {
	if (current != null) {
		inOrder(current.llink);
		System.out.print(current.info);
		inOrder(current.rlink);
	}
}

// 非递归中序遍历
public void nonRecursiveInOrder() {
	Stack<BinaryTreeNode> stack = new Stack<BinaryTreeNode>();
	BinaryTreeNode current;
	current = root;
	while ((current != null) || (!stack.empty())) {
		if (current != null) {
			stack.push(current);
			current = current.llink;
		} else {
			current = (BinaryTreeNode) stack.peek();
			stack.pop();
			System.out.print(current.info);
			current = current.rlink;
		}
	}
}

// 递归前序遍历
public void preOrder(BinaryTreeNode current) {
	if (current != null) {
		System.out.print(current.info);
		preOrder(current.llink);
		preOrder(current.rlink);
	}
}

// 非递归前序遍历
public void nonRecursivePreOrder() {
	Stack<BinaryTreeNode> stack = new Stack<BinaryTreeNode>();
	BinaryTreeNode current;
	current = root;
	while ((current != null) || (!stack.isEmpty())) {
		if (current != null) {
			System.out.print(current.info);
			stack.push(current);
			current = current.llink;
		} else {
			current = (BinaryTreeNode) stack.peek();
			stack.pop();
			current = current.rlink;
		}
	}
}

// 递归后序遍历
public void postOrder(BinaryTreeNode current) {
	if (current != null) {
		postOrder(current.llink);
		postOrder(current.rlink);
		System.out.print(current.info);
	}
}

// 非递归后序遍历
public void notRecursivePostOrder() {
	Stack<BinaryTreeNode> stack = new Stack<BinaryTreeNode>();
	BinaryTreeNode current;
	current = root;
	while ((current != null) || (!stack.isEmpty())) {
		if (current != null) {
			current.isFirst = true;
			stack.push(current);
			current = current.llink;
		} else {
			current = stack.peek();
			stack.pop();
			if (current.isFirst) {
				current.isFirst = false;
				stack.push(current);
				current = current.rlink;
			} else {
				System.out.print(current.info);
				current = null;
			}
		}
	}
}

// 树的层次遍历
public void bfs() {
	Queue<BinaryTreeNode> queue = new LinkedList<BinaryTreeNode>();
	BinaryTreeNode current;
	current = root;
	while ((current != null) || (!queue.isEmpty())) {
		if (current != null) {
			System.out.print(current.info);
			queue.add(current.llink);
			queue.add(current.rlink);
			current = queue.poll();
		} else {
			current = queue.poll();
		}
	}
}

public class BinaryTreeNode {
	private BinaryTreeNode llink = null;
	private BinaryTreeNode rlink = null;
	private int info;
	private boolean isFirst;

	public BinaryTreeNode(int info) {
		this.info = info;
	}
}

public void initTree() {
	root = new BinaryTreeNode(1);
	root.llink = new BinaryTreeNode(2);
	root.rlink = new BinaryTreeNode(3);
	root.llink.llink = null;
	root.llink.rlink = new BinaryTreeNode(4);
}

public BinaryTreeNode getRoot() {
	return root;
}

public void setRoot(BinaryTreeNode root) {
	this.root = root;
}

public static void main(String[] args) {
	Tree tree = new Tree();
	tree.initTree();
	// tree.preOrder(tree.getRoot());
	// tree.nonRecursivePreOrder();
	// tree.inOrder(tree.getRoot());
	// tree.nonRecursiveInOrder();
	// tree.postOrder(tree.getRoot());
	// tree.notRecursivePostOrder();
	tree.bfs();
}

 

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值