1. 程式人生 > >劍指offer面試題-Java版-持續更新

劍指offer面試題-Java版-持續更新

紅色 縮小 進行 n-1 bubuko 變量 argument style 對象

最近在用Java刷劍指offer(第二版)的面試題。書中原題的代碼采用C++編寫,有些題的初衷是為了考察C++的指針、模板等特性,這些題使用Java編寫有些不合適。但多數題還是考察通用的算法、數據結構以及編程思想等,與語言本身無太大關系。因此在選擇編程語言時,我還是選擇了Java。好吧,主要是我C++不怎麽會,僅僅是曾經學過倆月,使用Java順手一些。後續可能再用Python刷一遍。

面試題3 數組中重復的數字

  題目一:找出數組中重復的數字

  • 描述:在長度為n的數組裏所有數字都在0~n-1範圍內。數組中某些數字是重復的,請找出任意一個重復的數字。如數組{2, 3, 1, 0, 2, 5, 3},輸出的重復的數字為2或3。
  • 思路:利用數組的索引在0~n-1這個範圍的性質,將數字i移至索引i的位置。
  • 考點:對數組的理解以及對問題的分析能力。

  題目二:不修改數組找出重復的數字

  • 描述:在長度為n+1的數組裏所有數字都在1~n範圍內。找出重復的數字,但不修改數組。
  • 思路:當然可完全利用題目一的方法,只是需要輔助空間。不需要輔助空間是最好的了。這裏使用二分法,對數組進行計數,逐漸縮小重復的數字所處的範圍。
  • 考點:對二分查找的靈活應用,畢竟寫出正確無誤的二分法時有些難度的。同時要重視與面試官的溝通,明確需求,如是否能更改數組,是否可以使用輔助空間等。
