Java筆試面試-算法常用面試題

1.說一下什麼是二分法?使用二分法時需要注意什麼?如何用代碼實現?

  二分法查找(Binary Search)也稱折半查找,是指當每次查詢時,將數據分爲前後兩部分,再用中值和待搜索的值進行比較,如果搜索的值大於中值,則使用同樣的方式(二分法)向後搜索,反之則向前搜索,直到搜索結束爲止。
 二分法使用的時候需要注意:二分法只適用於有序的數據,也就是說,數據必須是從小到大,或是從大到小排序的。

public class Lesson7_4 {
    public static void main(String[] args) {
        // 二分法查找
        int[] binaryNums = {1, 6, 15, 18, 27, 50};
        int findValue = 27;
        int binaryResult = binarySearch(binaryNums, 0, binaryNums.length - 1, findValue);
        System.out.println("元素第一次出現的位置(從0開始):" + binaryResult);
    }
    /**
     * 二分查找,返回該值第一次出現的位置(下標從 0 開始)
     * @param nums      查詢數組
     * @param start     開始下標
     * @param end       結束下標
     * @param findValue 要查找的值
     * @return int
     */
    private static int binarySearch(int[] nums, int start, int end, int findValue) {
        if (start <= end) {
            // 中間位置
            int middle = (start + end) / 2;
            // 中間的值
            int middleValue = nums[middle];
            if (findValue == middleValue) {
                // 等於中值直接返回
                return middle;
            } else if (findValue < middleValue) {
                // 小於中值,在中值之前的數據中查找
                return binarySearch(nums, start, middle - 1, findValue);
            } else {
                // 大於中值,在中值之後的數據中查找
                return binarySearch(nums, middle + 1, end, findValue);
            }
        }
        return -1;
    }
}

執行結果如下:

元素第一次出現的位置(從0開始):4

2.什麼是斐波那契數列?用代碼如何實現?

  斐波那契數列(Fibonacci Sequence),又稱黃金分割數列、因數學家列昂納多·斐波那契(Leonardoda Fibonacci)以兔子繁殖爲例子而引入,故又稱爲“兔子數列”,指的是這樣一個數列:0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233,377,610,987,1597,2584,4181,6765,10946,17711…… 在數學上,斐波那契數列以如下被以遞推的方法定義:
  F(1)=1,F(2)=1, F(n)=F(n-1)+F(n-2)(n>=3,n∈N*)在現代物理、準晶體結構、化學等領域,斐波納契數列都有直接的應用。

  斐波那契數列之所以又稱黃金分割數列,是因爲隨着數列項數的增加,前一項與後一項之比越來越逼近黃金分割的數值 0.6180339887……

  斐波那契數列指的是這樣一個數列:0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233,377,610,987,1597,2584,4181,6765,10946,17711……

  斐波那契數列的特徵:第三項開始(含第三項)它的值等於前兩項之和。

  斐波那契數列代碼實現示例,如下所示:

public class Lesson7_4 {
    public static void main(String[] args) {
        // 斐波那契數列
        int fibonacciIndex = 7;
        int fibonacciResult = fibonacci(fibonacciIndex);
        System.out.println("下標(從0開始)" + fibonacciIndex + "的值爲:" + fibonacciResult);
    }
    /**
     * 斐波那契數列
     * @param index 斐波那契數列的下標(從0開始)
     * @return int
     */
    private static int fibonacci(int index) {
        if (index == 0 || index == 1) {
            return index;
        } else {
            return fibonacci(index - 1) + fibonacci(index - 2);
        }
    }
}

執行結果如下:

下標(0開始)7的值爲:13

3.一般而言,兔子在出生兩個月後,就有繁殖能力,一對兔子每個月能生出一對小兔子來。如果所有兔子都不死,那麼一年以後可以繁殖多少對兔子?請使用代碼實現。

  先來分析一下,本題目

  • 第一個月:有 1 對小兔子;
  • 第二個月:小兔子變成大兔子;
  • 第三個月:大兔子下了一對小兔子;
  • 第四個月:大兔子又下了一對小兔子,上個月的一對小兔子變成了大兔子;
    ……
    最後總結的規律如下列表所示:

