二〇一九劍指BAT,Java後端研發面試題解析,連解題思路都告訴你

BAT做爲中國三大最大互聯網公司,是每一個程序員求之不得的工做環境,比如高中生高考的目的是清華北大復旦同樣,因此每一個年輕程序員都應該嘗試進入BAT工做。node

第一題:搜索二位矩陣

編寫一個高效的算法來搜索 m x n 矩陣 matrix 中的一個目標值 target。該矩陣具備如下特性程序員

  • 每行的元素從左到右升序排列。
  • 每列的元素從上到下升序排列。

示例面試

現有矩陣 matrix 以下:算法

[
  [1,   4,  7, 11, 15],
  [2,   5,  8, 12, 19],
  [3,   6,  9, 16, 22],
  [10, 13, 14, 17, 24],
  [18, 21, 23, 26, 30]
]
  • 給定 target = 5,返回 true。
  • 給定 target = 20,返回 false。

解題思路

二維數組是有規律的:右上角的數字是一列中最小的、一行中最大的,經過這個數字和 target 進行對比,能夠將一行或者一列做爲候選區域排出,那麼 target 可能存在的範圍縮小,最終得出結果。數組

public Boolean searchMatrix(int[][] matrix, int target) {
    if (matrix.length == 0) {
        return false;
    }
    for (int i = 0, j = matrix[0].length - 1; i < matrix.length && j >= 0; ) {
        if (matrix[i][j] > target) {
            j--;
        } else if (matrix[i][j] < target) {
            i++;
        } else {
            return true;
        }
    }
    return false;
}

第二題:替換空格

請實現一個函數,將一個字符串中的每一個空格替換成「%20」。例如,當字符串爲We Are Happy.則通過替換以後的字符串爲 We%20Are%20Happyapp

解題思路

  1. 經過字符串中空格的個數,計算新字符串長度
  2. 兩個指針進行字符串拷貝,當遇到‘ ’時替換爲 %20
public String replaceSpace(StringBuffer str) {
    char[] chars = str.toString().toCharArray();
    StringBuilder res = new StringBuilder();
    for (char c : chars) {
        if (c == ' ') res.append("%20"); else res.append(c);
    }
    return res.toString();
}

第三題:從頭至尾打印鏈表

輸入一個鏈表,按鏈表值從尾到頭的順序返回一個ArrayList函數

解題思路

public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
    LinkedList<Integer> stack = new LinkedList<>();
    while (listNode != null) {
        stack.addLast(listNode.val);
        listNode = listNode.next;
    }
    ArrayList<Integer> res = new ArrayList<>();
    while (!stack.isEmpty()) {
        res.add(stack.pollLast());
    }
    return res;
}
  1. 遞歸:當鏈表過長時,會致使棧溢出
public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
    ArrayList<Integer> res = new ArrayList<>();
    print(res,listNode);
    return res;
}
private void print(ArrayList<Integer> res, ListNode listNode) {
    if (listNode == null) return;
    print(res, listNode.next);
    res.add(listNode.val);
}

第四題:重建二叉樹

輸入某二叉樹的前序遍歷和中序遍歷的結果,請重建出該二叉樹。假設輸入的前序遍歷和中序遍歷的結果中都不含重複的數字。例如輸入前序遍歷序列{1,2,4,7,3,5,6,8}和中序遍歷序列{4,7,2,1,5,3,8,6},則重建二叉樹並返回ui

解題思路

  1. 經過前序遍歷找到 root 節點
  2. 那麼在 中序遍歷中 root 節點的左側則是左子樹,右側是右子樹
  3. 依次類推,遞歸生成節點的左子樹和右子樹
  4. 構建過程由下往上
public TreeNode reConstructBinaryTree(int[] pre, int[] in) {
    Map<Integer, Integer> preIndex = new HashMap<>();
    for (int i = 0; i < pre.length; i++) {
        preIndex.put(pre[i], i);
    }
    return buildTree(preIndex, in, 0, in.length - 1);
}
private TreeNode buildTree(Map<Integer, Integer> preIndex, int[] in, int start, int end) {
    if (start == end) {
        return new TreeNode(in[start]);
    }
    int indexOfRoot = start;
    for (int i = start; i <= end; i++) {
        if (preIndex.get(in[i]) < preIndex.get(in[indexOfRoot])) {
            indexOfRoot = i;
        }
    }
    TreeNode root = new TreeNode(in[indexOfRoot]);
    if (start <= indexOfRoot - 1) root.left = buildTree(preIndex, in, start, indexOfRoot - 1);
    if (indexOfRoot + 1 <= end) root.right = buildTree(preIndex, in, indexOfRoot + 1, end);
    return root;
}

