Skip to content

常见算法


算法就是解决实际问题的过程和方法!就像我们用导航软件找最短路线一样,算法已经融入生活的方方面面。
无论是点外卖、刷短视频,还是做数据分析,算法都在悄悄影响着我们的选择和效率。

所以,学习算法能够锻炼系统化思考能力和编程思维。功利一点说,也是为了应付技术面试。

学习算法推荐两步:

  1. 理清每种算法的核心流程
  2. 动手写代码实现

排序算法

排序是把一组数据按照特定规则重新排列。比如我们生活中用导航软件找最短路线,其实背后也有排序算法在帮忙。

冒泡排序

每一轮遍历数组,把当前未排序部分中最大的元素“冒泡”到末尾。每完成一轮,未排序区间就缩小一格,直到全部有序。

简单流程如下:

  1. 从头到尾遍历数组,比较相邻元素,如果前面的比后面的大,就交换它们。
  2. 每一轮结束,最大的元素就被“冒泡”到末尾。
  3. 重复上述过程,直到没有需要交换的元素。

来看一个简化版的 Java 代码示例:

java
// 定义一个待排序数组
int[] arr = {5, 2, 3, 1};
// 外层循环控制轮数,arr.length - 1 防止数组越界
for (int i = 0; i < arr.length - 1; i++) {
    // 内层循环控制每轮比较次数,再 -i 是为了只处理还没排好序的部分
    for (int j = 0; j < arr.length - 1 - i; j++) {
        // 如果前一个比后一个大,交换
        if (arr[j] > arr[j + 1]) {
            int temp = arr[j];
            arr[j] = arr[j + 1];
            arr[j + 1] = temp;
        }
    }
}

这段代码每次都把当前未排序区间的最大值“推”到最后。输出结果就是一个升序排列的数组。

选择排序

每一轮从未排序区间中选出最小的元素,放到已排序区间的末尾。这样每轮都能确定一个最终位置的元素。

基本流程如下:

  1. 从头到尾遍历数组,假设当前位置 i 是最小值。
  2. 用一个内层循环,找出 i 之后最小的元素。
  3. 如果找到更小的,就和 i 位置交换。
  4. 重复上述过程,直到全部有序。

来看一个简化版的 Java 代码示例:

java
// 定义一个待排序数组
int[] arr = {5, 1, 3, 2};
// 外层循环控制轮数
for (int i = 0; i < arr.length - 1; i++) {
    int minIndex = i; // 记录本轮最小值的索引
    // 内层循环找出最小值,i + 1为了只在还没排好序的部分里找最小值
    for (int j = i + 1; j < arr.length; j++) {
        if (arr[j] < arr[minIndex]) {
            minIndex = j;
        }
    }
    // 只和最小值交换一次,减少不必要的交换
    if (minIndex != i) {
        int temp = arr[i];
        arr[i] = arr[minIndex];
        arr[minIndex] = temp;
    }
}

这种写法相比“每遇到更小的就交换”更高效,因为每轮只发生一次交换,避免了大量无意义的操作。

查找算法

查找是在一组数据中快速定位到目标元素。一般来说,查找操作通常发生在数据已经排好序之后,这样才能用更高效的查找方式。

基本/顺序查找

最直接的查找方式,就是顺序查找——从头到尾一个个比对,直到找到目标或者遍历结束。这种方法实现简单,但当数据量很大时,效率会非常低,最坏情况下需要遍历整个数组。

来看一个简单的伪代码示例:

java
// 顺序查找
int[] arr = {7, 23, 79, 81, 103, 127, 131, 147};
int target = 81;

for (int i = 0; i < arr.length; i++) {
    if (arr[i] == target) {
        // 找到了,返回下标
        return i;
    }
}
// 没找到,返回-1
return -1;

这种查找方式适合数据量小、或者数据本身无序的场景。

二分查找

当数据已经有序时,二分查找(折半查找)能大幅提升查找效率。它的核心思想是:每次都把查找区间一分为二,逐步缩小范围,直到找到目标或区间为空。

二分查找的流程如下:

  1. 设定头指针 left 和尾指针 right,初始分别指向数组两端。
  2. 计算中间位置 middle
  3. 比较目标值和中间元素:
    • 如果目标比中间值大,说明目标在右半区间,更新 left = middle + 1
    • 如果目标比中间值小,说明目标在左半区间,更新 right = middle - 1
    • 如果相等,直接返回中间下标。
  4. 重复上述过程,直到 left > right,说明查找结束。

来看一个标准的 Java 代码实现:

java
// 二分查找
public static int searchDataIndex(int[] array, int number) {
    int left = 0;
    int right = array.length - 1;
    while (left <= right) {
        int middle = (left + right) / 2;
        if (number > array[middle]) {
            left = middle + 1;
        } else if (number < array[middle]) {
            right = middle - 1;
        } else {
            return middle; // 找到目标,返回下标
        }
    }
    return -1; // 没找到
}

二分查找会遇到的极端情况:

  • 目标不存在:如果目标值不在数组中,最终  left  会大于  right,循环结束,返回  -1,表示没找到。
  • 数组为空:如果数组长度为 0,right  初始就是 -1,循环根本不会进入,直接返回  -1。
  • 溢出问题:在极大数组下,比如超过了 int 能表示的最大值的情况下,(left + right) / 2  可能会溢出。更安全的写法是  left + (right - left) / 2,但一般面试和日常开发中,普通写法就够用了。

二分查找的效率远高于顺序查找,尤其是在大数据量场景下,查找次数大大减少。

评论