某廠後端研發實習(可轉正)面試

0.簡述

​ 面試流程,大概如下:先是40分鐘的筆試編程題,面試官給出3個編程題,讓我現場進行編程,至少完成其中一道,無IDE,用的是視頻對話的網頁自帶的檢查器(貌似只能檢查出基本類型拼寫錯誤及補全括號等基礎書寫問題)。編程題目如下圖

在這裏插入圖片描述

編程題時間到之後面試官會提醒(我太菜了,一題沒做出來),問我要再繼續做,還是講下我的思路。我選擇講下我的思路(畢竟還差不少才能寫出一題來QAQ),面試官聽完我的思路後也沒再說什麼。

​ 然後就是開始正式的面試,首先做個簡短自我介紹,之後面試官讓我簡單介紹一下我的項目情況,再輕描淡寫地問了我的團隊項目的實現原理。再後來開始問我一些基礎核心知識。

​ 首先,問的是數組和鏈表的優缺點,這個我答的還可以,然後問我 Java 裏面有哪些容器是用鏈表實現的,這個我沒答上來。再來就是問了我 Java 裏面的Map是怎樣的結構,其查詢效率又如何。之後又問了我排序算法都知道哪些(我說了冒泡、歸併、快排、桶排序、基數排序等),再問了我這些算法時間複雜度比較低的是哪些(快排和歸併),分別是多少(都是 nlogn ),再讓我仔細講一下快排是怎麼實現的(我忘記了 orz ),再然後,問了我二叉搜索樹是怎麼樣的數據結構(每個結點的值大於左子節點的值,小於或等於右子結點的值),二叉搜索樹的查詢效率是多少(這個我忘了 orz ,我懵了個 nlogn )。

​ 再來就問了一下數據庫的知識,問我學的什麼數據庫(MySQL),然後又問我引擎用的是什麼(這個我也答不上 orz ,我甚至到沒聽過這個概念,後來聽面試官說 InooDB ,我纔想起一點)。然後讓我講一下什麼是ACID屬性(我答得踉踉蹌蹌,並不好, orz ),然後舉例問我,假如數據庫裏存有某商品的庫存,現在查詢到這個數據,並執行減一的操作,這整個操作過程有什麼問題嗎?(我答得不是太好,大概說了一下事務的一致性原理)面試官又問我怎麼解決這個問題(我回答加鎖),面試官繼續追問,怎麼加鎖,技術上具體如何實現(我就被問倒了,後來面試官說直接加線程鎖是不行,因爲它不是內存裏的東西,我後知後覺才明白是數據庫的存儲過程,果然我數據庫還菜比 orz )。

​ 大概整個面試過程就這麼多,後面面試官提問結束後,會詢問我還有什麼問題,我就再跟面試官簡單聊了幾句(畢竟太菜了,沒臉接着聊了),然後就結束了。整個過程大概85分鐘左右。

1.筆試(1)

​ 第一道筆試題應該採用動態規劃來解決比較合適。
在這裏插入圖片描述
d[i][j]d[i][j]爲使用 arr 中(arr已按從小到大的順序排列)前 i 種零錢,找的零錢爲 j 的組合方式的種數。

d[i][j]=k=0j/d[i]d[i1][jkd[i]]d[i][j] = \sum_{k=0}^{j/d[i]} d[i-1][j-k*d[i]],即第 i 種有以下選擇:可以不用、可以用1個、可以用2個、…、可以用 j / d[ i ] 個。

