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]