第五題:用兩個棧實現一個隊列

用兩個棧來實現一個隊列,完成隊列的 Push 和 Pop 操做。 隊列中的元素爲int類型spa

解題思路

  1. 用 stack1 做爲 push 隊列,將元素 push 到 stack1
  2. 用 stack2 做爲 pop 隊列,當 stack2 爲空時則將 stack1 的數據 push 到 stack2,不然直接 pop stack2

至關於將兩個 stack 拼接:-> stack1 <::> stack2 ->設計

Stack<Integer> pushStack = new Stack<>();
Stack<Integer> popStack = new Stack<>();
public void push(int node) {
    pushStack.push(node);
}
public int pop() {
    if (popStack.isEmpty()) {
        while (!pushStack.isEmpty()) {
            popStack.push(pushStack.pop());
        }
    }
    if (popStack.isEmpty()) return -1; else return popStack.pop();
}

第六題:旋轉數組的最小數字

把一個數組最開始的若干個元素搬到數組的末尾,咱們稱之爲數組的旋轉。 輸入一個非減排序的數組的一個旋轉,輸出旋轉數組的最小元素。 例如數組{3,4,5,1,2}爲{1,2,3,4,5}的一個旋轉,該數組的最小值爲1。 NOTE:給出的全部元素都大於0,若數組大小爲0,請返回0

解題思路

  1. 旋轉以後的數組存在兩個上升序列,最小元素在兩個上升序列的中間
  2. 用兩個指針在兩個序列中找到最大和最小的值,這樣 end 指向的數則爲最小
public int minNumberInRotateArray(int[] array) {
    if (array.length == 0) {
        return 0;
    }
    int start = 0, end = array.length - 1;
    while (end - start != 1) {
        int mid = (start + end) / 2;
        if (array[mid] >= array[start]) {
            start = mid;
        }
        if (array[mid] <= array[end]) {
            end = mid;
        }
    }
    return array[end];
}

第七題:斐波納切數列

你們都知道斐波那契數列,如今要求輸入一個整數n,請你輸出斐波那契數列的第n項(從0開始,第0項爲0)。n<=39

解題思路

  1. 遞歸計算很慢,是最簡單的算法
public int Fibonacci(int n) {
    if (n == 0) {
        return 0;
    }
    if (n == 1) {
        return 1;
    }
    int l = 1, ll = 0;
    for (int i = 2; i <= n; i++) {
        int t = ll + l;
        ll = l;
        l = t;
    }
    return l;
}

第八題:二進制中1的個數

輸入一個整數,輸出該數二進制表示中1的個數。其中負數用補碼錶示

解題思路

  1. 負數是補碼錶示
  2. >>> 爲無符號右移,>>爲有符號右移,當 n 爲負數是會增長多餘的1
public int NumberOf1(int n) {
    int mask = 0x01;
    int res = 0;
    int t = n;
    while (t != 0) {
        if ((t & mask) == 1) {
            res++;
        }
        t = t >>> 1;
    }
    return res;
}

第九題:數值的整數次方

給定一個 double 類型的浮點數 base 和 int 類型的整數 exponent 。求 base 的 exponent 次方

解題思路

  1. 當 n 爲偶數時,$$a^n = a^{n/2} * a^{n/2}$$
  2. 當 n 爲奇數時,$$a^n = a^{n/2} * a^{n/2} * a$$
  3. 能夠利用相似斐波納切的方式,利用遞歸來進行求解
public double Power(double base, int exponent) {
    if (base == 0) {
        return 0;
    }
    if (base == 1) {
        return 1;
    }
    int t_exponent = Math.abs(exponent);
    double t = PositivePower(base, t_exponent);
    return exponent > 0 ? t : 1 / t;
}
private double PositivePower(double base, int exponent) {
    if (exponent == 0) {
        return 1;
    }
    if (exponent == 1) {
        return base;
    }
    double t = PositivePower(base, exponent >> 1);
    t *= t;
    if ((exponent & 0x01) == 1) {
        t *= base;
    }
    return t;
}

第十題:打印最大的n位數

輸入n,打印出 1 到最大的 n 位十進制數。好比輸入3,則打印出一、二、3 直到最大的 3 位數 999

解題思路

  1. n 可能很大,致使輸出的數字超過 int 或者 long