初始化則爲:d[i][0]=1;d[0][j]=0;d[i][0] = 1; d[0][j] = 0; 即 湊成0元只有一種方法,也就是不用任何一種零錢;用前0種零錢湊成 j( j ≠ 0) 元的方法,爲0種,因爲前0種零錢即沒有零錢。

    public static void dynamic_prommgram(int[] arr,int n){
        int[][] d = new int[arr.length+1][n+1];//length+1表示不適用任何幣種、只使用1、只使用1 2 只使用1 2 3......等等,共length+1種情況,且n+1表示總計0、1.....至n元共n+1種情況
        for(int i = 0;i<=arr.length;i++) d[i][0] = 1;
        for(int i = 1 ;i<=arr.length;i++){//因爲d[0][i]是0,所以i從1開始
            for(int j = 1;j<=n;j++){//由於d[i][0]==1,所以j從1開始
                for(int k=0;k<=j/arr[i-1];k++){//例如,使用面值爲1時,對應的coins[]下標是i-1,邏輯上和實際上不是一致的
                    d[i][j] +=d[i-1][j-k*arr[i-1]];
                }
            }
        }
        System.out.println(d[arr.length][n]);
    }

再將這段代碼中的二元數組簡化爲一元數組即可。

    public int solution(int[] arr, int n){
        int[] d = new int[n+1];
        d[0] = 1;
        for (int i = 0; i < arr.length ;i++) {
            for (int j = arr[i]; j <= n; j++) {
                d[j] += d[j - arr[i]];
            }
        }
        int result = d[n];
        return result;
    }

2.筆試(2)

​ 第二道題是股票最大利潤問題。
在這裏插入圖片描述
首先是隻買一次的情況。

只要記錄前面的最小价格,將這個最小价格作爲買入價格,然後將當前的價格作爲出售價格,查看當前收益是不是最大利益。

    public int solution(int[] prices){
        if (prices.length == 0)
            return 0;
        int min = prices[0];
        int max = 0;
        for (int i = 1; i < prices.length; i++) {
            if (prices[i] < min)
                min = prices[i];
            else
                max = Math.max(max, prices[i] - min);
        }
        return max;
    }

然後是在日期不重疊的情況下賣買多次的最大利潤。

對於prices[ i ] > prices[ i-1 ], 那麼就可以進行一次賣買,即把prices[ i ] - prices[ i-1 ] 加入到收益當中,因爲我們可以昨天買入,今天賣出,若明天價更高的話,還可以今天買入,明天賣出。以此類推,遍歷完整個數組後即可求得最大利潤。

    public int solution2(int[] prices){
        int profit = 0;
        for (int i = 1; i < prices.length; i++) {
            if (prices[i] - prices[i-1] > 0)
                profit += prices[i] - prices[i-1];
        }
        return  profit;
    }

3.筆試(3)

​ 第三道題是36進制求和。
在這裏插入圖片描述
​ 這道題相對來說簡單點,可以考慮將‘0’ - ‘9’,‘a’ - ‘z’存儲到List中,index是0-35爲其對應的數字。定義一個StringBuilder,將每一位的計算 結果利用append方法加入其中,最後再用reverse方法倒轉過來即可,注意最後要先檢查進位temp是否爲0,如果不爲0,則需要再append字符‘1’,再倒轉。

import java.util.Arrays;
import java.util.List;

public class Main {
    static Character[] nums = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'};
    static List<Character> list = Arrays.asList(nums);

    public String solution(String num_a,String num_b){
        String result = new String();
        char[] n1 = num_a.toCharArray();
        char[] n2 = num_b.toCharArray();
        int i = num_a.length()-1;
        int j = num_b.length()-1;
        int temp = 0;
        StringBuilder stringBuilder = new StringBuilder();
        while(i >= 0 || j >=0){
            if (i >=0 && j >= 0){
                char c1 = n1[i];
                char c2 = n2[j];
                int index1 = list.indexOf(c1);
                int index2 = list.indexOf(c2);
                int sum = index1 +index2 + temp;
                if (sum >= 36){
                    temp = 1;
                    stringBuilder.append(list.get(sum % 36));
                }
                else {
                    temp = 0;
                    stringBuilder.append(list.get(sum));
                }
                i--;
                j--;
            }
            else if (i >= 0){
                int sum = list.indexOf(n1[i]) + temp;
                if (sum >= 36){
                    temp = 1;
                    stringBuilder.append(list.get(sum % 36));
                }
                else {
                    temp = 0;
                    stringBuilder.append(list.get(sum));
                }
                i--;
            }
            else {
                int sum = list.indexOf(n2[j]) + temp;
                if (sum >= 36){
                    temp = 1;
                    stringBuilder.append(list.get(sum % 36));
                }
                else {
                    temp = 0;
                    stringBuilder.append(list.get(sum));
                }
                j--;
            }
        }
        if (temp != 0){
            stringBuilder.append('1');
        }
        result = stringBuilder.reverse().toString();
        return result;
    }