在這裏插入圖片描述
可以看出,兔子每個月的總對數剛好符合斐波那契數列,第 12 個月的時候,總共有 144 對兔子。 實現代碼如下:

public class Lesson7_4 {
    public static void main(String[] args) {
        // 兔子的總對數
        int rabbitNumber = fibonacci(12);
        System.out.println("第 12 個月兔子的總對數是:" + rabbitNumber);
    }
    /**
     * 斐波那契數列
     * @param index 斐波那契數列的下標(從0開始)
     * @return int
     */
    private static int fibonacci(int index) {
        if (index == 0 || index == 1) {
            return index;
        } else {
            return fibonacci(index - 1) + fibonacci(index - 2);
        }
    }
}

執行結果如下:

12 個月兔子的總對數是:144

4.什麼是冒泡排序?用代碼如何實現?

  冒泡排序(Bubble Sort)算法是所有排序算法中最簡單、最基礎的一個,它的實現思路是通過相鄰數據的交換達到排序的目的。

  冒泡排序的執行流程是:

  • 對數組中相鄰的數據,依次進行比較;
  • 如果前面的數據大於後面的數據,則把前面的數據交換到後面。經過一輪比較之後,就能把數組中最大的數據排到數組的最後面了;
  • 再用同樣的方法,把剩下的數據逐個進行比較排序,最後得到就是從小到大排序好的數據。

  冒泡排序算法代碼實現,如下所示:

public class Lesson7_4 {
    public static void main(String[] args) {
        // 冒泡排序調用
        int[] bubbleNums = {132, 110, 122, 90, 50};
        System.out.println("排序前:" + Arrays.toString(bubbleNums));
        bubbleSort(bubbleNums);
        System.out.println("排序後:" + Arrays.toString(bubbleNums));
    }
    /**
     * 冒泡排序
     */
    private static void bubbleSort(int[] nums) {
        int temp;
        for (int i = 1; i < nums.length; i++) {
            for (int j = 0; j < nums.length - i; j++) {
                if (nums[j] > nums[j + 1]) {
                    temp = nums[j];
                    nums[j] = nums[j + 1];
                    nums[j + 1] = temp;
                }
            }
            System.out.print("第" + i + "次排序:");
            System.out.println(Arrays.toString(nums));
        }
    }
}

執行結果如下:

排序前:[132, 110, 122, 90, 50]1次排序:[110, 122, 90, 50, 132]2次排序:[110, 90, 50, 122, 132]3次排序:[90, 50, 110, 122, 132]4次排序:[50, 90, 110, 122, 132]

排序後:[50, 90, 110, 122, 132]

5.什麼是選擇排序?用代碼如何實現?

  選擇排序(Selection Sort)算法也是比較簡單的排序算法,其實現思路是每一輪循環找到最小的值,依次排到數組的最前面,這樣就實現了數組的有序排列。

  比如,下面是一組數據使用選擇排序的執行流程:

  • 初始化數據:18, 1, 6, 27, 15
  • 第一次排序:1, 18, 6, 27, 15
  • 第二次排序:1, 6, 18, 27, 15
  • 第三次排序:1, 6, 15, 27, 18
  • 第四次排序:1, 6, 15, 18, 27

  選擇排序算法代碼實現,如下所示:

public class Lesson7_4 {
    public static void main(String[] args) {
        // 選擇排序調用
        int[] selectNums = {18, 1, 6, 27, 15};
        System.out.println("排序前:" + Arrays.toString(selectNums));
        selectSort(selectNums);
        System.out.println("排序後:" + Arrays.toString(selectNums));
    }
    /**
     * 選擇排序
     */
    private static void selectSort(int[] nums) {
        int index;
        int temp;
        for (int i = 0; i < nums.length - 1; i++) {
            index = i;
            for (int j = i + 1; j < nums.length; j++) {
                if (nums[j] < nums[index]) {
                    index = j;
                }
            }
            if (index != i) {
                temp = nums[i];
                nums[i] = nums[index];
                nums[index] = temp;
            }
            System.out.print("第" + i + "次排序:");
            System.out.println(Arrays.toString(nums));
        }
    }
}

