递归与迭代的区别
递归与迭代都是基于控制结构:迭代用重复结构,而递归用选择结构。
举个例子吧:你要给某个小孩子买玩具。
递归: 你自己不太了解小孩子的需求,为了缩小范围,让你的儿子去给孙子挑选。儿子比你强点有限,但依然不太了解小孩子的需求。为了缩小范围,你又让你孙子去挑选。如此这般,直到找到合适的玩具。**
迭代:** 你挑了一件觉得不行,又挑了一件又不行。如此这般,直到找到合适的玩具。**
所以一句话:递归是自己调用自己,每次旨在缩小问题规模。迭代是自己执行很多次,每次旨在更接近目标。**
迭代是逐渐逼近,用新值覆盖旧值,直到满足条件后结束,不保存中间值,空间利用率高。
递归是将一个问题分解为若干相对小一点的问题,遇到递归出口再原路返回,因此必须保存相关的中间值,这些中间值压入栈保存,问题规模较大时会占用大量内存。
二叉树
二叉树的定义
public class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode() {}
TreeNode(int val) { this.val = val; }
TreeNode(int val, TreeNode left, TreeNode right) {
this.val = val;
this.left = left;
this.right = right;
}
}
二叉树的前中后序遍历(迭代版)
前序遍历(中左右)
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> res=new ArrayList<>();
if(root==null){
return res;
}
Deque<TreeNode> stack = new LinkedList<>(); //易错点
stack.push(root);
while(!stack.isEmpty()){
TreeNode temp =stack.pop();
res.add(temp.val);
if(temp.right!=null){ //易错点:先右边入栈,然后左边入栈,这样才能中左右的出栈
stack.push(temp.right);
}
if(temp.left!=null){
stack.push(temp.left);
}
}
return res;
}
后序遍历(左右中)
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> res=new ArrayList<>();
if(root==null){
return res; //易错点
}
Deque<TreeNode> stack = new LinkedList<>(); //易错点
stack.push(root);
while(!stack.isEmpty()){
TreeNode temp =stack.pop();
res.add(temp.val);
if(temp.left!=null){ //易错点:先左边入栈,然后右边入栈,这样才能中右左的出栈
stack.push(temp.left);
}
if(temp.right!=null){
stack.push(temp.right);
}
}
Collections.reverse(res); //易错点:中右左的出栈后要倒序
return res;
}
中序遍历(左中右)
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
if(root==null){
return res;
}
Deque<TreeNode> stack = new LinkedList<>();
TreeNode cur = root;
while(!stack.isEmpty() || cur!=null){
while(cur!=null){ //处理左边
stack.push(cur);
cur=cur.left;
}
cur=stack.pop();
res.add(cur.val);
cur=cur.right; //处理右边
}
return res;
}
二叉树的深度
(那么简单我都想不到,递归办法,深度优先搜索)
public int maxDepth(TreeNode root) {
if(root==null){
return 0;
}
else{
int m = maxDepth(root.left);
int n = maxDepth(root.right);
int len = m>n?m+1:n+1;
return len;
}
}
二叉树的重建
根据前序遍历和中序遍历重建树:再后序遍历得到后序遍历
public TreeNode buildTree(int[] preorder, int[] inorder) {
if (preorder == null || preorder.length == 0 || inorder == null || inorder.length == 0 || preorder.length != inorder.length) {
return null;
}
return help(preorder, 0, preorder.length - 1, inorder, 0, inorder.length - 1);
}
private TreeNode help(int[] preorder, int pStart, int pEnd, int[] inorder, int iStart, int iEnd) {
//递归的第一步:递归终止条件,避免死循环
if (pStart > pEnd || iStart > iEnd) {
return null;
}
//重建根节点
TreeNode treeNode = new TreeNode(preorder[pStart]);
int index = 0; //index找到根节点在中序遍历的位置
while (inorder[iStart + index] != preorder[pStart]) {
index++;
}
//重建左子树
treeNode.left = help(preorder, pStart + 1, pStart + index, inorder, iStart, iStart + index - 1);
//重建右子树
treeNode.right = help(preorder, pStart + index + 1, pEnd, inorder, iStart + index + 1, iEnd);
return treeNode;
}
(TODO) 根据后序遍历和中序遍历重建树:再前序遍历得到前序遍历
不能有前序和后序重建树,因为不能对左右子树进行划分。
二叉树的翻转
public static TreeNode invertTree(TreeNode root) {
if(root == null) return null;
TreeNode left = invertTree(root.left);
TreeNode right = invertTree(root.right);
root.left = right;
root.right = left;
return root;
}