    public static void main(String[] args) {
        Main main = new Main();
        String num_a = "1b";
        String num_b = "2x";
        System.out.println( main.solution(num_a,num_b));
    }
}

4.面試

​ 面試這邊的基礎核心知識整理爲一個個小問題來歸納吧。

4.1 數組和鏈表的優缺點

數組的優點

  • 隨機訪問性強
  • 查找速度快

數組的缺點

  • 插入和刪除效率低
  • 可能浪費內存
  • 內存空間要求高,必須有足夠的連續內存空間
  • 數組大小固定,不能動態拓展

鏈表的優點

  • 插入刪除速度快
  • 內存利用率高,不會浪費內存
  • 大小沒有固定,拓展很靈活

鏈表的缺點

  • 不能隨機查找,必須從第一個開始遍歷,查找效率低

4.2 Java裏有哪些容器是用鏈表實現的

帶Linked字眼都是用用鏈表實現的。

java容器類類庫:Collection & Map :
在這裏插入圖片描述
在這裏插入圖片描述

4.3 Java裏的Map是怎樣的結構,其查詢效率如何

Map 是一種鍵-值對(key-value)集合,Map 集合中的每一個元素都包含一個鍵對象和一個值對象。其中,鍵對象不允許重複,而值對象可以重複,並且值對象還可以是 Map 類型的,就像數組中的元素還可以是數組一樣。

Map 接口主要有兩個實現類:HashMap 類和 TreeMap 類。其中,HashMap 類按哈希算法來存取鍵對象,而 TreeMap 類可以對鍵對象進行排序。

HashMap是基於哈希表(散列表)實現的,時間複雜度平均能達到O(1)。

TreeMap是基於紅黑樹(一種自平衡二叉查找樹)實現的,時間複雜度平均能達到O(log n)。

4.4 幾大排序算法及其實現

4.4.1 排序算法說明

4.4.1.1 術語說明
  • 排序:對一序列對象根據某個關鍵字進行排序
  • 穩定:如果a原來在b前面,而a=b,排序之後a仍然在b的前面;
  • 不穩定:如果a原來在b前面,而a=b,排序之後a可能出會現在b的後面;
  • 內排序:所有排序操作都在內存中完成;
  • 外排序:由於數據太大,因此把數據放在磁盤中,而排序通過磁盤和內存的數據傳輸才能進行;
4.4.1.2 算法總結

在這裏插入圖片描述
註釋:

  • n:數據規模
  • k:“桶”的個數
  • In-place:原址的,不佔用額外內存
  • Out-place:佔用額外內存
4.4.1.3 算法分類
  • 根據待排序的數據大小不同,可分爲:內部排序(以下均爲內部排序)、外部排序
  • 根據穩定性:穩定排序、不穩定排序

特別地,對於內部排序:

  • 根據排序原理不同,可分爲:插入排序、交換(快速)排序、選擇排序、歸併排序、計數排序

  • 根據所需工作量劃分:簡單排序O(n2)O(n^2)O(nlogn)O(nlogn)、基數排序O(dn)O(d*n)

  • 比較排序與非比較排序:常見的快速排序、歸併排序、堆排序、冒泡排序等屬於比較排序。

    比較排序的優勢是,適用於各種規模的數據,也不在乎數據的分佈,都能進行排序。可以說,比較排序適用於一切需要排序的情況。

    計數排序、基數排序、桶排序則屬於非比較排序。非比較排序是通過確定每個元素之前,應該有多少個元素來排序。

    針對數組arr,計算arr[i]之前有多少個元素,則唯一確定了arr[i]在排序後數組中的位置 。非比較排序只要確定每個元素之前的已有的元素個數即可,所有一次遍歷即可解決。

    算法時間複雜度O(n)。非比較排序時間複雜度底,但由於非比較排序需要佔用空間來確定唯一位置。所以對數據規模和數據分佈有一定的要求。

