🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
上一课时我们介绍了数据结构的知识,数据结构属于计算机存储的基础,有了它才能更好地将数据进行存储。而算法可以这样理解:它是为数据结构服务的,使用合适的算法可以更快地操作和查询这些数据。 算法的内容有很多,随随便便一本算法书有个 700 页到 1500 页也是很平常的事,因此我们在这里不能把所有的算法问题全部讲到,即使专门再开设一个算法专栏,也只能挑重点的讲。那么我们好钢就要用在刀刃上,本课时会把面试中经常出现的和平常工作中使用频率最高的算法,拿出来给大家分享。 我们本课时的面试题是,你知道哪些算法?讲一下它的内部实现? #### 典型回答 最常见、最基础的算法是二分法,它是二分查找算法的简称(Binary Search Algorithm),也叫折半搜索算法或对数搜索算法。它是一种在有序数组中查找某一特定元素的搜索算法,顾名思义,是将一组有序元素中的数据划分为两组,通过判断中间值来确认要查找值的大致位置,然后重复此过程进行元素查询。 例如,我们要查询 1~100 中的某个数值,比如我们要查询的数值为 75,如果按照顺序从 1 开始一直往后排序对比的话,需要经历 75 次,才能查询到我们想要的数据;而如果使用二分法,则会先判断 50(1~100 的中间值)和 75 哪个大,然后就能确定要查询的值是在 50~100 之间,最后再进行二分,用 75 和 75 进行比较,结果发现此值就是我们想要找的那个值,于是我们只用了两步就找到了要查询的值,这就是算法的“魔力”。 ![](https://img.kancloud.cn/84/39/8439377347fa81b9a1d80c0f4b4fdc80_558x264.png) ![](https://img.kancloud.cn/3e/21/3e21b8d33181881b4cf04386be14ee5f_578x236.png) 二分查找算法的实现代码如下: ``` public class AlgorithmExample { public static void main(String[] args) { // 要查询的数组 int[] binaryNums = new int[100]; for (int i = 0; i < 100; i++) { // 初始化数组(存入 100 个数据) binaryNums[i] = (i + 1); } // 要查询的数值 int findValue = 75; // 调用二分查找算法 int binaryResult = binarySearch(binaryNums, 0, binaryNums.length - 1, findValue); // 打印结果 System.out.println("元素的位置是:" + (binaryResult + 1)); } /** * 二分查找算法(返回该值第一次出现的位置) * @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; } } ``` 以上程序的执行结果为: ``` 元素的位置是:75 ``` 从上面的内容我们可以看出二分法虽然简单,但是也要满足一个特定的条件才行,那就是要使用二分法必须是有序排列的数值才行,不然是没办法实现二分法的。 除了二分法之外还有另一个比较常用的排序算法:冒泡排序。 冒泡排序(Bubble Sort)又被称为泡式排序,它是指重复走访要排序的数列,每次比较两个元素,如果顺序不对就进行交换,直到没有被交换的元素为止,这样就完成了一次冒泡排序。 为了让大家更好地理解冒泡排序,我录制了一个 gif 图片用于展示冒泡排序的执行过程,如下图所示: ![](https://img.kancloud.cn/17/fe/17fe8dcc69b3b258e2b5b7bb8b89f575_451x278.gif) 由上图可以看出,冒泡排序的关键执行流程是:依次对比相邻的两个数字,如果前面的数字大于后面的数字,那么就将前、后两个数字进行位置交换;这样每次对比完一轮数据之后,能找出此轮最大的数字并放置到尾部,如此重复,直到没有可以交换的数据为止,这样就完成了冒泡排序。 接下来我们就使用 Java 代码来实现一个冒泡排序算法,实现代码如下: ``` import java.util.Arrays; public class AlgorithmExample { public static void main(String[] args) { // 待排序数组 int[] nums = {33, 45, 11, 22, 12, 39, 27}; bubbleSort(nums); // 打印排序完数组 System.out.println(Arrays.toString(nums)); } /** * 冒泡排序 */ private static void bubbleSort(int[] nums) { for (int i = 1; i < nums.length; i++) { for (int j = 0; j < nums.length - i; j++) { if (nums[j] > nums[j + 1]) { // 前面数字大于后面的数字,执行位置交换 int temp = nums[j]; nums[j] = nums[j + 1]; nums[j + 1] = temp; } } } } } ``` 以上程序的执行结果为: ``` [11, 12, 22, 27, 33, 39, 45] ``` 从以上结果可以看出,冒泡排序算法的执行成功了,结果也符合我们的预期。 #### 考点分析 冒泡排序和二分法属于程序员必须掌握的两种最基础的算法了,如果连这两个算法都不知道或者都不能手写这两种算法的话,可能会给面试官留下非常不好的印象。因为二者都属于基础中的基础了,其实只要理解了两种算法的核心思想,再手写代码也不是什么难事,如果实在写不出具体的代码,最起码要做到能写出伪代码的程度。 和此知识点相关的面试题,还有以下这些: * 如何优化冒泡排序算法? * 是否还知道更多的算法? #### 知识扩展 * [ ] 冒泡排序优化 从上面冒泡排序的 gif 图片可以看出,在最后一轮对比之前,数组的排序如下图所示: ![](https://img.kancloud.cn/80/d1/80d1bdec40ae702457b7cbdf8c6db4be_846x514.png) 从图片可以看出,此时数组已经完全排序好了,但是即使这样,冒泡排序还是又执行了一次遍历对比,如下图所示: ![](https://img.kancloud.cn/17/fe/17fe8dcc69b3b258e2b5b7bb8b89f575_451x278.gif) 因此我们就可以想办法去掉无效的遍历,这样就可以优化冒泡排序的执行效率了。 我们可以在第一层循环内加一个判断标识,每次赋值为 true,假如在第二层循环(内层循环)时执行了位置交换,也就是 if 中的代码之后,我们把此值设置成 false;如果执行完内层循环判断之后,变量依然为 true,这就说明没有可以移动的元素了,冒泡排序可以结束执行了,优化后的代码如下所示: ``` import java.util.Arrays; public class AlgorithmExample { public static void main(String[] args) { // 待排序数组 int[] nums = {33, 45, 11, 22, 12, 39, 27}; bubbleSort(nums); // 打印排序完数组 System.out.println(Arrays.toString(nums)); } /** * 冒泡排序 */ private static void bubbleSort(int[] nums) { for (int i = 1; i < nums.length; i++) { // 判断标识 boolean flag = true; for (int j = 0; j < nums.length - i; j++) { if (nums[j] > nums[j + 1]) { // 前面数字大于后面的数字,执行位置交换 int temp = nums[j]; nums[j] = nums[j + 1]; nums[j + 1] = temp; // 执行了位置交换,更改标识 flag = false; } } if (flag) { // 没有可以移动的元素了,跳出循环 break; } } } } ``` 以上程序的执行结果为: ``` [11, 12, 22, 27, 33, 39, 45] ``` 此结果说明,冒泡排序的执行符合我们的预期,执行成功。 * [ ] 插入排序 插入排序(Insertion Sort)算法是指依次循环当前的列表,通过对比将每个元素插入到合适的位置,它的具体执行过程,如下图所示: ![](https://img.kancloud.cn/74/a7/74a7331f68d6a2173ccdcd77df469142_583x553.gif) 插入算法的具体实现代码如下: ``` public class AlgorithmExample { public static void main(String[] args) { int[] insertNums = {4, 33, 10, 13, 49, 20, 8}; // 插入排序调用 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; } } } ``` 以上程序的执行结果为: ``` 插入排序后:[4, 8, 10, 13, 20, 33, 49] ``` * [ ] 选择排序 选择排序(Selection Sort)算法是指依次循环数组,每轮找出最小的值放到数组的最前面,直到循环结束就能得到一个有序数组,它的执行过程如下图所示: ![](https://img.kancloud.cn/c2/83/c2832274b9eda42de6fdeef085b3ffd2_550x388.gif) 选择排序的具体实现代码如下: ``` public class AlgorithmExample { public static void main(String[] args) { int[] insertNums = {4, 33, 10, 13, 49, 20, 8}; // 调用选择排序 selectSort(insertNums); System.out.println("选择排序后结果:" + Arrays.toString(insertNums)); } /** * 选择排序 */ 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)); } } } ``` 以上程序的执行结果为: ``` 第0次排序:[4, 33, 10, 13, 49, 20, 8] 第1次排序:[4, 8, 10, 13, 49, 20, 33] 第2次排序:[4, 8, 10, 13, 49, 20, 33] 第3次排序:[4, 8, 10, 13, 49, 20, 33] 第4次排序:[4, 8, 10, 13, 20, 49, 33] 第5次排序:[4, 8, 10, 13, 20, 33, 49] 选择排序后结果:[4, 8, 10, 13, 20, 33, 49] ``` #### 小结 本着将一个知识点吃透的原则,本课时我们总共介绍了四种算法:冒泡排序算法、二分法、插入排序算法、选择排序算法等,并且还讲了冒泡排序算法的优化。但由于篇幅的原因,这些只能介绍一些常用的算法,如果想要更加深入地学习算法,还需要你投入更多的时间来学习,推荐阅读《算法》(第 4 版)的内容。