Android面試常見算法題解「排序、鍊表、二叉樹」

android禿老師 發佈 2022-10-27T13:13:44.896208+00:00

我們在面試Android開發崗位時;不僅會面試許多技術問題,還常會問到一些算法題目,那麼這期就來解答這幾種常見算法題。Android常見算法題1、排序算法快排從平均時間來看,快速排序是效率最高的,但快速排序在最壞情況下的時間性能不如堆排序和歸併排序。

我們在面試Android開發崗位時;不僅會面試許多技術問題,還常會問到一些算法題目,那麼這期就來解答這幾種常見算法題。

Android常見算法題

1、排序算法

快排

從平均時間來看,快速排序是效率最高的,但快速排序在最壞情況下的時間性能不如堆排序和歸併排序。

每次找個基數,然後對比基數,l 和 h 指針, 從兩頭開始找比自己小向右移,比自己大的向左移。

  public static int[] quickSort(int[] data) {
        return quickSort(data, 0, data.length - 1);
    }

    public static int[] quickSort(int[] data, int low, int height) {
        int l = low;
        int h = height;
        int povit = data[l];
        while (l < h) {
            while (l < h && data[h] >= povit) h--;
    
            if (l < h) {
                data[l] = data[h];
                l++;
            }


            while (l < h && data[l] <= povit) l++;
    
            if (l < h) {
                data[h] = data[l];
                h--;
            }
    
            data[l] = povit;
        }
        if (l - 1 > low) quickSort(data, low, l - 1);
        if (h + 1 < height) quickSort(data, h + 1, height);
        return data;
    }

冒泡排序

內存優先

  public static int[] bubbleSort(int[] array) {
        if (array == null || array.length <= 1) return array;
        for (int i = 0; i < array.length; i++) {
            for (int j = i + 1; j < array.length; j++) {
                if (array[j] <= array[i]) {
                    int temp = array[i];
                    array[i] = array[j];
                    array[j] = temp;
                }
            }
        }
        return array;
    }

插入排序

內存優先

首先位置1上的數和位置0上的數進行比較,如果位置1上的數大於位置0上的數,將位置0上的數向後移一位,將1插入到0位置,否則不處理。位置k上的數和之前的數依次進行比較,如果位置K上的數更大,將之前的數向後移位,最後將位置k上的數插入不滿足條件點,反之不處理。


    public static int[] insertSort(int[] array) {
        if (array == null || array.length <= 1)
            return array;
        int j;
        for (int i = 1; i < array.length; i++) {
            // 如果 後一個 小於 前一個,就向前繼續查找
            if (array[i] < array[i - 1]) {
                int temp = array[i];
                // 如果 當前大於temp 就向後移動一位
                for (j = i; (j > 0) && (array[j-1] > temp); j--) {
                    array[j] = array[j - 1];
                }
                array[j] = temp;
            }
    
        }
        return array;
    }

2、鍊表算法

判斷鍊表有環

思路:快慢指針

兩鍊表合併

思路:

1、鍊表 head 記錄初始鍊表,tempHead 記錄當前點的鍊表 2、輸入l1 與 l2 長度可能不一致 3、進位記錄 carry


public static ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        int carry = 0;
        ListNode head = new ListNode();
        ListNode tempHead = head;
        head.next = tempHead;
        while (l1 != null || l2 != null) {
            ListNode tempNode = new Listnode();
            int l1val = l1 == null ? 0 : l1.val;
            int l2val = l2 == null ? 0 : l2.val;
            int result = l1val + l2val + carry;

            carry = result / 10;
            tempNode.val = result % 10;
    
            tempHead.next = tempNode;
            tempHead = tempHead.next;
            
            if (l1 != null) {
                l1 = l1.next;
            }
            if (l2 != null) {
                l2 = l2.next;
            }
        }
        if (carry > 0) {
            ListNode tail = new ListNode(carry);
            tempHead.next = tail;
        }
        return head.next;
    }