4.4.2 冒泡排序(Bubble Sort)

  • 算法描述:比較相鄰的元素,如果第一個比第二個大,就交換它們兩個;

  • 代碼實現:

    public int[] bubbleSort(int[] array){
            if (array.length == 0)
                return array;
            for (int i = 0; i < array.length; i++){
                for (int j = 0; j < array.length - 1 - i; j++){
                    if (array[j] > array[j+1]){
                        int temp = array[j+1];
                        array[j+1] = array[j];
                        array[j] = temp;
                    }
                }
            }
            return array;
        }
    
  • 算法分析

    • 最佳情況:T(n)=O(n)T(n) = O(n)
    • 最壞情況:T(n)=O(n2)T(n) = O(n^2)
    • 平均情況:T(n)=O(n2)T(n) = O(n^2)

4.4.3 選擇排序(Selection Sort)

  • 算法描述:找到最小的數,放到第一個位置,再在剩下的數中找到最小的數放到第一個位置……直到第n-1趟,數組就有序化了。

  • 代碼實現:

    public int[] selectionSort(int[] array){
            if (array.length == 0)
              return array;
            for (int i = 0; i < array.length; i++){
                int minIndex = i;
                for (int j = i; j < array.length; j++){
                    if (array[j] < array[minIndex]){
                        minIndex = j;
                    }
                }//找到最小的數,與未排序序列的第一個位置的數交換
                int temp = array[minIndex];
                array[minIndex] = array[i];
                array[i] = temp;
            }
            return array;
        }
    
  • 算法分析

    • 最佳情況:T(n)=O(n2)T(n) = O(n^2)
    • 最壞情況:T(n)=O(n2)T(n) = O(n^2)
    • 平均情況:T(n)=O(n2)T(n) = O(n^2)

4.4.4 插入排序(Insertion Sort)

  • 算法描述:從第一個元素開始,認爲該元素已被排序,取出下一個元素,在已排序的元素序列中從後往前掃描,如果被掃描元素大於取出的元素,則將被掃描元素往後移一位,直到被掃描元素小於或等於取出的元素,將取出的元素放入被掃描元素後面,如此重複至最後一個元素被取出並放置。

  • 代碼實現:

    public int[] insertionSort(int[] array){
            if (array.length == 0)
                return array;
            for (int i = 1; i < array.length; i++){
                int preIndex = i-1;
                int current = array[i];//記錄取出的數
                while (preIndex >= 0 && array[preIndex] > current){
                    array[preIndex + 1] = array[preIndex];
                    preIndex--;
                }//被掃描元素大於取出的元素
                array[preIndex + 1] = current;
                //這裏已經將被取出元素爲最小元素的情況考慮
            }
            return array;
        }
    
  • 算法分析

    • 最佳情況:T(n)=O(n) T(n) = O(n)
    • 最壞情況:T(n)=O(n2) T(n) = O(n^2)
    • 平均情況:T(n)=O(n2) T(n) = O(n^2)

