一、二叉樹
對於每次遞迴遍歷的時候,會產生一個遍歷序,也就是對於一個節點間,會進行三次訪問
可以在這三次中改變列印的位置。從而形成先序,中序,後序遍歷。
程式碼:
public static void OrderRecur(Node head) {
if (head == null) {
return;
}
//第一次訪問節點就輸出,System.out.print(head.value + " ");
OrderRecur(head.left);
//第二次訪問節點輸出 System.out.print(head.value + " ");
OrderRecur(head.right);
//第三次訪問節點輸出System.out.print(head.value + " ");
}
非遞迴遍歷
先序
/**
* 準備一個棧,將root壓入棧
* 1.彈出棧元素
* 2.列印
* 3.先壓入右節點,再壓入左節點(如果有的話)
* 4.迴圈上述步驟
* @param root
*/
public static void PreOrderWithoutRecursion(Node root){
//棧
Stack<Node> nodes = new Stack<>();
nodes.push(root);
while (!nodes.isEmpty()){
//彈出
Node pop = nodes.pop();
System.out.println(pop.value);
//壓入孩子節點
if (pop.right != null){
nodes.push(pop.right);
}
if (pop.left != null){
nodes.push(pop.left);
}
}
}
中序
/**
* 準備一個棧,將root壓入棧
* 1.一直將節點的左孩子壓入棧中
* 2.彈出列印
* 3.如果彈出的存在右孩子,也將右孩子的左孩子一直壓入棧
* 4,迴圈
*/
public static void InOrderWithoutRecursion(Node root){
//棧
Stack<Node> nodes = new Stack<>();
while (! nodes.isEmpty() || root != null){
//1.將左孩子全部壓入棧中
if (root != null){
nodes.push(root);
root = root.left;
}
else {
//2.彈出,列印
root = nodes.pop();
System.out.print(root.value+" ");
//繼續右孩子
root = root.right;
}
}
}
後續
/**
* 準備兩個棧,將root壓入棧
* 1.彈出,壓入到2棧中
* 2.將左孩子壓入1棧
* 3.將右孩子壓入1棧
* 4.一直到1棧為空,輸出2棧元素
*/
public static void PosOrderWithoutRecursion(Node root){
Stack<Node> stack1 = new Stack<>();
Stack<Node> stack2 = new Stack<>();
stack1.push(root);
while (!stack1.isEmpty()){
//彈出,壓縮2棧
Node pop = stack1.pop();
stack2.push(pop);
//分別壓入左孩子和有孩子
if (pop.left != null){
stack1.push(pop.left);
}
if (pop.right != null){
stack1.push(pop.right);
}
}
while ( !stack2.isEmpty()){
Node pop = stack2.pop();
System.out.print(pop.value+" ");
}
}
層次
/**
* 層次遍歷
* 準備一個佇列 ,加入根節點
* 1,彈出,列印
* 2.加入左孩子
* 3.加入右孩子
* 4.迴圈
*/
public static void WithOrder(Node root){
Queue<Node> queue = new LinkedList<>();
queue.add(root);
while (!queue.isEmpty()){
//彈出列印
Node poll = queue.poll();
System.out.print(poll.value+" ");
if (poll.left != null){
queue.add(poll.left);
}
if (poll.right != null){
queue.add(poll.right);
}
}
}
題
獲取最大寬度
使用層次遍歷
/**
* 獲取最大寬度 將根節點加入到佇列中
* 準備一個佇列,準備一個hashMap,用來儲存各個節點處於第幾層
* 1.先層次遍歷,在新增節點,要向map中新增節點處在都第幾層
* 2.在層次遍歷中,如果發現節點的層數和要統計的層數相同,就當前層數節點數++,並將最大值保留
* 不同則說明已經統計到下一層了,將統計的層數更新,將當前節點數更新
*/
public static int getMaxWith(Node root){
Queue<Node> queue = new LinkedList<>();
//各個節點的層數
Map<Node, Integer> map = new HashMap<>();
queue.add(root);
//root在第一層
map.put(root,1);
//當前層數
int curLevel = 1;
//當前層數的節點
int curLevelNodes = 0;
//最大值
int max = Integer.MIN_VALUE;
while (!queue.isEmpty()){
Node poll = queue.poll();
int curNodeLevel = map.get(poll);
//當前節點的層數等於當前層數
if (curNodeLevel == curLevel){
//節點數++
curLevelNodes++;
}
//已經到下一層
else {
//更新max
max = Math.max(max,curLevelNodes);
//更新層數
curLevel++;
//初始化當前層數節點數
curLevelNodes = 1;
}
if (poll.left != null){
map.put(poll.left, curLevel+1);
queue.add(poll.left);
}
if (poll.right!= null){
map.put(poll.right, curLevel+1);
queue.add(poll.right);
}
}
max = Math.max(curLevelNodes,max);
return max;
}
判斷是否為搜尋二叉樹(左子樹值小於根小於右子樹)
中序遍歷改寫
判斷是否為完全二叉樹
/**
* 判斷是否為完全二叉樹
* 滿足兩個條件
* 使用層次遍歷
* 1.如果節點有右孩子沒有左孩子,則不是
* 2.如果不是孩子雙全,並且滿足第一個條件,那麼接下來的節點就都是葉子節點
* @return
*/
public static boolean isCompletedBinaryTree(Node root){
Queue<Node> queue = new LinkedList<>();
queue.add(root);
Node leftChird = null;
Node rightChird = null;
//是否是孩子雙全
boolean sigleChild = false;
while (! queue.isEmpty()){
Node poll = queue.poll();
leftChird = poll.left;
rightChird = poll.right;
//如果滿足了第一個條件,並且當前節點不是葉節點
//或 只有右孩子沒有左孩子,
//則不是完全二叉樹
if (sigleChild && (leftChird != null || rightChird!= null)
||
(rightChird != null && leftChird == null)){
return false;
}
if (leftChird != null){
queue.add(leftChird);
}
if (rightChird != null){
queue.add(rightChird);
}
//如果孩子不雙全,則變為單個節點的狀態
if (leftChird == null || rightChird == null){
sigleChild = true;
}
}
return true;
}
判斷是否為平衡二叉樹(模板,遞迴)
/**
* 是否是平衡二叉樹
* 平衡二叉樹:左子樹是平衡二叉樹,右子樹是平衡二叉樹,子樹的高度差小於2
* 因此對於子樹而言,需要返回兩個值,是否是平衡樹,以及高度
*/
public static ResultType process(Node node){
if (node == null){
return new ResultType(true,0);
}
ResultType leftResult = process(node.left);
ResultType rightResult = process(node.right);
//高度
int height = Math.max(leftResult.height, rightResult.height)+1;
//平衡
boolean isBlance = Math.abs(leftResult.height- rightResult.height) < 2;
return new ResultType(isBlance,height);
}
public static boolean isBalanceBinaryTreeRecursion(Node root){
ResultType process = process(root);
return process.isBlance;
}
//返回結果封裝
public static class ResultType {
//是否平衡
private boolean isBlance;
//高度
private int height;
public ResultType(boolean isBlance, int height) {
this.isBlance = isBlance;
this.height = height;
}
}
判斷是否為滿二叉樹(模板,遞迴)
/**
* 判斷是否為滿二叉樹
* 模板:得到height,nodes
* (2^height)- 1 = nodes
*/
public static boolean isFullBinaryTree(Node root){
if (root == null){
return true;
}
ResultData resultData = f(root);
int height = resultData.height;
int nodes = resultData.nodes;
boolean isFull = (1 << height)-1 == nodes;
return isFull;
}
public static ResultData f(Node node){
if (node == null){
//空樹高度0,節點數0
return new ResultData(0,0);
}
ResultData leftRes = f(node.left);
ResultData ritRes = f(node.right);
int height = Math.max(leftRes.height, ritRes.height);
int nodes = leftRes.nodes+ ritRes.nodes;
return new ResultData(height,nodes);
}
//滿二叉樹封裝結果
public static class ResultData{
private int height;
private int nodes;
public ResultData(int height, int nodes) {
this.height = height;
this.nodes = nodes;
}
}
最低公共祖先節點
給定兩個二叉樹的節點node1和node2,找到他們的最低公共祖先節點
/**
* 準備一個map,儲存各個節點的父節點
* 準備一個set,將n1鏈的父節點進行儲存
* 遍歷n2的父節點過程中,如果在set中存在,則直接返回該點就是最終lca
*/
public static Node LCA(Node root,Node n1,Node n2){
//存放對應父節點
HashMap<Node,Node> map = new HashMap<>();
map.put(root,root);
process(root,map);
//存放n1的父節點鏈
HashSet<Node> set = new HashSet<>();
Node cur1 = n1;
Node cur2 = n2;
//一直到根節點
while (cur1 != map.get(cur1)){
set.add(cur1);
cur1 = map.get(cur1);
}
//加入根節點
set.add(root);
//在鏈中找n2的父節點們
while (!set.contains(cur2)){
cur2 = map.get(cur2);
}
return cur2;
}
//儲存父節點
public static void process(Node node, HashMap<Node,Node> map){
if (node == null){
return;
}
//新增父節點
map.put(node.left,node);
map.put(node.right,node);
process(node.left,map);
process(node.right,map);
}
方法二
/**
* lca方法二
* 對於每一個節點來說
* 如果左右子樹中存在n1或n2則返回
* 不存在則返回空
* 到根節點的時候,也就是回溯到最上層時候,如果左子樹不存在n1和n2,那麼返回右子樹最先出現的n1或n2就是lca
* 如果n1和n2分別出現在左右子樹
* 那麼根節點就是lca
*/
public static Node LCA2(Node root,Node n1,Node n2){
if (root == null || root == n1 || root == n2){
return root;
}
//尋找孩子
Node left = LCA2(root.left, n1, n2);
Node right = LCA2(root.right, n1, n2);
//如果n1和n2是兩個分支中
if (left != null && right != null){
return root;
}
return left!=null?left:right;
}
摺紙問題
請把一段紙條豎著放在桌子上,然後從紙條的下邊向上方對摺1次,壓出摺痕後 展開。
此時摺痕是凹下去的,即摺痕突起的方向指向紙條的背面。 如果從紙條的下邊向上方連續對摺2次,壓出摺痕後展開,此時有三條摺痕,從 上到下依次是下摺痕、下摺痕和上摺痕。
給定一個輸入引數N,代表紙條都從下邊向上方連續對摺N次。
請從上到下列印所有摺痕的方向。
例如:N=1時,列印: down N=2時,列印: down down up
public static void main(String[] args) {
int N = 3;
paperFold(N);
}
public static void process(int level,int N, boolean down){
if (level > N){
return;
}
process(level+1,N,true);
System.out.println(down?"down":"up");
process(level+1,N,false);
}
public static void paperFold(int N){
process(1,N,true);
}