Skip to main content

两种查找算法:顺序查找 vs 二分查找(一看就懂)

一、查找是什么?

查找就是在一堆数据中找特定的东西

比如:

  • 在电话本中找某人的号码
  • 在字典中找某个字
  • 在成绩单中找自己的成绩

形象比喻:挨家挨户找人

就像警察找人,从第一户开始敲门问:"请问XX在家吗?"

查找过程动画(脑补):

数组:[5, 2, 8, 1, 9]
要找:8

第1步:检查第1个元素5,不是8
第2步:检查第2个元素2,不是8
第3步:检查第3个元素8,找到了!

C#代码(带详细注释):

using System;

class SequentialSearchExample
{
static void Main()
{
// 待查找的数组
int[] numbers = { 5, 2, 8, 1, 9 };
int target = 8; // 要找的数字

Console.WriteLine($"在数组 [{string.Join(", ", numbers)}] 中查找 {target}");
Console.WriteLine("使用顺序查找(一个一个找)\n");

bool found = false; // 是否找到
int position = -1; // 找到的位置(-1表示没找到)

// 顺序查找:从头开始,一个一个检查
for (int i = 0; i < numbers.Length; i++)
{
Console.WriteLine($"检查第{i+1}个元素:{numbers[i]}");

if (numbers[i] == target)
{
found = true;
position = i; // 记录位置
Console.WriteLine($"✓ 找到了!在第{i+1}个位置");
break; // 找到了就停止
}
else
{
Console.WriteLine($" 不是{target},继续找...");
}
}

if (!found)
{
Console.WriteLine($"✗ 没找到 {target}");
}

Console.WriteLine($"\n查找结果:{(found ? "找到" : "没找到")},位置:{position}");
}
}

输出结果:

在数组 [5, 2, 8, 1, 9] 中查找 8
使用顺序查找(一个一个找)

检查第1个元素:5
不是8,继续找...
检查第2个元素:2
不是8,继续找...
检查第3个元素:8
✓ 找到了!在第3个位置

查找结果:找到,位置:2

顺序查找特点:

  • 优点:简单,对数据没有要求(数据可以乱序)
  • 缺点:慢,如果数据有10000个,最坏要查10000次
  • 时间复杂度:O(n)

重要前提:数据必须已排序!

形象比喻:猜数字游戏

你心里想一个1-100的数字,别人猜:

  • 猜50,你说"太大了"
  • 猜25,你说"太小了"
  • 每次排除一半的可能性

查找过程动画(脑补):

有序数组:[1, 2, 5, 8, 9]
要找:8

第1步:数组中间是5(位置2),8比5大 → 去右边找 [8, 9]
第2步:右边部分中间是8(位置3),找到了!

C#代码(带详细注释):

using System;

class BinarySearchExample
{
static void Main()
{
// 注意:二分查找要求数组必须已排序!
int[] numbers = { 1, 2, 5, 8, 9 }; // 已经排好序
int target = 8; // 要找的数字

Console.WriteLine($"在有序数组 [{string.Join(", ", numbers)}] 中查找 {target}");
Console.WriteLine("使用二分查找(每次排除一半)\n");

// 二分查找的三个关键指针
int left = 0; // 查找范围的左边界
int right = numbers.Length - 1; // 查找范围的右边界
bool found = false; // 是否找到
int position = -1; // 找到的位置
int step = 1; // 记录第几步

while (left <= right) // 只要查找范围有效
{
// 计算中间位置
int mid = (left + right) / 2;
int midValue = numbers[mid];

Console.WriteLine($"第{step}步:");
Console.WriteLine($" 查找范围:下标{left}到{right} → [{string.Join(", ", numbers[left..(right+1)])}]");
Console.WriteLine($" 中间位置:下标{mid},值是{midValue}");

if (midValue == target)
{
// 找到了!
found = true;
position = mid;
Console.WriteLine($" ✓ 找到了!在位置{mid}");
break;
}
else if (midValue < target)
{
// 目标比中间值大,去右边找
Console.WriteLine($" {target} > {midValue},去右边找");
left = mid + 1;
}
else
{
// 目标比中间值小,去左边找
Console.WriteLine($" {target} < {midValue},去左边找");
right = mid - 1;
}

step++;
Console.WriteLine();
}

if (!found)
{
Console.WriteLine($"✗ 没找到 {target}");
}

Console.WriteLine($"\n查找结果:{(found ? "找到" : "没找到")},位置:{position}");
Console.WriteLine($"总共查找了{step-1}次");

// 对比:如果是顺序查找这个数组
Console.WriteLine("\n【对比】如果是顺序查找:");
for (int i = 0; i < numbers.Length; i++)
{
if (numbers[i] == target)
{
Console.WriteLine($"需要查找{i+1}次才能找到");
break;
}
}
}
}

