30-你知道哪些算法?讲一下它的内部实现过程?

上一课时我们介绍了数据结构的知识,数据结构属于计算机存储的基础,有了它才能更好地将数据进行存储。而算法可以这样理解:它是为数据结构服务的,使用合适的算法可以更快地操作和查询这些数据。

算法的内容有很多,随随便便一本算法书有个 700 页到 1500 页也是很平常的事,因此我们在这里不能把所有的算法问题全部讲到,即使专门再开设一个算法专栏,也只能挑重点的讲。那么我们好钢就要用在刀刃上,本课时会把面试中经常出现的和平常工作中使用频率最高的算法,拿出来给大家分享。

我们本课时的面试题是,你知道哪些算法?讲一下它的内部实现?

典型回答

最常见、最基础的算法是二分法,它是二分查找算法的简称(Binary Search Algorithm),也叫折半搜索算法或对数搜索算法。它是一种在有序数组中查找某一特定元素的搜索算法,顾名思义,是将一组有序元素中的数据划分为两组,通过判断中间值来确认要查找值的大致位置,然后重复此过程进行元素查询。

例如,我们要查询 1~100 中的某个数值,比如我们要查询的数值为 75,如果按照顺序从 1 开始一直往后排序对比的话,需要经历 75 次,才能查询到我们想要的数据;而如果使用二分法,则会先判断 50(1~100 的中间值)和 75 哪个大,然后就能确定要查询的值是在 50~100 之间,最后再进行二分,用 75 和 75 进行比较,结果发现此值就是我们想要找的那个值,于是我们只用了两步就找到了要查询的值,这就是算法的“魔力”。

在这里插入图片描述
在这里插入图片描述
二分查找算法的实现代码如下:

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 图片用于展示冒泡排序的执行过程,如下图所示:
在这里插入图片描述
由上图可以看出,冒泡排序的关键执行流程是:依次对比相邻的两个数字,如果前面的数字大于后面的数字,那么就将前、后两个数字进行位置交换;这样每次对比完一轮数据之后,能找出此轮最大的数字并放置到尾部,如此重复,直到没有可以交换的数据为止,这样就完成了冒泡排序。

接下来我们就使用 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 图片可以看出,在最后一轮对比之前,数组的排序如下图所示:
在这里插入图片描述
从图片可以看出,此时数组已经完全排序好了,但是即使这样,冒泡排序还是又执行了一次遍历对比,如下图所示:
在这里插入图片描述
因此我们就可以想办法去掉无效的遍历,这样就可以优化冒泡排序的执行效率了。

我们可以在第一层循环内加一个判断标识,每次赋值为 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)算法是指依次循环当前的列表,通过对比将每个元素插入到合适的位置,它的具体执行过程,如下图所示:

在这里插入图片描述
插入算法的具体实现代码如下:

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)算法是指依次循环数组,每轮找出最小的值放到数组的最前面,直到循环结束就能得到一个有序数组,它的执行过程如下图所示:
在这里插入图片描述
选择排序的具体实现代码如下:

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 版)的内容。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值