4.4.5 希爾排序(Shell Sort)

  • 算法描述:希爾排序也是一種插入排序,是插入排序的一個高效版本,也成爲縮小增量排序。

    算法需先選擇一個增量序列t1, t2, t3, … , tk, 其中 ti > tj, tk = 1;

    按照增量序列個數k,進行k趟排序;

    每趟排序,根據對應的增量ti ,將待排序列分割成若干長度爲m的子序列,分別對各子表進行直接插入排序。僅當增量因子爲 1 時,整個序列作爲一個表來處理,表長度即爲整個序列長度。

  • 代碼實現:

    public int[] shellSort(int[] array){
            int len = array.length;
            int temp,gap = len/2;//增量初始化爲整個序列長度的一半
            while (gap > 0){
                for (int i = gap; i < len; i++){
                    temp = array[i];
                    //對子表進行直接插入排序
                    int preIndex = i - gap;
                    while (preIndex >= 0 && array[preIndex] > temp){
                        array[preIndex + gap] = array[preIndex];
                        preIndex -= gap;
                    }
                    array[preIndex + gap] = temp;
                }
                gap /= 2;
            }
            return array;
        }
    
  • 算法分析

    • 希爾排序的算法分析是一個複雜的問題,其時間爲所取增量序列的函數,這涉及到一些數學上尚未解決的問題。時間複雜度在 O( n ^ (1.3-2) )。

4.4.6 歸併排序 (Merge Sort)

  • 算法描述:歸併排序是採用分治法的一個典型應用。算法將長度爲n的待排序列分爲兩個長度爲n/2的子序列;對這兩個子序列分別進行歸併排序;將排序好的子序列合併成一個最終的排序序列。分而治之。

  • 代碼實現:

    public static int[] mergeSort(int[] array){
            if (array.length < 2) return array;
            int mid = array.length/2;
        //分成兩個子序列分別進行歸併排序
            int[] left = Arrays.copyOfRange(array,0,mid);
            int[] right = Arrays.copyOfRange(array,mid,array.length);
            return merge(mergeSort(left),mergeSort(right));
        }
    
        public static int[] merge(int[] left, int[] right){
            int[] result = new int[left.length + right.length];
            //合成序列
            for (int index = 0, i = 0, j = 0; index < result.length; index++){
                if (i >= left.length)
                    result[index] = right[j++];
                else if (j >= right.length)
                    result[index] = left[i++];
                else if (left[i] > right[j])
                    result[index] = right[j++];
                else
                    result[index] = left[i++];
            }
            return result;
        }
    
  • 算法分析

    • 最佳情況:T(n)=O(n) T(n) = O(n)
    • 最壞情況:T(n)=O(nlogn) T(n) = O(n log n) (運用主定理或遞歸樹可求解)
    • 平均情況:T(n)=O(nlogn)T(n) = O(nlogn)

4.4.7 快速排序(Quick Sort)

  • 算法描述:快排也利用了分治的原理,先從待排序列中選出一個基準數(pivot),將比這個數大的全放到它的右邊,小於或等於它的放到左邊,再對左右兩邊遞歸進行同樣的操作。

  • 代碼實現:

    public static int[] quickSort(int[] array, int start, int end){
            if (array.length < 1 || start < 0 || end >= array.length || start > end) return null;
            int smallIndex = partition(array,start,end);
            if (smallIndex > start)
                quickSort(array,start,smallIndex - 1);//分界線前面有元素
            if (smallIndex < end)
                quickSort(array,smallIndex + 1, end);//分界線後面還有元素
            return array;
        }
    
        public static int partition(int[] array, int start, int end){
            int pivot = (int)(start + Math.random() * (end - start +1));
            int smallIndex = start - 1;//分界線的索引
            swap(array,pivot,end);//將基準值放到最後
            for (int i = start; i <= end; i++){
                //從第一個開始掃描到最後
                if (array[i] <= array[end]){
                    smallIndex++;
                    //小於或等於基準值的,說明在分界線的前面又要多留一個位置,所以+1
                    if (i > smallIndex)
                        swap(array,i,smallIndex);
                    //如果當前元素在分界線後,則須交換
                }
            }
            return smallIndex;
        }
    
        public static void swap(int[] array, int i, int j){
            int temp = array[i];
            array[i] = array[j];
            array[j] = temp;
        }
    //調用:quickSort(array, 0, array.length-1)
    
  • 算法分析:

    • 最佳情況:T(n)=O(nlogn)T(n) = O(n log n)
    • 最壞情況:T(n)=O(n2)T(n) = O(n^2 )
    • 平均情況:T(n)=O(nlogn)T(n) = O(n log n)