输出结果:

在有序数组 [1, 2, 5, 8, 9] 中查找 8
使用二分查找(每次排除一半)

第1步:
查找范围:下标0到4 → [1, 2, 5, 8, 9]
中间位置:下标2,值是5
8 > 5,去右边找

第2步:
查找范围:下标3到4 → [8, 9]
中间位置:下标3,值是8
✓ 找到了!在位置3

查找结果:找到,位置:3
总共查找了2次

【对比】如果是顺序查找:需要查找4次才能找到

二分查找特点:

  • 优点:非常快!100万数据最多查20次
  • 缺点:要求数据必须已排序
  • 时间复杂度:O(log n)

四、两种查找算法对比

特性顺序查找二分查找
数据要求任意顺序必须已排序
查找方式从头到尾一个一个找每次排除一半
最坏情况找n次找log₂n次
1000个数据最多找1000次最多找10次
100万个数据最多找100万次最多找20次
代码难度简单稍复杂
适用场景数据少、不常查找数据多、经常查找

五、重要概念:时间复杂度

查找次数的增长对比:

数据量(n)   顺序查找(最多)   二分查找(最多)
----------------------------------------
10 10次 4次
100 100次 7次
1000 1000次 10次
10000 10000次 14次
100000 100000次 17次
1000000 1000000次 20次

二分查找的速度优势随着数据量增大而明显!


六、练习题

练习题1:顺序查找代码填空

题目:补全顺序查找代码中的空白

int SequentialSearch(int[] array, int target)
{
for (int i = 0; i < ______; i++) // 填空1
{
if (array[i] == ______) // 填空2
{
return ______; // 填空3:找到了,返回位置
}
}
return ______; // 填空4:没找到,返回-1
}

答案

// 填空1:array.Length
// 填空2:target
// 填空3:i
// 填空4:-1

练习题2:二分查找逻辑判断

题目:在有序数组 [2, 4, 6, 8, 10, 12, 14, 16, 18, 20] 中查找数字 14,回答以下问题:

  1. 第一次比较的中间位置是哪个数?
  2. 需要几次比较才能找到?
  3. 如果查找数字 5,需要几次比较才能确定不存在?

手动查找过程

数组:[2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
找:14

第1次:中间是下标4(10),14>10 → 去右边 [12,14,16,18,20]
第2次:右边中间是下标7(16),14<16 → 去左边 [12,14]
第3次:左边中间是下标5(12),14>12 → 去右边 [14]
第4次:找到14

答案:
1. 第一次比较的数:10
2. 找到14需要:4次比较
3. 找5的过程:
第1次:中间10,5<10 → 去左边 [2,4,6,8]
第2次:中间6,5<6 → 去左边 [2,4]
第3次:中间4,5>4 → 去右边 [](右边为空)
总共:3次比较确定不存在

七、排序算法练习题(巩固算法思路)

排序练习题1:理解冒泡排序过程

题目:观察下面的冒泡排序过程,回答后面的问题

// 对数组 [7, 3, 9, 2, 5] 进行冒泡排序
// 请写出每一轮的结果

初始:[7, 3, 9, 2, 5]

第1轮(比较相邻,大的后移):
7和3比较 → 交换 → [3, 7, 9, 2, 5]
7和9比较 → 不交换
9和2比较 → 交换 → [3, 7, 2, 9, 5]
9和5比较 → 交换 → [3, 7, 2, 5, 9]

第2轮:
3和7比较 → 不交换
7和2比较 → 交换 → [3, 2, 7, 5, 9]
7和5比较 → 交换 → [3, 2, 5, 7, 9]
(9已排好,不比较)

问题:
1. 第3轮开始时数组是什么?
2. 第3轮需要比较几次?
3. 总共需要多少轮才能完全排好?

答案:
1. 第3轮开始:[3, 2, 5, 7, 9]
2. 第3轮比较:2次(3和2,2和5)
3. 总共需要:4轮(n-1轮,n=5)

排序练习题2:手动执行选择排序

题目:手动对数组 [8, 3, 6, 1, 4] 执行选择排序,写出每一步

要求:写出每一轮找到的最小值和交换过程

初始:[8, 3, 6, 1, 4]

第1轮:
找最小值:从全部中找,最小值是1(位置3)
交换:8和1交换 → [1, 3, 6, 8, 4]

第2轮:
找最小值:从[3,6,8,4]中找,最小值是3(位置1)
交换:3已在正确位置,不交换 → [1, 3, 6, 8, 4]

第3轮:
找最小值:从[6,8,4]中找,最小值是4(位置4)
交换:6和4交换 → [1, 3, 4, 8, 6]

第4轮:
找最小值:从[8,6]中找,最小值是6(位置4)
交换:8和6交换 → [1, 3, 4, 6, 8]

完成:[1, 3, 4, 6, 8]

八、综合应用题

应用题:学生成绩查找系统

场景:有一个班级的学生成绩,需要实现查找功能

using System;

class StudentGradeSystem
{
static void Main()
{
// 学生成绩(无序)
int[] grades = { 85, 42, 90, 58, 76, 95, 33 };

Console.WriteLine("学生成绩系统");
Console.WriteLine("成绩列表:" + string.Join(", ", grades));

while (true)
{
Console.Write("\n请输入要查找的成绩(输入-1退出):");
string input = Console.ReadLine();

if (!int.TryParse(input, out int target) || target == -1)
break;

Console.WriteLine("\n=== 方法1:顺序查找(无序时用)===");
SequentialSearchDemo(grades, target);

Console.WriteLine("\n=== 方法2:二分查找(需要先排序)===");
BinarySearchDemo(grades, target);
}
}

static void SequentialSearchDemo(int[] array, int target)
{
int count = 0; // 查找次数

for (int i = 0; i < array.Length; i++)
{
count++;
if (array[i] == target)
{
Console.WriteLine($"找到了!位置:{i},查找了{count}次");
return;
}
}

Console.WriteLine($"没找到!查找了{count}次");
}

static void BinarySearchDemo(int[] array, int target)
{
// 二分查找需要先排序
int[] sortedArray = new int[array.Length];
Array.Copy(array, sortedArray, array.Length);
Array.Sort(sortedArray);

Console.WriteLine("先排序数组:" + string.Join(", ", sortedArray));

int left = 0;
int right = sortedArray.Length - 1;
int count = 0;

while (left <= right)
{
count++;
int mid = (left + right) / 2;

if (sortedArray[mid] == target)
{
Console.WriteLine($"找到了!在排序后位置:{mid},查找了{count}次");
Console.WriteLine("(注意:这是排序后的位置,不是原位置)");
return;
}
else if (sortedArray[mid] < target)
{
left = mid + 1;
}
else
{
right = mid - 1;
}
}

Console.WriteLine($"没找到!查找了{count}次");
}
}

九、学习建议

  1. 先理解思想,再写代码:明白"为什么"比记住"怎么写"更重要
  2. 画图辅助理解:在纸上画数组,模拟查找过程
  3. 对比学习:两种查找算法对比着学
  4. 注意前提条件:二分查找必须数据有序!
  5. 思考应用场景:什么时候用顺序查找?什么时候用二分查找?

十、一句话总结

顺序查找:简单但慢,适合数据少或无序数据
二分查找:快但需要先排序,适合数据多且经常查找

关键区别:二分查找每次排除一半,效率远高于顺序查找!