(二)分治算法
一、基本概念
分治即分而治之,特征在于将一个复杂的问题分成两个或更多个相同的子问题,再把子问题分成更小的问题,直到问题不可拆分可以直接求解,则原问题的解即子问题的解的合并,而这个技巧是很多高效算法的基础,如排序算法,傅立叶变换等。
二、设计思想
分治思想:将一个难以求解的大问题分割成一个规模较小的相同问题,以便各个击破,分而治之。
分治设计:(1)给出一个分治算法的终止条件;
(2)将大问题分割成多个规模较小的相同问题,递归求解。
三、适用场景
分治算法能够解决的问题场景必须满足以下两大特征:
(1)所解决的问题可以分解成多个相同的独立的子问题,不存在公共的子子问题;
(2)当所解决的问题分解到一定程度可以解决。
四、场景分析
下面根据一道根据前序遍历和中序遍历推导树结构的题为例进行说明,原题表述如下:
输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。
4.1 概念补充
前序、中序、后序指的是节点的位置,先输出节点数据为前序,中间输出为中序,最后输出为后序,但始终保持左右节点数据输出次序的不变性。
前序遍历:对于当前节点,先输出该节点数据,然后输出他的左节点数据,最后输出他的右节点数据。
中序遍历:对于当前节点,先输出该节点的左节点数据,然后输出该节点数据,最后输出他的右节点数据。
4.2 分治分析
分析三种情况;
(1)不存在树,则前序遍历序列为空,返回null即可;
(2)仅存在一个根节点树,则前序遍历序列仅一个元素,构建一个节点返回即可;
(3)不仅存在一个根节点,且至少存在一个子节点,这里我们考虑更一般的情况,即左右节点假设都存在,则前序遍历的第一个节点必然是根节点,由中序遍历概念可知,根节点在中序遍历的位置以左必然是左子树的中序遍历结果,以右必然是右子树的中序遍历结果,再回到前序遍历上,中序遍历中假设左子树节点的数目为n,则前序遍历中[1,n]必然是左子树的前序遍历序列,其余元素必然是右子树的前序遍历序列,因此这里通过分析就将前序遍历序列拆分成了根节点+(左子树的前序遍历序列+左子树的中序遍历序列)+(右子树的前序遍历序列+右子树的中序遍历序列),而左子树的前序遍历序列+左子树的中序遍历序列重建二叉树与当前是同一个问题,因而递归即可,同样右子树的重建也一样。
代码示意如下所示:
* public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode(int x) { val = x; } * } */ import java.util.*; public class Solution { /** * @param pre 前序数组 * @param in 中序数组 * @return 树根节点 */ public TreeNode getOriginalTree(ArrayList<Integer> pre,ArrayList<Integer> in){ // 基准条件:考虑不存在节点和存在一个节点的特殊情况 // 分析一:不存在节点 if(in.isEmpty()){ return null; } // 分析二:仅存在一个根节点 if(in.size()==1){ TreeNode finalNode = new TreeNode(in.get(0)); finalNode.left=null; finalNode.right=null; return finalNode; } // 分析三:至少存在一个子节点 TreeNode treeNode = new TreeNode(pre.get(0));
// 分离出左子树和右子树的前序遍历和中序遍历,并分别返回对应的根节点,最后构成一个完整的二叉树 int index=0; for(int i=0;i<in.size();i++){ if(in.get(i)==treeNode.val){ index=i; break; } } ArrayList<Integer> left = new ArrayList<Integer>(); ArrayList<Integer> right = new ArrayList<Integer>(); for(int i=0;i<in.size();i++){ if(i<index){ left.add(in.get(i)); } if(i>index){ right.add(in.get(i)); } } ArrayList<Integer> preLeft = new ArrayList<Integer>(); ArrayList<Integer> preRight = new ArrayList<Integer>(); for(int i=0;i<pre.size();i++){ if(i<=index){ preLeft.add(pre.get(i)); } if(i>index){ preRight.add(pre.get(i)); } } preLeft.remove(0); treeNode.left=getOriginalTree(preLeft, left); treeNode.right=getOriginalTree(preRight, right); return treeNode; } }