執行結果如下:

排序前:[18, 1, 6, 27, 15]0次排序:[1, 18, 6, 27, 15]1次排序:[1, 6, 18, 27, 15]2次排序:[1, 6, 15, 27, 18]3次排序:[1, 6, 15, 18, 27]

排序後:[1, 6, 15, 18, 27]

6.什麼是插入排序?用代碼如何實現?

  插入排序(Insertion Sort)算法是指依次把當前循環的元素,通過對比插入到合適位置的排序算法。 比如,下面是一組數據使用插入排序的執行流程:

  • 初始化數據:18, 1, 6, 27, 15
  • 第一次排序:1, 18, 6, 27, 15
  • 第二次排序:1, 6, 18, 27, 15
  • 第三次排序:1, 6, 18, 27, 15
  • 第四次排序:1, 6, 15, 18, 27

  插入排序算法代碼實現,如下所示:

public class Lesson7_4 {
    public static void main(String[] args) {
        // 插入排序調用
        int[] insertNums = {18, 1, 6, 27, 15};
        System.out.println("排序前:" + Arrays.toString(insertNums));
        insertSort(insertNums);
        System.out.println("排序後:" + Arrays.toString(insertNums));
    }
    /**
     * 插入排序
     */
    private static void insertSort(int[] nums) {
        int i, j, k;
        for (i = 1; i < nums.length; i++) {
            k = nums[i];
            j = i - 1;
            // 對 i 之前的數據,給當前元素找到合適的位置
            while (j >= 0 && k < nums[j]) {
                nums[j + 1] = nums[j];
                // j-- 繼續往前尋找
                j--;
            }
            nums[j + 1] = k;
            System.out.print("第" + i + "次排序:");
            System.out.println(Arrays.toString(nums));
        }
    }
}

執行結果如下:

排序前:[18, 1, 6, 27, 15]1次排序:[1, 18, 6, 27, 15]2次排序:[1, 6, 18, 27, 15]3次排序:[1, 6, 18, 27, 15]4次排序:[1, 6, 15, 18, 27]

排序後:[1, 6, 15, 18, 27]

7.什麼是快速排序?用代碼如何實現?

  快速排序(Quick Sort)算法和冒泡排序算法類似,都是基於交換排序思想實現的,快速排序算法是對冒泡排序算法的改進,從而具有更高的執行效率。

  快速排序是通過多次比較和交換來實現排序的執行流程如下:

  • 首先設定一個分界值,通過該分界值把數組分爲左右兩個部分;
  • 將大於等於分界值的元素放到分界值的右邊,將小於分界值的元素放到分界值的左邊;
  • 然後對左右兩邊的數據進行獨立的排序,在左邊數據中取一個分界值,把小於分界值的元素放到分界值的左邊,大於等於分界值的元素,放到數組的右邊;右邊的數據也執行同樣的操作;
  • 重複上述操作,當左右各數據排序完成後,整個數組也就完成了排序。

  快速排序算法代碼實現,如下所示:

public class Lesson7_4 {
    public static void main(String[] args) {
        // 快速排序調用
        int[] quickNums = {18, 1, 6, 27, 15};
        System.out.println("排序前:" + Arrays.toString(quickNums));
        quickSort(quickNums, 0, quickNums.length - 1);
        System.out.println("排序後:" + Arrays.toString(quickNums));
    }
    /**
     * 快速排序
     */
    private static void quickSort(int[] nums, int left, int right) {
        int f, t;
        int ltemp = left;
        int rtemp = right;
        // 分界值
        f = nums[(left + right) / 2];
        while (ltemp < rtemp) {
            while (nums[ltemp] < f) {
                ++ltemp;
            }
            while (nums[rtemp] > f) {
                --rtemp;
            }
            if (ltemp <= rtemp) {
                t = nums[ltemp];
                nums[ltemp] = nums[rtemp];
                nums[rtemp] = t;
                --rtemp;
                ++ltemp;
            }
        }
        if (ltemp == rtemp) {
            ltemp++;
        }
        if (left < rtemp) {
            // 遞歸調用
            quickSort(nums, left, ltemp - 1);
        }
        if (right > ltemp) {
            // 遞歸調用
            quickSort(nums, rtemp + 1, right);
        }
    }
}

