二叉樹基礎(chǔ)知識(shí):可查看http://www.cnblogs.com/polly333/p/4740355.html
層序遍歷:依靠隊(duì)列,沒有遞歸解法??刹榭?a target="_blank" rel="nofollow">https://www.cnblogs.com/hapjin/p/5409921.html
前中后序遍歷:依靠棧,有遞歸和非遞歸兩種解法
1.求二叉樹根到葉節(jié)點(diǎn)的最短距離
https://leetcode-cn.com/problems/minimum-depth-of-binary-tree/description/
類似maximum-depth-of-binary-tree。不過(guò)注意,如果一個(gè)節(jié)點(diǎn)只有左子樹或只有右子樹。我們不能取左右子樹中最短的,會(huì)取到0(葉子節(jié)點(diǎn)指的是沒有子節(jié)點(diǎn)的節(jié)點(diǎn)),這樣不符合題意。所以二者其一為空時(shí),就取另一個(gè)的長(zhǎng)度,最為最短長(zhǎng)度。
遞歸解法:
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public int minDepth(TreeNode root) {
if(root==null){
return 0;
}
if(root.left==null){
return minDepth(root.right)+1;
}
if(root.right==null){
return minDepth(root.left)+1;
}
return Math.min(minDepth(root.right)+1,minDepth(root.left)+1);
}
}
非遞歸解法:用到了層序遍歷。
根節(jié)點(diǎn)入隊(duì)列。
然后在循環(huán)中判斷隊(duì)列非空時(shí),彈出隊(duì)列中的節(jié)點(diǎn),并把節(jié)點(diǎn)的子節(jié)點(diǎn)入隊(duì)列。
curNum用來(lái)記錄一層的節(jié)點(diǎn)數(shù)。
lastNum記錄這層還需要遍歷的節(jié)點(diǎn)數(shù)。當(dāng)lastNum為0時(shí),說(shuō)明這層已經(jīng)遍歷完,可以層數(shù)+1;
終止條件即為:找到了左右子樹都為空的節(jié)點(diǎn)。
如果是求maximum-depth,則終止條件是queue為空==》即所有的節(jié)點(diǎn)都被遍歷過(guò)。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public int minDepth(TreeNode root) {
if(root==null)return 0;
LinkedList<TreeNode> queue = new LinkedList<TreeNode>();
queue.add(root);
int curNum = 0;
int lastNum=1;
int level=1;
while(!queue.isEmpty()){
TreeNode temp = queue.poll();
lastNum--;
if(temp.left==null&&temp.right==null){
return level;
}
if(temp.left!=null){
queue.add(temp.left);
curNum++;
}
if(temp.right!=null){
queue.add(temp.right);
curNum++;
}
if(lastNum==0){
lastNum=curNum;
curNum=0;
level++;
}
}
return 0;
}
}
要點(diǎn):
- 遞歸解法:需要注意判斷子節(jié)點(diǎn)左右節(jié)點(diǎn)其中一個(gè)為空的情況
- 非遞歸解法:用linkedList作為隊(duì)列;記錄層中節(jié)點(diǎn)數(shù),遍歷完一層,層數(shù)+1;
關(guān)于LinkedList
- poll()和pop()都是取first并刪除,隊(duì)列為空時(shí)前者返回null,后者返回NoSuchElementException。本質(zhì)都是調(diào)用unLinkList()
- offer(),add()都是向隊(duì)列中添加元素到末尾。offer調(diào)了add。返回值為true,實(shí)際調(diào)用linkLast()。
push是添加元素到開頭。實(shí)際調(diào)用linkFirst()。無(wú)返回值
2.求二叉樹根到葉節(jié)點(diǎn)的最大距離
https://leetcode-cn.com/problems/maximum-depth-of-binary-tree/description/
非遞歸解法
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public int maxDepth(TreeNode root) {
if(root==null)return 0;
LinkedList<TreeNode> queue = new LinkedList<TreeNode>();
queue.add(root);
int curNum=0;
int lastNum=1;
int level =0;
while(!queue.isEmpty()){
TreeNode temp = queue.poll();
lastNum--;
if(temp.left!=null){
queue.add(temp.left);
curNum++;
}
if(temp.right!=null){
queue.add(temp.right);
curNum++;
}
if(lastNum==0){
lastNum=curNum;
curNum=0;
level++;
}
}
return level;
}
}
要點(diǎn):這次的level初始值為0。原因就在于max不會(huì)提前判斷葉子節(jié)點(diǎn),葉子節(jié)點(diǎn)會(huì)走到最后level++的里面,所以不需要直接+1
3.對(duì)稱二叉樹的判斷
https://leetcode-cn.com/problems/symmetric-tree/description/
遞歸解法
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public boolean isSymmetric(TreeNode root) {
if(root==null)return true;
return ST(root.left,root.right);
}
private boolean ST(TreeNode left,TreeNode right){
if(left==null&&right==null)return true;
else if(left==null||right==null)return false;
else if(left.val!=right.val)return false;
else{
return ST(left.left,right.right)&&ST(left.right,right.left);
}
}
}
要點(diǎn):遞歸式在于左樹的左孩子和右樹的右孩子判對(duì)稱;左樹的右孩子和右樹的左孩子判對(duì)稱。終止條件在于兩邊孩子都是null的時(shí)候,left.val=right.val即返回true。
時(shí)間復(fù)雜度: 本質(zhì)其實(shí)就是DFS,時(shí)間復(fù)雜度為O(n)
空間復(fù)雜度: O(h)
迭代解法
利用兩個(gè)隊(duì)列來(lái)完成。
要點(diǎn):對(duì)稱樹實(shí)際上是判斷左樹和右樹是否對(duì)稱。以根節(jié)點(diǎn)為中軸線,左邊的存入leftQueue,右邊的存入rightQueue;存入順序記得對(duì)稱,一一進(jìn)行比對(duì)
注意:此處不能直接左右為空返回true,因?yàn)樾枰M(jìn)一步的判斷
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public boolean isSymmetric(TreeNode root) {
if(root==null)return true;
return ST(root);
}
private boolean ST(TreeNode root){
if(root==null)return true;
LinkedList<TreeNode> leftQueue=new LinkedList<TreeNode>();
LinkedList<TreeNode> rightQueue=new LinkedList<TreeNode>();
leftQueue.add(root.left);
rightQueue.add(root.right);
while(!leftQueue.isEmpty()&&!rightQueue.isEmpty()){
TreeNode left=leftQueue.poll();
TreeNode right=rightQueue.poll();
if(left==null&&right==null)continue;
else if(left==null&&right!=null||right==null&&left!=null)return false;
else if(left.val!=right.val)return false;
else{
leftQueue.add(left.left);
leftQueue.add(left.right);
rightQueue.add(right.right);
rightQueue.add(right.left);
}
}
return true;
}
}
4.N叉樹后序遍歷
迭代解法:
1.用棧
2.打印條件是:沒有孩子(最終節(jié)點(diǎn))或者上一個(gè)節(jié)點(diǎn)是它的孩子(孩子節(jié)點(diǎn)打完,需要打印上層節(jié)點(diǎn))
3.放入時(shí)從右向左放節(jié)點(diǎn),與讀取順序相反
如果是前序遍歷,則不需要if判斷,直接pop打印即可
/*
// Definition for a Node.
class Node {
public int val;
public List<Node> children;
public Node() {}
public Node(int _val,List<Node> _children) {
val = _val;
children = _children;
}
};
*/
class Solution {
public List<Integer> postorder(Node root) {
List<Integer> resultList = new LinkedList<Integer>();
if(root==null)return resultList;
Stack<Node> stack =new Stack<Node>();
stack.push(root);
Node pre=null;
Node cur=null;
while(!stack.isEmpty()){
cur = stack.peek();
if(pre!=null && cur.children.contains(pre) || cur.children.isEmpty()){
resultList.add(cur.val);
stack.pop();
pre=cur;
}else{
int length=cur.children.size();
for(int i=0;i<cur.children.size();i++){
stack.push(cur.children.get(length-i-1));
}
}
}
return resultList;
}
}
遞歸解法
dfs,會(huì)遍歷到最下面的節(jié)點(diǎn),然后打印。保證先打出來(lái)的就是最下層。而且是先遍歷到左節(jié)點(diǎn)的最下層,才會(huì)遍歷右節(jié)點(diǎn)。
如果是前序遍歷,則把 res.add(root.val);提到for循環(huán)之前
class Solution {
public void dfs(List<Integer> res,Node root){
if(root==null){
return;
}
for(Node x:root.children){
dfs(res,x);
}
res.add(root.val);
}
public List<Integer> postorder(Node root) {
List<Integer> res = new ArrayList<Integer>();
dfs(res,root);
return res;
}
}
5.中序遍歷
要點(diǎn):
1.使用棧
2.入棧當(dāng)前節(jié)點(diǎn),并將左子節(jié)點(diǎn)置為下個(gè)遍歷節(jié)點(diǎn)。while循環(huán)結(jié)束時(shí),所有的左子節(jié)點(diǎn)都入棧。
3.出棧時(shí),pop節(jié)點(diǎn)打印。并將右子節(jié)點(diǎn)設(shè)為下個(gè)遍歷節(jié)點(diǎn)。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
List<Integer> res = new LinkedList<Integer>();
public List<Integer> inorderTraversal(TreeNode root) {
if(root==null)return res;
Stack<TreeNode> stack = new Stack<TreeNode>();
TreeNode cur=root;
while(cur!=null||!stack.isEmpty()){
while(cur!=null){
stack.push(cur);
cur=cur.left;
}
//取出節(jié)點(diǎn),并把當(dāng)前節(jié)點(diǎn)設(shè)成取出節(jié)點(diǎn)的右子節(jié)點(diǎn)
if(!stack.isEmpty()){
cur=stack.pop();
res.add(cur.val);
cur=cur.right;
}
}
return res;
}
}
6.層次遍歷
遞歸方式
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> result = new ArrayList<>();
set(root, 0, result);
return result;
}
public void set(TreeNode treeNode, int level, List<List<Integer>> result) {
if(treeNode==null){
return;
}
if(level==result.size()){
result.add(new ArrayList<>());
}
result.get(level).add(treeNode.val);
set(treeNode.left,level+1,result);
set(treeNode.right,level+1,result);
}
}
非遞歸方式
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
List<List<Integer>> res = new LinkedList<List<Integer>>();
public List<List<Integer>> levelOrder(TreeNode root) {
LinkedList<TreeNode> queue = new LinkedList<TreeNode>();
if(root==null)return res;
queue.add(root);
int curNum=0;
int lastNum=1;
while(!queue.isEmpty()){
List<Integer> tempList = new LinkedList<Integer>();
while(lastNum!=0){
TreeNode temp = queue.pop();
tempList.add(temp.val);
if(temp.left!=null){
queue.add(temp.left);
curNum++;
}
if(temp.right!=null){
queue.add(temp.right);
curNum++;
}
lastNum--;
}
lastNum=curNum;
curNum=0;
res.add(tempList);
}
return res;
}
}
變形題:二叉樹的右視圖
https://leetcode-cn.com/problems/binary-tree-right-side-view/description/
只取每層的最后一個(gè)節(jié)點(diǎn)
7.翻轉(zhuǎn)二叉樹
https://leetcode-cn.com/problems/invert-binary-tree/description/
遞歸解法:
1.翻轉(zhuǎn)左右節(jié)點(diǎn)
2.翻轉(zhuǎn)左右子樹
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public TreeNode invertTree(TreeNode root) {
if(root==null)return null;
TreeNode temp=root.right;
root.right=root.left;
root.left=temp;
invertTree(root.left);
invertTree(root.right);
return root;
}
}
8.平衡二叉樹的判斷
遞歸方式:
用maximum-depth的方法求樹高度。
判斷根節(jié)點(diǎn)的左右兩子樹高度差是否大于1,若大于1則非平衡樹,返回false;否則,繼續(xù)遞歸的判斷其左子樹和右子樹是否是平衡樹。
這樣做會(huì)重復(fù)遍歷多次節(jié)點(diǎn)。求一個(gè)節(jié)點(diǎn)的深度是O(lgn),所以求所有節(jié)點(diǎn)的就是O(nlgn)。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public boolean isBalanced(TreeNode root) {
if(root==null)return true;
int left = treeDepth(root.left);
int right = treeDepth(root.right);
if(Math.abs(left-right)>1){
return false;
}
return isBalanced(root.left)&&isBalanced(root.right);
}
private int treeDepth(TreeNode root){
if(root==null)return 0;
int left = treeDepth(root.left);
int right= treeDepth(root.right);
return Math.max(left,right)+1;
}
}
上面那個(gè)方法正確但不是很高效,因?yàn)?strong>每一個(gè)點(diǎn)都會(huì)被上面的點(diǎn)計(jì)算深度時(shí)訪問一次,我們可以進(jìn)行優(yōu)化。方法是如果我們發(fā)現(xiàn)子樹不平衡,則不計(jì)算具體的深度,而是直接返回-1。那么優(yōu)化后的方法為:對(duì)于每一個(gè)節(jié)點(diǎn),我們通過(guò)checkDepth方法遞歸獲得左右子樹的深度,如果子樹是平衡的,則返回真實(shí)的深度,若不平衡,直接返回-1,此方法時(shí)間復(fù)雜度O(N),空間復(fù)雜度O(H),參見代碼如下:
要點(diǎn)在于計(jì)算高度的時(shí)候順便算出是否平衡,同一個(gè)節(jié)點(diǎn)不需要遍歷兩次
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public boolean isBalanced(TreeNode root) {
int height = checkDepth(root);
if(height>=0){
return true;
}else{
return false;
}
}
private int checkDepth(TreeNode root){
if(root==null)return 0;
int left = checkDepth(root.left);
int right= checkDepth(root.right);
if(left==-1||right==-1)return -1;
if(Math.abs(left-right)>1){
return -1;
}else{
return Math.max(left,right)+1;
}
}
}
9.二叉樹剪枝
https://leetcode-cn.com/problems/binary-tree-pruning/description/
思路:
1.如果本身為1,保留這個(gè)節(jié)點(diǎn)。對(duì)其左右節(jié)點(diǎn)進(jìn)行剪枝
2.如果左右節(jié)點(diǎn)剪枝不為空,那么保留這個(gè)節(jié)點(diǎn)
3.如果左右節(jié)點(diǎn)剪枝為空,說(shuō)明節(jié)點(diǎn)為0,左右節(jié)點(diǎn)剪枝結(jié)果為null,不保留這個(gè)節(jié)點(diǎn)
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public TreeNode pruneTree(TreeNode root) {
if(root==null)return null;
TreeNode newRoot;
TreeNode left = pruneTree(root.left);
TreeNode right = pruneTree(root.right);
if(root.val==1){
newRoot = new TreeNode(root.val);
newRoot.left = left;
newRoot.right= right;
}else if(left!=null||right!=null){
newRoot = new TreeNode(root.val);
newRoot.left = left;
newRoot.right= right;
}else{
newRoot=null;
}
return newRoot;
}
}
二叉搜索樹
https://blog.csdn.net/qq_37887537/article/details/75647670
1.將有序數(shù)組轉(zhuǎn)化為二叉搜索樹
https://leetcode-cn.com/problems/convert-sorted-array-to-binary-search-tree/description/
二叉搜索樹按照中序遍歷可得到有序數(shù)組;那么反之,我們可以得知,根節(jié)點(diǎn)是有序數(shù)組的中間節(jié)點(diǎn),從中間節(jié)點(diǎn)分開左右兩個(gè)有序數(shù)組,這兩個(gè)有序數(shù)組的中間節(jié)點(diǎn)又分別為中間節(jié)點(diǎn)的左右子節(jié)點(diǎn)。這就是二分查找的中心思想啊。
思路:
用二分查找法,找到根節(jié)點(diǎn),然后左子節(jié)點(diǎn)為左區(qū)間的中間節(jié)點(diǎn);右子節(jié)點(diǎn)為右區(qū)間的中間節(jié)點(diǎn)。遞歸而成
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public TreeNode sortedArrayToBST(int[] nums) {
return midSortToBST(nums,0,nums.length-1);
}
private TreeNode midSortToBST(int[] nums,int left,int right){
if(left>right)return null;
int mid = (left+right)/2;
TreeNode cur = new TreeNode(nums[mid]);
cur.left = midSortToBST(nums,left,mid-1);
cur.right = midSortToBST(nums,mid+1,right);
return cur;
}
}
2.二叉搜索樹剪枝
https://leetcode-cn.com/problems/trim-a-binary-search-tree/description/
二叉搜索樹不一定是平衡樹。
思路:
1.如果root值小于L,則拋棄左子樹,返回右子樹的剪枝結(jié)果
2.如果root值大于R,則拋棄右子樹,返回左子樹的剪枝結(jié)果
3.如果root值介于L與R之間,則根為root的值,左右節(jié)點(diǎn)分別是左右子樹的剪枝結(jié)果
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public TreeNode trimBST(TreeNode root, int L, int R) {
if(root==null)return null;
if(root.val<L){
return trimBST(root.right,L,R);
}else if(root.val>R){
return trimBST(root.left,L,R);
}else{
TreeNode cur = new TreeNode(root.val);
cur.left = trimBST(root.left,L,R);
cur.right = trimBST(root.right,L,R);
return cur;
}
}
}