1. 程式人生 > >面試中遇到的手撕程式碼(二)

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

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 
*/