public void PrintN(int n) {
    if (n <= 0) {
        return;
    }
    String res = "0";
    while (true) {
        Boolean all9 = true;
        res = Plus(res, 1);
        System.out.println(res);
        for (int i = 0; i < res.length(); i++) {
            if (res.charAt(i) != '9') {
                all9 = false;
                break;
            }
        }
        if (all9 && res.length() == n) {
            break;
        }
    }
}
private String Plus(String t, int i) {
    char[] chars = t.toCharArray();
    StringBuilder res = new StringBuilder();
    chars[chars.length - 1] += i;
    Boolean flag = false;
    for (int j = chars.length - 1; j >= 0; j--) {
        int a = chars[j];
        if (flag) {
            a++;
            flag = false;
        }
        if (a > '9') {
            flag = true;
            a = a - '9' + '0' - 1;
        }
        res.append((char) a);
    }
    if (flag) {
        res.append('1');
    }
    return res.reverse().toString();
}

第十一題:在O(1)的時間複雜度下刪除節點

給定單向鏈表的頭指針以及待刪除的指針,定義一個函數在 O(1) 的時間複雜度下刪除

解題思路

  1. 待刪除節點非尾節點,將後一個節點的值複製到當前節點,而後刪除後一個節點
  2. 待刪除節點爲尾節點,從頭節點開始,找到待刪除節點的前一個節點進行刪除
public void O1DeleteNode(ListNode head, ListNode needDelete) {
    if (needDelete.next != null) {
        ListNode next = needDelete.next.next;
        needDelete.val = needDelete.next.val;
        needDelete.next = next;
    } else {
        ListNode cursor = head;
        while (cursor != null) {
            if (cursor.next == needDelete) break;
            cursor = cursor.next;
        }
        if (cursor == null) return;
        cursor.next = needDelete.next;
    }
}

第十二題:調整數組順序使奇數位於偶數前面

輸入一個整數數組,實現一個函數來調整該數組中數字的順序,使得全部的奇數位於數組的前半部分,全部的偶數位於數組的後半部分,並保證奇數和奇數,偶數和偶數之間的相對位置不變

解題思路

  1. 須要保證排序的穩定性
  2. 採用冒泡算法進行排序
public void reOrderArray(int[] array) {
    if (array.length <= 1) {
        return;
    }
    for (int i = array.length - 1; i >= 0; i--) {
        for (int j = i; j < array.length - 1; j++) {
            if (array[j] % 2 == 0 && array[j + 1] % 2 == 1) swap(array, j, j + 1);
        }
    }
}
private void swap(int[] array, int a, int b) {
    int t = array[a];
    array[a] = array[b];
    array[b] = t;
}

第十三題:鏈表中倒數第k個結點

輸入一個鏈表,輸出該鏈表中倒數第k個結點

解題思路

  1. 兩個指針,快指針先走 k 步,而後慢指針在向前移動,當快指針遍歷結束,慢指針指向倒數第 k 個節點
  2. 須要考慮倒數 k 個節點不存在的狀況
public ListNode FindKthToTail(ListNode head, int k) {
    if (head == null) {
        return null;
    }
    ListNode cursor = head;
    ListNode cursorK = head;
    int i = 0;
    while (cursorK != null) {
        cursorK = cursorK.next;
        if (i >= k) {
            cursor = cursor.next;
        }
        i++;
    }
    if (i < k) {
        return null;
    }
    return cursor;
}

第十五題:反轉鏈表

輸入一個鏈表,反轉鏈表後,輸出新鏈表的表頭

解題思路

  1. 三個指針
public ListNode ReverseList(ListNode head) {
    if (head == null || head.next == null) {
        return head;
    }
    ListNode pre = head, cur = head.next, next;
    pre.next = null;
    while (cur != null) {
        next = cur.next;
        cur.next = pre;
        pre = cur;
        cur = next;
    }
    return pre;
}

寫在最後

面試題還有不少,可是本文章限於篇幅,只分享15到一線互聯網面試真題;關於本人所設計到的面試題,本人都已經整理成一份完整的PDF面試題集合,現免費分享給各位有須要的Java工程師朋友,點擊下方傳送門便可免費領取喲

傳送門

  • 雄心是成功路上的指南,
  • 信心是永不放棄的呼喚,
  • 熱心是成功者的胸懷,
  • 耐心是驅趕困難的利劍,
  • 責任心是邁向成功的必然。
  • 願五心伴您度過每一天!
  • 衷心祝你們面試一路順風,馬到成功!
相關文章
相關標籤/搜索