演算法基礎學習2

橡皮筋兒發表於2021-11-27

一、二叉樹

對於每次遞迴遍歷的時候,會產生一個遍歷序,也就是對於一個節點間,會進行三次訪問

可以在這三次中改變列印的位置。從而形成先序,中序,後序遍歷。

程式碼:

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);
}

相關文章