搜索算法故事:从图书馆找书到迷宫探险

一、搜索算法是什么?

想象你在图书馆找一本书。最简单的方法是从第一本书开始,一本本翻看(顺序搜索),但如果图书馆有几百万本书,这显然太慢了。聪明的图书馆会给书分类编号(如计算机类在 3 楼 A 区),你可以直接去对应区域找(二分搜索),效率就大大提高了。

搜索算法的本质

在一堆数据中找到目标值的位置或判断其是否存在。不同的搜索算法就像不同的 "找东西" 策略,适用于不同的场景。

二、顺序搜索:逐本翻书的新手

故事

小明第一次去图书馆,不知道书的分类,只能从第一本书开始,逐本翻看,直到找到《Java 编程从入门到精通》或翻完全部书架。

代码实现

java

arduino 复制代码
public class SequentialSearch {
    // 在数组中查找目标值,返回索引;未找到返回-1
    public static int search(int[] array, int target) {
        for (int i = 0; i < array.length; i++) {
            if (array[i] == target) {
                return i;  // 找到目标,返回位置
            }
        }
        return -1;  // 未找到
    }
    
    public static void main(String[] args) {
        int[] bookshelf = {12, 34, 56, 78, 90, 23, 45};
        int targetBook = 78;
        int position = search(bookshelf, targetBook);
        
        if (position != -1) {
            System.out.println("找到书啦!在第 " + position + " 个位置");
        } else {
            System.out.println("没找到这本书...");
        }
    }
}

特点

  • 简单直接:适用于任何数据(有序或无序)
  • 效率低:最坏情况下需要遍历整个数组(时间复杂度 O (n))
  • 适用场景:数据量小、无序或未排序的数据

三、二分搜索:会用目录的聪明人

故事

小明第二次去图书馆,学会了先查目录(假设书按编号从小到大排列)。他先看中间的书,如果编号比目标小,就去后半部分找;如果比目标大,就去前半部分找。每次缩小一半范围,直到找到目标。

代码实现

java

arduino 复制代码
public class BinarySearch {
    // 在有序数组中二分查找目标值
    public static int search(int[] sortedArray, int target) {
        int left = 0;
        int right = sortedArray.length - 1;
        
        while (left <= right) {
            int mid = left + (right - left) / 2;  // 计算中间位置
            
            if (sortedArray[mid] == target) {
                return mid;  // 找到目标
            } else if (sortedArray[mid] < target) {
                left = mid + 1;  // 目标在右半部分
            } else {
                right = mid - 1;  // 目标在左半部分
            }
        }
        
        return -1;  // 未找到
    }
    
    public static void main(String[] args) {
        int[] sortedBookshelf = {10, 20, 30, 40, 50, 60, 70};
        int targetBook = 50;
        int position = search(sortedBookshelf, targetBook);
        
        System.out.println("找到书啦!在第 " + position + " 个位置");
    }
}

特点

  • 要求有序:必须先对数据排序
  • 效率高:每次排除一半数据,时间复杂度 O (log n)
  • 适用场景:大规模有序数据(如字典、索引表)

四、哈希搜索:用 ISBN 快速定位

故事

图书馆引入了计算机系统,每本书都有唯一的 ISBN 编号。小明只需在系统中输入 ISBN,系统通过 "魔法公式"(哈希函数)直接计算出这本书在哪个书架的哪个位置,无需翻找。

代码实现

java

arduino 复制代码
import java.util.HashMap;

public class HashSearch {
    public static void main(String[] args) {
        // 创建一个哈希表,键是ISBN,值是书架位置
        HashMap<String, Integer> librarySystem = new HashMap<>();
        
        // 添加书籍信息
        librarySystem.put("ISBN978-7-121-15535-2", 5);  // 计算机类在5号书架
        librarySystem.put("ISBN978-7-302-25708-4", 12); // 数学类在12号书架
        librarySystem.put("ISBN978-7-111-40701-0", 3);  // 物理类在3号书架
        
        // 查找目标书
        String targetISBN = "ISBN978-7-302-25708-4";
        if (librarySystem.containsKey(targetISBN)) {
            int position = librarySystem.get(targetISBN);
            System.out.println("找到书啦!在第 " + position + " 号书架");
        } else {
            System.out.println("图书馆没有这本书...");
        }
    }
}

特点

  • 极快:平均时间复杂度 O (1),几乎瞬间找到
  • 空间换时间:需要额外空间存储哈希表
  • 适用场景:高频查找、数据量极大(如数据库索引、缓存系统)

五、广度优先搜索(BFS):逐层探索的迷宫

故事

小明在迷宫中找出口,他先尝试所有可能的第一步(上、下、左、右),标记走过的路;如果没找到出口,再尝试所有可能的第二步(从每个第一步的终点出发),以此类推,直到找到出口或遍历完所有路径。

代码实现(简化版)

java

ini 复制代码
import java.util.LinkedList;
import java.util.Queue;

public class BFSMazeSearch {
    public static void main(String[] args) {
        // 0表示通路,1表示墙
        int[][] maze = {
            {0, 1, 0, 0, 0},
            {0, 1, 0, 1, 0},
            {0, 0, 0, 0, 0},
            {0, 1, 1, 1, 0},
            {0, 0, 0, 1, 0}
        };
        
        int startX = 0, startY = 0;  // 起点
        int endX = 4, endY = 4;      // 终点
        
        // 使用队列存储待探索的位置
        Queue<int[]> queue = new LinkedList<>();
        boolean[][] visited = new boolean[maze.length][maze[0].length];
        
        queue.offer(new int[]{startX, startY});
        visited[startX][startY] = true;
        
        int[][] directions = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};  // 上、下、左、右
        
