天天看點

面試中遇到的手撕代碼(二)

8.棧的最大值問題

問題的描述和思路可以參考這裡

Java實作代碼如下:

import java.util.Stack;

/**
 * 棧的最大值問題 Title: Description: Company:
 * 
 * @author 鄭偉
 * @date 2018年4月12日下午8:57:29
 */
public class SpecialStack {

    Stack<Integer> stack = new Stack<>();
    Stack<Integer> maxStack = new Stack<>();

    public void push(Integer num) {
        if (stack.isEmpty()) {
            maxStack.push(num);
            stack.push(num - maxStack.peek());
        } else {
            stack.push(num - maxStack.peek());
            if (num > maxStack.peek()) {
                maxStack.pop();
                maxStack.push(num);
            }
        }
    }

    public int pop() {
        if (!stack.isEmpty()) {
            if (stack.peek() >=  && !maxStack.isEmpty()) {
                int result = maxStack.pop();
                maxStack.push(result - stack.pop());
                return result;
            } else if (stack.peek() <  && !maxStack.isEmpty()) {
                return (maxStack.peek() + stack.pop());
            } else {
                return -;
            }
        } else {
            return -;
        }
    }

    public int max() {
        if (stack.isEmpty()) {
            return ;
        }
        if (maxStack.isEmpty())
            return -;
        return maxStack.peek();
    }

    public static void main(String[] args) {
        int arr[] = { , , , , , , , , , ,  };
        SpecialStack specialStack = new SpecialStack();
        for (int i = ; i < arr.length; i++) {
            specialStack.push(arr[i]);
            System.out.print("入棧:" + arr[i]);
            System.out.println("最大值:" + specialStack.max());
        }
        for (int i = ; i < arr.length; i++) {
            System.out.print("出棧:" + specialStack.pop());
            System.out.println("最大值:" + specialStack.max());
        }
    }

}
           

9.非遞歸實作二叉樹的周遊

前序周遊:

對于樹中的任意一個節點cur:

(1)通路cur,并将節點入棧;

(2)判斷節點cur的左孩子是否為空。若不為空,則将cur的左孩子cur.left置為目前的結點cur;

(3)若為空,則取棧頂節點并進行出棧操作(根據出棧節點去找該節點的右孩子),并将棧頂結點的右孩子cur.right置為目前的結點cur,循環至1);

中序周遊:

對于樹中的任意節點cur:

(1)若cur的左孩子不為空,将p壓棧,并将cur的左子樹置為目前節點cur,然後對目前節點重複操作。

(2)若cur的左孩子為空,将棧頂元素出棧并進行通路,把目前節點置為cur的右孩子。

(3)直到棧為空且cur為空

後序周遊:

對于樹中的任意節點cur

(1) 如果該節點沒有左孩子和右孩子可以直接通路該節點;

如果其左孩子和右孩子被通路過了,可以直接通路該節點;

(2)如果不是情況(1),那麼就先将右孩子壓棧,再将左孩子壓棧,這樣出棧順序就是先出左孩子再出右孩子。

import java.util.Stack;

/**
 * 非遞歸的樹的周遊 Title: Description: Company:
 * 
 * @author 鄭偉
 * @date 2018年4月13日下午3:37:12
 */
public class Print_Tree {

    // 先序周遊非遞歸
    // 如果發現右兒子沒有了,那麼出棧,指向cur,如果cur右兒子有那麼久列印右兒子,把右兒子入棧,如果沒有右兒子,那麼久繼續出棧,出棧的節點設為cur
    public static void preOrder(TreeNode pNode) {
        Stack<TreeNode> stack = new Stack<>();
        while (pNode != null || !stack.isEmpty()) {
            while (pNode != null) {
                // 先列印目前節點
                System.out.print(pNode.val+" ");// 若節點不為空先通路再壓棧
                stack.push(pNode);// 目前節點入棧
                pNode = pNode.left;// 将目前節點置為p的左孩子,若不為空繼續通路并壓棧
            }
            // 當p為空時,說明根節點和左孩子列印周遊完畢了,接下來出棧周遊右孩子
            if (!stack.isEmpty()) {// 左子樹不存在,那麼就是講棧頂彈出,作為目前 節點
                pNode = stack.pop();
                // 講目前節點設定為右邊的節點
                pNode = pNode.right;
            }
        }
    }

    // 中序周遊非遞歸
    // 就隻如果節點有左子樹就不停的入棧,直到左邊沒有左子樹,然後出棧,列印目前值,然後cur指向右節點。
    public static void InOrder(TreeNode pNode) {
        Stack<TreeNode> stack = new Stack<TreeNode>();
        while (pNode != null || !stack.isEmpty()) {
            // 不停的把左子樹入棧
            while (pNode != null) {
                stack.push(pNode);
                pNode = pNode.left;
            }
            // 當左子樹沒有的時候,也就是如到底部了
            if (stack != null) {
                pNode = stack.pop();// 彈出一個節點
                System.out.print(pNode.val+" ");
                pNode = pNode.right;// 開始答應右邊的節點
            }
        }
    }

    // 後續周遊
    // 先右子樹壓棧,再左子樹壓棧
    public static void PostOrder(TreeNode pNode) {
        if (pNode == null)
            return;
        Stack<TreeNode> stack = new Stack<TreeNode>();
        TreeNode preNode = null;
        TreeNode curNode;
        stack.push(pNode);// 根節點先入棧
        while (!stack.isEmpty()) {
            curNode = stack.peek();
            // 如果目前節點的左右子節點都為null,那麼就直接答應目前節點;
            // 目前一個節點不為空并且是目前節點的左孩子或者右孩子,當是左孩子時說明目前節點右孩子為空,
            // 當是右孩子時,說明左右孩子都通路過了,且都不為空
            if (curNode.left == null
                    && curNode.right == null
                    || (preNode != null && (preNode == curNode.left || preNode == curNode.right))) {
                System.out.print(curNode.val+" ");// 通路目前節點
                preNode = curNode;
                // curNode指向棧頂,由于列印過了,就直接出棧
                stack.pop();
            } else {
                // 目前節點為棧頂元素 如果目前節點不是葉子節點,在目前節點之前通路的那個節點不是目前節點的孩子,則進行壓棧
                // 先壓棧右節點再壓棧左節點 這樣出棧時是先左後右
                if (curNode.right != null)
                    stack.push(curNode.right);
                if (curNode.left != null)
                    stack.push(curNode.left);
            }
        }
    }

    public TreeNode buildTree(int[] nums, int i) {
        if (i >= nums.length)
            return null;
        TreeNode root = new TreeNode(nums[i]);
        root.left = buildTree(nums, i *  + );
        root.right = buildTree(nums, i *  + );
        return root;
    }

    public static void main(String[] args) {
        Print_Tree pTree = new Print_Tree();
        int[] nums = {,,,,,};
        TreeNode buildTree = pTree.buildTree(nums, );
        System.out.println("前序周遊");
        Print_Tree.preOrder(buildTree);
        System.out.println();
        System.out.println("中周遊");
        Print_Tree.InOrder(buildTree);
        System.out.println();
        System.out.println("後序周遊");
        Print_Tree.PostOrder(buildTree);
    }

}

/**
*前序周遊
*1 2 4 5 3 6 
*中周遊
*4 2 5 1 6 3 
*後序周遊
*4 5 2 6 3 1 
*/
           

繼續閱讀