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

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() >= 0 && !maxStack.isEmpty()) {
                int result = maxStack.pop();
                maxStack.push(result - stack.pop());
                return result;
            } else if (stack.peek() < 0 && !maxStack.isEmpty()) {
                return (maxStack.peek() + stack.pop());
            } else {
                return -1;
            }
        } else {
            return -1;
        }
    }

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

    public static void main(String[] args) {
        int arr[] = { 5, 4, 1, 2, 3, 10, 9, 8, 6, 7, 15 };
        SpecialStack specialStack = new SpecialStack();
        for (int i = 0; i < arr.length; i++) {
            specialStack.push(arr[i]);
            System.out.print("入棧:" + arr[i]);
            System.out.println("最大值:" + specialStack.max());
        }
        for (int i = 0; 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 * 2 + 1);
        root.right = buildTree(nums, i * 2 + 2);
        return root;
    }

    public static void main(String[] args) {
        Print_Tree pTree = new Print_Tree();
        int[] nums = {1,2,3,4,5,6};
        TreeNode buildTree = pTree.buildTree(nums, 0);
        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 
*/
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章