        boolean found = false;
        
        while (!queue.isEmpty()) {
            int[] current = queue.poll();
            int x = current[0];
            int y = current[1];
            
            // 找到出口
            if (x == endX && y == endY) {
                found = true;
                System.out.println("找到出口啦!");
                break;
            }
            
            // 尝试四个方向
            for (int[] dir : directions) {
                int newX = x + dir[0];
                int newY = y + dir[1];
                
                // 检查是否可以走
                if (newX >= 0 && newX < maze.length && newY >= 0 && newY < maze[0].length 
                    && maze[newX][newY] == 0 && !visited[newX][newY]) {
                    queue.offer(new int[]{newX, newY});
                    visited[newX][newY] = true;
                }
            }
        }
        
        if (!found) {
            System.out.println("没找到出口...");
        }
    }
}

特点

  • 逐层探索:适合找最短路径(如迷宫、社交网络关系)
  • 用队列实现:先进先出(FIFO)
  • 空间需求大:最坏情况下需要存储所有节点

六、深度优先搜索(DFS):一条路走到黑的探险

故事

小明在迷宫中找出口,他选择一个方向一直走,直到走不通再返回上一个岔路口,尝试另一个方向。比如先一直向右走,遇到墙再返回,尝试向上走,以此类推,直到找到出口或遍历所有路径。

代码实现(递归版)

java

ini 复制代码
public class DFSMazeSearch {
    private static boolean found = false;  // 标记是否找到出口
    
    public static void main(String[] args) {
        int[][] maze = {
            {0, 1, 0, 0, 0},
            {0, 1, 0, 1, 0},
            {0, 0, 0, 0, 0},
            {0, 1, 1, 1, 0},
            {0, 0, 0, 1, 0}
        };
        
        int startX = 0, startY = 0;
        int endX = 4, endY = 4;
        boolean[][] visited = new boolean[maze.length][maze[0].length];
        
        dfs(maze, startX, startY, endX, endY, visited);
        
        if (found) {
            System.out.println("找到出口啦!");
        } else {
            System.out.println("没找到出口...");
        }
    }
    
    private static void dfs(int[][] maze, int x, int y, int endX, int endY, boolean[][] visited) {
        // 检查是否越界、是墙或已访问
        if (x < 0 || x >= maze.length || y < 0 || y >= maze[0].length 
            || maze[x][y] == 1 || visited[x][y]) {
            return;
        }
        
        // 标记为已访问
        visited[x][y] = true;
        
        // 找到出口
        if (x == endX && y == endY) {
            found = true;
            return;
        }
        
        // 尝试四个方向(上、下、左、右)
        dfs(maze, x - 1, y, endX, endY, visited);  // 上
        if (found) return;  // 找到出口,提前返回
        
        dfs(maze, x + 1, y, endX, endY, visited);  // 下
        if (found) return;
        
        dfs(maze, x, y - 1, endX, endY, visited);  // 左
        if (found) return;
        
        dfs(maze, x, y + 1, endX, endY, visited);  // 右
    }
}

特点

  • 一条路走到黑:适合探索所有可能路径(如游戏、回溯算法)
  • 用递归或栈实现:后进先出(LIFO)
  • 空间需求小:只需存储当前路径

七、总结:搜索算法的选择指南

算法 适用场景 时间复杂度 核心思想
顺序搜索 无序数据、小规模 O(n) 逐个比较,简单直接
二分搜索 有序数据、大规模 O(log n) 每次排除一半数据
哈希搜索 高频查找、数据量大 O(1) 哈希函数直接映射位置
BFS(广度优先) 最短路径、分层数据(如社交网络) O(V+E) 逐层探索,用队列实现
DFS(深度优先) 所有路径探索、游戏回溯 O(V+E) 一条路走到黑,用递归或栈实现

记忆口诀

  • 数据无序用顺序

  • 数据有序用二分

  • 高频查找用哈希

  • 最短路径用 BFS

  • 路径探索用 DFS

掌握这些搜索算法,就像学会了不同的 "找东西" 技巧,可以根据实际场景选择最高效的方法。这是算法学习的基础,也是解决复杂问题的关键。

相关推荐
StackNoOverflow11 小时前
MySQL Explain 返回列详解:从入门到实战,附 SQL 与避坑大全
android
CYRUS_STUDIO20 小时前
Frida 检测与对抗实战:进程、maps、线程、符号全特征清除
android·逆向
csj5021 小时前
安卓基础之《(28)—Service组件》
android
lhbian1 天前
PHP、C++和C语言对比:哪个更适合你?
android·数据库·spring boot·mysql·kafka
catoop1 天前
Android 最佳实践、分层架构与全流程解析(2025)
android
ZHANG13HAO1 天前
Android 13 特权应用(Android Studio 开发)调用 AOSP 隐藏 API 完整教程
android·ide·android studio
田梓燊1 天前
leetcode 142
android·java·leetcode
angerdream1 天前
Android手把手编写儿童手机远程监控App之JAVA基础
android
菠萝地亚狂想曲1 天前
Zephyr_01, environment
android·java·javascript
sTone873751 天前
跨端框架通信机制全解析:从 URL Schema 到 JSI 到 Platform Channel
android·前端