執行結果如下:

排序前:[18, 1, 6, 27, 15]

排序後:[1, 6, 15, 18, 27]

8.什麼是堆排序?用代碼如何實現?

  堆排序(Heap Sort)算法是利用堆結構和二叉樹的一些特性來完成排序的。 堆結構是一種樹結構,準確來說是一個完全二叉樹。完全二叉樹每個節點應滿足以下條件:

  • 如果按照從小到大的順序排序,要求非葉節點的數據要大於等於,其左、右子節點的數據;
  • 如果按照從大到小的順序排序,要求非葉節點的數據小於等於,其左、右子節點的數據。

  可以看出,堆結構對左、右子節點的大小沒有要求,只規定葉節點要和子節點(左、右)的數據滿足大小關係。

  比如,下面是一組數據使用堆排序的執行流程:
在這裏插入圖片描述
堆排序算法代碼實現,如下所示:

public class Lesson7_4 {
    public static void main(String[] args) {
        // 堆排序調用
        int[] heapNums = {18, 1, 6, 27, 15};
        System.out.println("堆排序前:" + Arrays.toString(heapNums));
        heapSort(heapNums, heapNums.length);
        System.out.println("堆排序後:" + Arrays.toString(heapNums));
    }
    /**
     * 堆排序
     * @param nums 待排序數組
     * @param n    堆大小
     */
    private static void heapSort(int[] nums, int n) {
        int i, j, k, temp;
        // 將 nums[0,n-1] 建成大根堆
        for (i = n / 2 - 1; i >= 0; i--) {
            // 第 i 個節點,有右子樹
            while (2 * i + 1 < n) {
                j = 2 * i + 1;
                if ((j + 1) < n) {
                    // 右左子樹小於右子樹,則需要比較右子樹
                    if (nums[j] < nums[j + 1]) {
                        // 序號增加 1,指向右子樹
                        j++;
                    }
                }
                if (nums[i] < nums[j]) {
                    // 交換數據
                    temp = nums[i];
                    nums[i] = nums[j];
                    nums[j] = temp;
                    // 堆被破壞,重新調整
                    i = j;
                } else {
                    // 左右子節點均大,則堆未被破壞,不需要調整
                    break;
                }
            }
        }
        for (i = n - 1; i > 0; i--) {
            // 與第 i 個記錄交換
            temp = nums[0];
            nums[0] = nums[i];
            nums[i] = temp;
            k = 0;
            // 第 i 個節點有右子樹
            while (2 * k + 1 < i) {
                j = 2 * k + 1;
                if ((j + 1) < i) {
                    // 右左子樹小於右子樹,則需要比較右子樹
                    if (nums[j] < nums[j + 1]) {
                        // 序號增加 1,指向右子樹
                        j++;
                    }
                }
                if (nums[k] < nums[j]) {
                    // 交換數據
                    temp = nums[k];
                    nums[k] = nums[j];
                    nums[j] = temp;
                    // 堆被破壞,重新調整
                    k = j;
                } else {
                    // 左右子節點均大,則堆未被破壞,不需要調整
                    break;
                }
            }
            // 輸出每步排序結果
            System.out.print("第" + (n - i) + "次排序:");
            System.out.println(Arrays.toString(nums));
        }
    }
}

執行結果如下:

堆排序前:[18, 1, 6, 27, 15]1次排序:[18, 15, 6, 1, 27]2次排序:[15, 1, 6, 18, 27]3次排序:[6, 1, 15, 18, 27]4次排序:[1, 6, 15, 18, 27]

堆排序後:[1, 6, 15, 18, 27]
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章