查找單鍊表中的倒數第k個結點

快慢指針

如何實現一個lru

前後結點

如何定位鍊表尾部前面的第k個節點,寫一下

雙指針

單鍊表的反轉

方式一:

 public ListNode reverseList(ListNode head) {
       if(head==null) return head;
        if (head.next==null) return head;
        ListNode lastNode = reverseList(head.next);
        head.next.next = head;
        head.next = null;
        return lastNode;
    }

方式二:

public ListNode reverseList(ListNode head) {
        if (head == null) return head;
        ListNode tempHead = new ListNode(0);
        ListNode tempNext = tempHead.next;
        while (head != null) {
            ListNode curNext = head.next;
            head.next = tempNext;
            tempNext = head;
            head = curNext;
        }
        return tempNext;
    }

從尾到頭列印單鍊表

思路: 使用棧

合併兩個有序的單鍊表,合併之後的鍊表依然有序

 public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        ListNode prehead = new ListNode(-1);

        ListNode prev = prehead;
        while (l1 != null && l2 != null) {
            if (l1.val <= l2.val) {
                prev.next = l1;
                l1 = l1.next;
            } else {
                prev.next = l2;
                l2 = l2.next;
            }
            prev = prev.next;
        }
    
        // 合併後 l1 和 l2 最多只有一個還未被合併完,我們直接將鍊表末尾指向未合併完的鍊表即可
        prev.next = l1 == null ? l2 : l1;
    
        return prehead.next;
    }

3、二叉樹算法

每個結點類:

 1     public class TreeNode{
 2         private String data;//自己結點數據
 3         private TreeNode leftChild;//左孩子
 4         private TreeNode rightChild;//右孩子
 5
 6         public String getData() {
 7             return data;
 8         }
 9
10         public void setData(String data) {
11             this.data = data;
12         }
13
14         public TreeNode(String data){
15             this.data = data;
16             this.leftChild = null;
17             this.rightChild = null;
18         }
19     }

很簡單,每個結點信息包含自己結點數據以及指向左右孩子的指針(為了方便,我這裡就叫指針了)。

二叉樹的創建

我們創建如下二叉樹:

代碼實現:

public class BinaryTree {
    private TreeNode  root = null;

    public TreeNode getRoot() {
        return root;
    }

    public BinaryTree(){
        root = new TreeNode("A");
    }

    /**
     * 構建二叉樹
     *          A
     *     B        C
     *  D    E    F   G
     */
    public void createBinaryTree(){
        TreeNode nodeB = new TreeNode("B");
        TreeNode nodeC = new TreeNode("C");
        TreeNode nodeD = new TreeNode("D");
        TreeNode nodeE = new TreeNode("E");
        TreeNode nodeF = new TreeNode("F");
        TreeNode nodeG = new TreeNode("G");
        root.leftChild = nodeB;
        root.rightChild = nodeC;
        nodeB.leftChild = nodeD;
        nodeB.rightChild = nodeE;
        nodeC.leftChild = nodeF;
        nodeC.rightChild = nodeG;
    }
        。。。。。。。
}

創建BinaryTree的時候就已經創建根結點A,createBinaryTree()方法中創建其餘結點並且建立相應關係。

獲得二叉樹的高度

樹中節點的最大層次稱為樹的高度,因此獲得樹的高度需要遞歸獲取所有節點的高度,取最大值。

     /**
     * 求二叉樹的高度
     * @author Administrator
     *
     */
    public int getHeight(){
        return getHeight(root);
    }

    private int getHeight(TreeNode node) {
        if(node == null){
            return 0;
        }else{
            int i = getHeight(node.leftChild);
            int j = getHeight(node.rightChild);
            return (i<j)?j+1:i+1;
        }
    }        

獲取二叉樹的結點數