4.4.8 堆排序(Heap Sort)

  • 算法描述:建堆後,通過調整,初始化建成最大堆;將第一個元素(堆頂元素)與最後一個元素互換,將最後一個元素看作有序(離開最大堆),再將前n-1個重複上述操作,直到所有元素都不在堆裏。

  • 代碼實現:

    static int  length;//記錄堆中元素個數
    
        public int[] heapSort(int[] array){
            length = array.length;
            if (length < 1) return array;
            buildMaxHeap(array);
            while (length > 0){
                swap(array, 0, length - 1);
                length--;//堆頂元素與堆的最後一個元素互換,堆中元素數-1
                adjustHeap(array, 0);
            }
            return array;
        }
    
        public static void buildMaxHeap(int[] array){
            //從最後一個非葉子節點開始向上構造最大堆
            //i的左子樹和右子樹分別爲 2i+1和2i+2
            for (int i = (length/2 - 1); i >= 0; i--){
                adjustHeap(array,i);
            }
        }
    
        public static void adjustHeap(int[] array,int i){
            int maxIndex = i;
            //如果左子樹最大
            if (i * 2 < length && array[i * 2] > array[maxIndex])
                maxIndex = i * 2;
            //如果又子樹最大
            if (i * 2 + 1 < length && array[i * 2 + 1] >array[maxIndex])
                maxIndex = i * 2 + 1;
            if (maxIndex != i){
                swap(array, maxIndex, i);
                //交換完後該結點下面的結點也需重新調整
                adjustHeap(array, maxIndex);
            }
        }
    
  • 算法分析:(如何計算?)

    • 最佳情況:T(n)=O(nlogn) T(n) = O(n log n)
    • 最壞情況:T(n)=O(nlogn)T(n) = O(n log n)
    • 平均情況:T(n)=O(nlogn)T(n) = O(n log n)

4.4.9 計數排序(Counting Sort)

  • 算法描述:使用一個額外的數組,其中第 i 個元素是待排數組中值等於 i 的元素的個數。然後根據額外的數組來將待排序數組的元素排到正確的位置。只能對整數進行排序。

  • 代碼實現:

    public static int[] countingSort(int[] array) {
            if (array.length == 0) return array;
            int bias, min = array[0], max = array[0];
            for (int i = 1; i < array.length; i++) {
                if (array[i] > max)
                    max = array[i];
                if (array[i] < min)
                    min = array[i];
            }
            bias = 0 - min;//偏移量
            int[] bucket = new int[max - min + 1];//新建一個“桶”
            Arrays.fill(bucket, 0);//給數組bucket的元素全部賦值0
            for (int i = 0; i < array.length; i++) {
                bucket[array[i] + bias]++;
            }
            int index = 0, i = 0;
            while (index < array.length) {
                if (bucket[i] != 0) {
                    array[index] = i - bias;
                    bucket[i]--;
                    index++;
                } else
                    i++;
            }
            return array;
        }
    
  • 算法分析:

    • 最佳情況:T(n)=O(n+k)T(n) = O(n+k)
    • 最壞情況:T(n)=O(n+k)T(n) = O(n+k)
    • 平均情況:T(n)=O(n+k)T(n) = O(n+k)

