二〇一九剑指BAT,Java后端研发面试题解析,连解题思路都告诉你

BAT作为中国三大最大互联网公司,是每个程序员梦寐以求的工作环境,好比高中生高考的目的是清华北大复旦一样,所以每个年轻程序员都应该尝试进入BAT工作。

第一题:搜索二位矩阵

编写一个高效的算法来搜索 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%20Happy

解题思路

  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},则重建二叉树并返回

解题思路

  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类型

解题思路

  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,则打印出1、2、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工程师朋友,点击下方传送门即可免费领取哟

传送门

  • 雄心是成功路上的指南
  • 信心是永不放弃的呼唤
  • 热心是成功者的胸怀
  • 耐心是驱赶困难的利剑
  • 责任心是迈向成功的必然
  • 愿五心伴您度过每一天!
  • 衷心祝大家面试一帆风顺,马到成功
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容