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

一、搜索算法是什么?

想象你在图书馆找一本书。最简单的方法是从第一本书开始,一本本翻看(顺序搜索),但如果图书馆有几百万本书,这显然太慢了。聪明的图书馆会给书分类编号(如计算机类在 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

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

相关推荐
一只柠檬新2 小时前
Web和Android的渐变角度区别
android
志旭2 小时前
从0到 1实现BufferQueue GraphicBuffer fence HWC surfaceflinger
android
_一条咸鱼_2 小时前
Android Runtime堆内存架构设计(47)
android·面试·android jetpack
用户2018792831672 小时前
WMS(WindowManagerService的诞生
android
用户2018792831672 小时前
通俗易懂的讲解:Android窗口属性全解析
android
openinstall3 小时前
A/B测试如何借力openinstall实现用户价值深挖?
android·ios·html
二流小码农3 小时前
鸿蒙开发:资讯项目实战之项目初始化搭建
android·ios·harmonyos
志旭4 小时前
android15 vsync源码分析
android
志旭4 小时前
Android 14 HWUI 源码研究 View Canvas RenderThread ViewRootImpl skia
android
whysqwhw4 小时前
Egloo 高级用法
android