package sword_offer;
//page 39 數組中重復的數字
public class Solution3 { //題目1 //輸出數組中重復的數字,空間復雜度O(1),時間復雜度O(n) //數組長度為n,數字在0~n-1範圍內 public static int duplicate(int[] arr) { for (int i = 0; i < arr.length; i++) { //System.out.println(i); while (arr[i] != i) { if (arr[i] == arr[arr[i]])
return arr[i]; else { int temp = arr[i]; arr[i] = arr[temp]; arr[temp] = temp; //System.out.println(Arrays.toString(arr)); } } } return -1; } //題目2 //輸出數組中重復的數字,空間復雜度O(1),時間復雜度O(nlog(n)) //數組長度為n+1,數字在1~n範圍內,要求不修改數組,並不使用輔助空間 public static int myGetDuplication(int[] arr) { int start = 1; int middle = arr.length / 2; int end = middle; while(end >= start) { //System.out.println("" + start + end); int count = 0; for (int i = 0; i < arr.length; i++) { if (arr[i] >= start && arr[i] <= end) count++; } if (end == start) { if (count > 1) return start; else break; } if (count > end - start + 1) { middle = (start + end) / 2; end = middle; } else { start = middle + 1; end = arr.length - 1; } } return -1; } //輸出數組中重復的數字,空間復雜度O(1),時間復雜度O(nlog(n)) //數組長度為n+1,數字在1~n範圍內,要求不修改數組,並不使用輔助空間 //比上一個函數邏輯清晰一點 public static int getDuplication(int[] arr) { int start = 1; int end = arr.length - 1; while(end >= start) { int middle = (end - start) / 2 + start; int count = getCount(arr, start, middle); if (end == start) { if (count > 1) return start; else break; } if (count > middle - start + 1) { end = middle; } else start = middle + 1; } return -1; } //計算數組arr元素處在[start, end]範圍內元素個數 private static int getCount(int[] arr, int start, int end) { int count = 0; for (int i = 0; i < arr.length; i++) { if (arr[i] >= start && arr[i] <= end) count++; } return count; } //測試 public static void main(String[] args) { int[] arr = {1, 2, 3, 1}; System.out.println(duplicate(arr)); int[] arr2 = {2, 3, 5, 4, 3, 2, 6, 7}; System.out.println(myGetDuplication(arr2)); int[] arr3 = {2, 3, 5, 4, 3, 2, 6, 7}; System.out.println(getDuplication(arr3)); } }

面試題4 二維數組中的查找

  • 描述:二維數組中,數字按從左到右、從上到下的順序遞增。給定一個整數,判斷該數組中是否含有該整數。
  • 思路:從數組的右上角或左下角開始進行查找數據,縮小可能包含該數的範圍。
  • 考點:畫圖分析問題,尋求問題的突破口。並能正確編寫程序,避免死循環等問題。

例如,從二維數組$\left[ {\begin{array}{*{20}{c}}
{1}&{2}&{8}&9 \\
{1}&{4}&{9}&{12} \\
{4}&{7}&{10}&{13} \\
{6}&{8}&{11}&{15}
\end{array}} \right]$中尋找是否包含數字7。

從右上角查找時,逐漸向左下方縮小範圍。紅色的代表包含目標值7的區域,過程如下:

$$\left[ {\begin{array}{*{20}{c}}
{\color{red}1}&{\color{red}2}&{\color{red}8}&9 \\
{\color{red}1}&{\color{red}4}&{\color{red}9}&{12} \\
{\color{red}4}&{\color{red}7}&{\color{red}{10}}&{13} \\
{\color{red}6}&{\color{red}8}&{\color{red}{11}}&{15}
\end{array}} \right]\to\left[ {\begin{array}{*{20}{c}}
{\color{red}1}&{\color{red}2}&{8}&9 \\
{\color{red}1}&{\color{red}4}&{9}&{12} \\
{\color{red}4}&{\color{red}7}&{10}&{13} \\
{\color{red}6}&{\color{red}8}&{11}&{15}
\end{array}} \right]\to\left[ {\begin{array}{*{20}{c}}
{1}&{2}&{8}&9 \\
{\color{red}1}&{\color{red}4}&{9}&{12} \\
{\color{red}4}&{\color{red}7}&{10}&{13} \\
{\color{red}6}&{\color{red}8}&{11}&{15}
\end{array}} \right]\to\left[ {\begin{array}{*{20}{c}}
{1}&{2}&{8}&9 \\
{1}&{4}&{9}&{12} \\
{\color{red}4}&{\color{red}7}&{10}&{13} \\
{\color{red}6}&{\color{red}8}&{11}&{15}
\end{array}} \right]$$

從左下角查找時,逐漸向右上方縮小範圍。過程如下:

$$\left[ {\begin{array}{*{20}{c}}
{1}&{\color{red}2}&{\color{red}8}&{\color{red}9} \\
{1}&{\color{red}4}&{\color{red}9}&{\color{red}{12}} \\
{4}&{\color{red}7}&{\color{red}{10}}&{\color{red}{13}} \\
{6}&{\color{red}8}&{\color{red}{11}}&{\color{red}{15}}
\end{array}} \right]\to\left[ {\begin{array}{*{20}{c}}
{1}&{\color{red}2}&{\color{red}8}&{\color{red}9} \\
{1}&{\color{red}4}&{\color{red}9}&{\color{red}{12}} \\
{4}&{\color{red}7}&{\color{red}{10}}&{\color{red}{13}} \\
{6}&{8}&{11}&{15}
\end{array}} \right]$$

package sword_offer;
//page 44 二維數組中的查找

public class Solution4 {
    //從右上角的元素開始查找,逐漸縮小範圍
    public static boolean findNum(int[][] arr, int target) {
        boolean found = false;
        int row = 0;
        int col = arr[0].length - 1;
        while (col > 0 && row <= arr.length) {
            int diff = arr[row][col] - target;
            if (diff == 0) {
                found = true;
                break;
            }
            else if (diff > 0) 
                col--;
            else 
                row++;
        }
        return found;
    }
    
    //測試
    public static void main(String[] args) {
        int[][] arr = {{1,2,8,9},{2,4,9,12},{4,7,10,13},{6,8,11,15}};
        System.out.println(findNum(arr, 9));
    }
}

面試題5 替換空格

  • 描述:將字符串中的每個空格替換成%20。如輸入"we are fine",輸出"we%20are%20fine"。
  • 思路:原題考察了C++中指針的操作。Java裏數組不可變,因此本題變得沒有難度了。利用String對象的.charAt函數遍歷每個字符,並使用StringBuilder構建新的字符串。
  • 考點:對字符串的處理。
package sword_offer;
//page 51 替換空格

public class Solution5 {
    //在Java中字符串時不可變的,因而只能構造一個新的字符串。原文中該題的難點也無法體現出來了。
    public static String replaceBlank(String str) {
        StringBuilder strb = new StringBuilder();
        for (int i = 0; i < str.length(); i++) {
            if (str.charAt(i) == ‘ ‘) {
                strb.append("%20");
            }
            else 
                strb.append(str.charAt(i));
        }
        return strb.toString();
    }
    
    //測試
    public static void main(String[] args) {
        String str = "We are happr.";
        System.out.println(replaceBlank(str));
    }
}

面試題6 從尾到頭打印鏈表

  • 描述:輸入一個鏈表的頭節點,從尾到頭打印每個節點的值。
  • 思路:從尾到頭打印,即為“先進後出”,則可以使用棧來處理;考慮遞歸的本質也是一個棧結構,可遞歸輸出。
  • 考點:對鏈表、棧、遞歸的理解。
package sword_offer;
//page 58 從尾到頭打印鏈表
import java.util.Stack;

//鏈表類
class ListNode{
    ListNode next = null;
    int value;
}

public class Solution6 {
        
    //方法1:使用Stack棧的先push後pop
    public static void printListReverse(ListNode listNode) {
        Stack<ListNode> stack = new Stack<ListNode>();
        while(listNode != null) {
            stack.push(listNode);
            listNode = listNode.next;
        }
        while(!stack.isEmpty()) {
            System.out.println(stack.pop().value);
        }
    }
    
    //方法2:使用遞歸的方式,相當於從內部往外部推
    public static void printListReverse_rec(ListNode listNode) {
        if(listNode != null) {
            if (listNode.next != null)
                printListReverse_rec(listNode.next);
            System.out.println(listNode.value);
        }
    }
    
    //測試
    public static void main(String[] args) {
        ListNode ln1 = new ListNode();
        ListNode ln2 = new ListNode();
        ListNode ln3 = new ListNode();
        ln1.next = ln2;
        ln2.next = ln3;
        ln1.value = 1;
        ln2.value = 2;
        ln3.value = 3;
        printListReverse_rec(ln1);
        printListReverse(ln1);        
    }
}

面試題7 重建二叉樹

  • 描述:輸入某二叉樹的前序遍歷和中序遍歷結果,重建該二叉樹。假設前序遍歷或中序遍歷的結果中無重復的數字。
  • 思路:前序遍歷的第一個元素為根節點的值,據此將中序遍歷數組拆分為左子樹+root+右子樹,前序遍歷數組拆分為root+左子樹+右子樹。再對左右子樹進行同樣的操作。
  • 考點:對二叉樹不同遍歷方法的掌握。

技術分享圖片

package sword_offer;
//page 62 重建二叉樹

//二叉樹類,包含左右子樹,以及用於查看的方法
class BinaryTreeNode {
    int value;
    BinaryTreeNode leftNode;
    BinaryTreeNode rightNode;
    //中序遍歷輸出查看
    public void printList() {
        if (leftNode != null)
            leftNode.printList();
        System.out.println(value);
        if (rightNode != null)
            rightNode.printList();
    }
}

public class Solution7 {
    //重建二叉樹函數
    public static BinaryTreeNode rebultTree(int[] preorder, int[] inorder) {
        BinaryTreeNode root = rebultTree(preorder, 0, preorder.length - 1, inorder, 0, inorder.length - 1);
        return root;
    }
    //重寫函數
    private static BinaryTreeNode rebultTree(int[] preorder, int startPre, int endPre, int[] inorder, int startIn,
            int endIn) {
        if (startPre > endPre || startIn > endIn)
            return null;
        BinaryTreeNode root = new BinaryTreeNode();
        root.value = preorder[startPre];
        for (int i = startIn; i <= endIn; i++) {
            if (inorder[i] == preorder[startPre]) {
                root.leftNode = rebultTree(preorder, startPre + 1, startPre + i - startIn, inorder, startIn, i - 1);
                root.rightNode = rebultTree(preorder, startPre + i - startIn + 1, endPre, inorder, i + 1, endIn);
            }
        }
        return root;
    }
    //測試
    public static void main(String[] args) {
        int[] preorder = { 1, 2, 4, 7, 3, 5, 6, 8 };
        int[] inorder = { 4, 7, 2, 1, 5, 3, 8, 6 };
        BinaryTreeNode root = rebultTree(preorder, inorder);
        //System.out.println(root.leftNode.rightNode.value);
        root.printList();
    }
}

面試題8 二叉樹的下一個節點

  • 描述:給定一棵二叉樹和其中的一個節點,找出中序遍歷序列的下一個節點。樹中應定義指向左節點、右節點、父節點的三個變量。
  • 思路:該節點若存在右節點,右子樹的最左節點則為下一節點;若不存在右節點,則向上遍歷,直至找到是父節點的左節點的那個節點,該節點的父節點則為下一節點。
  • 考點:對中序遍歷的理解。
package sword_offer;
//page 65 二叉樹的下一個節點

//定義二叉樹類,包含左右子樹、父節點,以及用於查看的函數
class TreeNode {
    char value;
    TreeNode left;
    TreeNode right;
    TreeNode father;
    
    //中序遍歷輸出查看
    public void printList() {
        if (left != null)
            left.printList();
        System.out.println(value);
        if (right != null)
            right.printList();
    }
}

public class Solution8 {
    public static TreeNode findNext(TreeNode node) {
        //有右節點,找到右子樹的最左節點
        if (node.right!= null) {
            node = node.right;
            while(node.left != null) node = node.left;
            return node;
        }
        
        //無右節點,則向上遍歷,直至找到節點為父節點的左子節點
        while(node.father != null) {
            if (node.father.left == node) return node.father;
            node = node.father;
        }
        return null;
    }
    public static void main(String[] args) {
        //建立一個二叉樹節點的數組,並tree[i].value賦值
        TreeNode[] tree = new TreeNode[9];
        char[] chars = {‘a‘, ‘b‘, ‘c‘, ‘d‘, ‘e‘, ‘f‘, ‘g‘, ‘h‘, ‘i‘};
        for (int i = 0; i < tree.length; i++) {
            tree[i] = new TreeNode();
            tree[i].value = chars[i];
        }
        /*
         *        a
         *      /            *     b     c
         *    / \   /          *   d   e  f  g
         *      /          *     h   i
         */
        //左右節點關系
        tree[0].left = tree[1];
        tree[0].right = tree[2];
        tree[1].left = tree[3];
        tree[1].right = tree[4];
        tree[2].left = tree[5];
        tree[2].right = tree[6];
        tree[4].left = tree[7];
        tree[4].right = tree[8];
        //父節點關系
        tree[1].father = tree[0];
        tree[2].father = tree[0];
        tree[3].father = tree[1];
        tree[4].father = tree[1];
        tree[5].father = tree[2];
        tree[6].father = tree[2];
        tree[7].father = tree[4];
        tree[8].father = tree[4];
        
        tree[0].printList();
    }
}

面試題9 兩個棧實現隊列

  • 描述:使用兩個棧實現一個隊列。隊列中實現尾部插入和頭部刪除函數。
  • 思路:棧結構“先進後出”,插入數據時進入第一個棧;刪除數據時,將第一個棧的所有數據都彈出到第二個棧,這時原先先插入的數據位於棧的頂端。即可滿足隊列的“先進先出”。
  • 考點:對棧和隊列的理解;對泛型的使用等。

技術分享圖片

package sword_offer;
//page 68 兩個棧實現隊列
import java.util.Stack;

//隊列類,包含兩個棧、兩個操作隊列的方法
class Queue <T>{
    Stack<T> stack1 = new Stack<>();
    Stack<T> stack2 = new Stack<>();
    //插入節點
    public void appendTail(T element) {
        stack1.push(element);
    }
    //刪除節點
    public T deleteHead(){
        if (stack2.isEmpty()) {
            while(!stack1.isEmpty()) {
                T data = stack1.pop();
                stack2.push(data);
            }
        }
        //為空時,輸出異常
        if (stack2.isEmpty())
            throw new IllegalArgumentException("queue is empty");
        return stack2.pop();
        
    }
}
public class Solution9 {
    //測試
    public static void main(String[] args) {
        Queue<Integer> queue = new Queue<>();
        queue.appendTail(1);
        queue.appendTail(2);
        queue.appendTail(3);
        System.out.println(queue.deleteHead());
        System.out.println(queue.deleteHead());
        queue.appendTail(4);
        System.out.println(queue.deleteHead());
        System.out.println(queue.deleteHead());
        System.out.println(queue.deleteHead());
    }
}

劍指offer面試題-Java版-持續更新