4.4.10 桶排序(Bucket Sort)

  • 算法描述:桶排序是計數排序的升級版,它利用了函數的映射關係,高效與否就在與這個映射函數的確定。

  • 代碼實現:

    public static ArrayList<Integer> bucketSort(ArrayList<Integer> array, int bucketSize) {
            if (array == null || array.size() < 2)
                return array;
            int max = array.get(0), min = array.get(0);
            // 找到最大值最小值
            for (int i = 0; i < array.size(); i++) {
                if (array.get(i) > max)
                    max = array.get(i);
                if (array.get(i) < min)
                    min = array.get(i);
            }
            int bucketCount = (max - min) / bucketSize + 1;
            ArrayList<ArrayList<Integer>> bucketArr = new ArrayList<>(bucketCount);
            ArrayList<Integer> resultArr = new ArrayList<>();
            for (int i = 0; i < bucketCount; i++) {
                bucketArr.add(new ArrayList<Integer>());
            }
            for (int i = 0; i < array.size(); i++) {
                bucketArr.get((array.get(i) - min) / bucketSize).add(array.get(i));
            }
            for (int i = 0; i < bucketCount; i++) {
                if (bucketSize == 1) { // 如果待排序數組中有重複數字時
                    for (int j = 0; j < bucketArr.get(i).size(); j++)
                        resultArr.add(bucketArr.get(i).get(j));
                } else {
                    if (bucketCount == 1)
                        bucketSize--;
                    ArrayList<Integer> temp = bucketSort(bucketArr.get(i), bucketSize);
                    for (int j = 0; j < temp.size(); j++)
                        resultArr.add(temp.get(j));
                }
            }
            return resultArr;
        }
    
  • 算法分析:

    • 最佳情況:T(n)=O(n+k)T(n) = O(n+k)
    • 最壞情況:T(n)=O(n+k)T(n) = O(n+k)
    • 平均情況:T(n)=O(n)T(n) = O(n)

4.4.11 基數排序(Radix Sort)

  • 算法描述:先取得數組中最大的數,並取得其位數;array爲原始數組,從最低位開始取每個位組成radix數組;對radix進行計數排序(利用計數排序適用於小範圍數的特點)。

  • 代碼實現:

    public static int[] radixSort(int[] array) {
            if (array == null || array.length < 2)
                return array;
            // 1.先算出最大數的位數;
            int max = array[0];
            for (int i = 1; i < array.length; i++) {
                max = Math.max(max, array[i]);
            }
            int maxDigit = 0;
            while (max != 0) {
                max /= 10;
                maxDigit++;
            }
            int mod = 10, div = 1;
            ArrayList<ArrayList<Integer>> bucketList = new ArrayList<ArrayList<Integer>>();
            for (int i = 0; i < 10; i++)
                bucketList.add(new ArrayList<Integer>());
            for (int i = 0; i < maxDigit; i++, mod *= 10, div *= 10) {
                for (int j = 0; j < array.length; j++) {
                    int num = (array[j] % mod) / div;
                    bucketList.get(num).add(array[j]);
                }
                int index = 0;
                for (int j = 0; j < bucketList.size(); j++) {
                    for (int k = 0; k < bucketList.get(j).size(); k++)
                        array[index++] = bucketList.get(j).get(k);
                    bucketList.get(j).clear();
                }
            }
            return array;
        }
    
  • 算法分析:

    • 最佳情況:T(n)=O(nk)T(n) = O(n * k)
    • 最差情況:T(n)=O(nk)T(n) = O(n * k)
    • 平均情況:T(n)=O(nk)T(n) = O(n * k)
  • 基數排序有兩兩種方法:

    • MSD從高位開始進行排序
    • LSD從低位開始進行排序
  • 基數排序 VS 計數排序 VS 桶排序

    這三種排序算法都利用了“桶”的概念,但對“桶”的用法上有明顯差異:

    • 基數排序:根據鍵值的每位數字來分配桶
    • 計數排序:每個桶只存儲單一的鍵值
    • 桶排序:每個桶存儲一定範圍的數值

4.5 二叉搜索樹是怎樣的數據結構,其查詢效率又是多少

二叉搜索樹(二叉查找樹、二叉排序樹):根節點的值大於其左子樹中任意一個節點的值,小於其右節點中任意一節點的值。這個規則適用於BST中的每一個節點。其查詢效率爲O(h),其中h爲這棵樹的樹高,又因爲2h=n 2^h = n,所以h=logn+1h = log n + 1

本篇完。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章