獲取二叉樹結點總數,需要遍歷左右子樹然後相加

 1     /**
 2      * 獲取二叉樹的結點數
 3      * @author Administrator
 4      *
 5      */
 6     public int getSize(){
 7         return getSize(root);
 8     }
 9
10     private int getSize(TreeNode node) {
11         if(node == null){
12             return 0;
13         }else{
14             return 1+getSize(node.leftChild)+getSize(node.rightChild);
15         }
16     }

二叉樹的遍歷

二叉樹遍歷分為前序遍歷,中序遍歷,後續遍歷,主要也是遞歸思想,下面直接給出代碼

    /**
     * 前序遍歷——疊代
     * @author Administrator
     *
     */
    public void preOrder(TreeNode node){
        if(node == null){
            return;
        }else{
            System.out.println("preOrder data:"+node.getData());
            preOrder(node.leftChild);
            preOrder(node.rightChild);
        }
    }

    /**
     * 中序遍歷——疊代
     * @author Administrator
     *
     */
    public void midOrder(TreeNode node){
        if(node == null){
            return;
        }else{
            midOrder(node.leftChild);
            System.out.println("midOrder data:"+node.getData());
            midOrder(node.rightChild);
        }
    }

    /**
     * 後序遍歷——疊代
     * @author Administrator
     *
     */
    public void postOrder(TreeNode node){
        if(node == null){
            return;
        }else{
            postOrder(node.leftChild);
            postOrder(node.rightChild);
            System.out.println("postOrder data:"+node.getData());
        }
    }

獲取某一結點的父結點

獲取結點的父節點也是遞歸思想,先判斷當前節點左右孩子是否與給定節點信息相等,相等則當前結點即為給定結點的父節點,否則繼續遞歸左子樹,右子樹。

 1 /**
 2      * 查找某一結點的父結點
 3      * @param data
 4      * @return
 5      */
 6     public TreeNode getParent(String data){
 7         //封裝為內部結點信息
 8         TreeNode node = new TreeNode(data);
 9         //
10         if (root == null || node.data.equals(root.data)){
11             //根結點為null或者要查找的結點就為根結點,則直接返回null,根結點沒有父結點
12             return null;
13         }
14         return getParent(root, node);//遞歸查找
15     }
16
17     public TreeNode getParent(TreeNode subTree, TreeNode node) {
18
19         if (null == subTree){//子樹為null,直接返回null
20             return null;
21         }
22         //判斷左或者右結點是否與給定結點相等,相等則此結點即為給定結點的父結點
23         if(subTree.leftChild.data.equals(node.data) || subTree.rightChild.data.equals(node.data)){
24             return subTree;
25         }
26         //以上都不符合,則遞歸查找
27         if (getParent(subTree.leftChild,node)!=null){//先查找左子樹,左子樹找不到查詢右子樹
28             return getParent(subTree.leftChild,node);
29         }else {
30             return getParent(subTree.rightChild,node);
31         }
32     }

以上就是Android常見的排序、鍊表、二叉樹算法面試題;當然不止這些比喻: 堆/棧/隊列 、 哈希 、遞歸/回溯 、 動態規劃 、 字符串 、 雙指針 、 貪心算法 、 模擬 等等。想要在面試中披荊斬棘獲取更好的offer,可以前往獲取《Android面試精選題》內容很全面大家可以放心刷題。offer必到手!

【私信:「手冊」獲取】《Android精選面試題庫》

總結

找崗位要注意的是: 找准自己的定位

  • 比如你特別想去A公司,你現在公司是10K,
  • 先找幾個BCDE公司練練手,薪酬談判的時候直接要高價,比如20K
  • 如果對方想也沒想就拒絕了,說明自己現在還不夠格,下次面試要15試試
  • 如果對方猶豫或者答應了,下次面試你就可以要20K 以此類推

目的就是找准市場給自己的定價,心裡有一個譜。先找到自己的定位,然後再談判。

關鍵字: