分块查找是一种基于分治思想的搜索算法,适用于顺序存储的有序数据。
它通过将数据分成若干块,再对这些块进行二分查找,从而提高查找效率。
🧩 分块查找的思路
-
数据分块:将数据划分为大小相等的块(一般为 √n 大小),并创建一个索引数组(称为块索引)来存储每个块的最大值。
-
定位块:首先在块索引数组中进行二分查找,找到目标值所在的块。
-
块内查找:然后在找到的块内进行线性查找或二分查找,确定目标值的位置。
🚀 代码实现(分块查找)
public class BlockSearchUnordered {
// 定义索引表类,包含块的最小值和最大值
static class Index {
int minValue; // 块中的最小值
int maxValue; // 块中的最大值
int startIndex; // 块的起始索引
Index(int minValue, int maxValue, int startIndex) {
this.minValue = minValue;
this.maxValue = maxValue;
this.startIndex = startIndex;
}
}
// 块查找函数
public static int blockSearch(int[] arr, int key, Index[] indexTable, int blockSize) {
if (arr == null || arr.length == 0 || indexTable == null) {
return -1;
}
// 第一步:在索引表中找到目标值可能的块
int blockIndex = -1;
for (int i = 0; i < indexTable.length; i++) {
// 检查 key 是否在当前块的范围内(minValue <= key <= maxValue)
if (key >= indexTable[i].minValue && key <= indexTable[i].maxValue) {
blockIndex = i;
break;
}
// 如果 key 超出了当前块的最大值,且后续块的最小值大于 key,则不在数组中
if (i < indexTable.length - 1 && key > indexTable[i].maxValue && key < indexTable[i + 1].minValue) {
return -1;
}
}
// 如果未找到合适的块(key 超出最大值)
if (blockIndex == -1) {
return -1;
}
// 第二步:在块内顺序查找
int start = indexTable[blockIndex].startIndex;
int end = Math.min(start + blockSize, arr.length); // 确保不越界
for (int i = start; i < end; i++) {
if (arr[i] == key) {
return i; // 找到目标值,返回索引
}
}
return -1; // 未找到
}
// 主函数测试
public static void main(String[] args) {
// 示例数组:块内无序,块间有序
int[] arr = {3, 1, 5, // 块 1: min=1, max=5
7, 9, 6, // 块 2: min=6, max=9
12, 10, 11}; // 块 3: min=10, max=12
int blockSize = 3; // 每块大小
// 创建索引表
int blockCount = (int) Math.ceil((double) arr.length / blockSize);
Index[] indexTable = new Index[blockCount];
for (int i = 0; i < blockCount; i++) {
int start = i * blockSize;
int end = Math.min(start + blockSize, arr.length);
int minValue = arr[start];
int maxValue = arr[start];
for (int j = start + 1; j < end; j++) {
if (arr[j] < minValue) minValue = arr[j];
if (arr[j] > maxValue) maxValue = arr[j];
}
indexTable[i] = new Index(minValue, maxValue, start);
}
// 测试查找
int key = 6; // 要查找的值
int result = blockSearch(arr, key, indexTable, blockSize);
if (result != -1) {
System.out.println("找到 " + key + " 在索引 " + result);
} else {
System.out.println(key + " 未找到");
}
// 打印索引表(调试用)
System.out.println("索引表:");
for (Index idx : indexTable) {
System.out.println("块范围: [" + idx.minValue + ", " + idx.maxValue + "], 起始索引: " + idx.startIndex);
}
}
}
⏳ 时间复杂度
-
时间复杂度:
-
binarySearch(blockIndex, target)
:O(log n)(对块索引数组进行二分查找) -
在块内进行线性查找:O(√n)(每块大小为 √n)
因此,整体时间复杂度为:O(log n + √n)。
-
-
空间复杂度:
-
O(√n) 用于存储块索引数组。
-
🎯 适用场景
✅ 顺序存储的有序数组
✅ 访问代价较高的情况(如磁盘存储)
✅ 数据量较大时,想通过分块减小搜索范围
🆚 与二分查找对比
二分查找 | 分块查找 | |
---|---|---|
查找方式 | 每次划分数组为两半 | 每次划分为更小的块 |
时间复杂度 | O(log n) | O(log n + √n) |
适用场景 | 普通有序数组 | 顺序存储的有序数组 |
优点 | 计算简单 | 数据存储在磁盘上时,减少了寻址次数 |
✨ 总结
-
分块查找比普通的二分查找多了分块的步骤,能更好地适应数据存储的特殊情况(如磁盘存储),但需要额外的空间用于存储块索引。
-
对于常规的内存中的数据,二分查找仍然是最优选择。