一、算法要求
- 设 a[0:n-1]是一个已排好序的数组。
请改写二分搜索算法,使得当搜索元素 x 不在数组中时,返回小于 x 的最大元素的位置 i 和大于 x 的最小元素位置 j。
当搜索元素在数组中 时,i 和 j 相同,均为 x 在数组中的位置。 - 设有 n 个不同的整数排好序后存放于 t[0:n-1]中,若存在一个下标 i,0≤i<n,使得 t[i]=i,设计一个有效的算法找到这个下标。
要求算法在最坏的情况下的计算时间为 O(logn)。
1. 思路
- 问题描述:给定n个元素,这些元素是有序的(假定为升序),从中查找特定元素x。算法思想:将有序序列分成规模大致相等的两部分,然后取中间元素与特定查找元素x进行比较,如果x等于中间元素,则查找成功,算法终止;如果x小于中间元素,则在序列的前半部分继续查找,即在序列的前半部分重复分解和治理操作;否则,在序列的后半部分继续查找,即在序列的后半部分重复分解和治理操作。
- 算法设计:用一维数组S[]存储该有序序列,设变量 low和 high表示查找范围的下界和上界,middle表示查找范围的中间位置,x为特定的查找元素。
(1)初始化。令 low=0,即指向有序数组S[]的第一个元素;high=n-1,即指向有序数组ST]的最后一个元素。
(2)middle=(low+high)/2,即指示查找范围的中间元素。
(3)判定low≤high是否成立,如果成立,转第4步,否则,算法结束。
(4)判断x与S[middle]的关系。如果 x=S[middle],则搜索成功,算法结束;如果x>S[middle],则令low=middle+1;否则令 high=middle-1,转为第2步。
2. 示例
二、完整代码
1. 主文件
main.cpp:
// Project1_1: 二分搜索
#include"Improve1.h"
int main() {
Console();
char input;
cin >> input;
int orderedArray[] = { -5, 0, 2, 7, 12, 13, 26, 29, 34, 38, 46, 47, 59, 67, 68, 72 },
length = sizeof(orderedArray) / sizeof(orderedArray[0]),
result, right, left = 0;
switch (input) {
case '1':
cout << "The existing ordered array is as follows: \n";
for (int i = 0; i < length; i++) {
cout << setw(3) << orderedArray[i];
}
cout << "\nPlease enter the number you want to find: ";
cin >> result;
BinarySearch(orderedArray, length, result, right, left);
break;
case '2':
left = 0;
right = length - 1;
SearchTag(orderedArray, left, right);
break;
default:
cout << "!!Syntax error, please re-enter";
}
}
2. 头文件
Improve1.h:
#pragma once
#ifndef __IMPROVE1__
#define __IMPROVE1__
#include<iostream>
#include<iomanip>
using namespace std;
//a[]: 有序数组
//n : 有序数组总长度;
//x : 目标;
//i : 右标记;
//j : 左标记;
//size : size = 2^k, 棋盘规格为2^k×2^k.
bool BinarySearch(int a[], int n, int x, int& i, int& j) {
int left = 0;
int right = n - 1;
while (left <= right) {
int middle = (left + right) / 2;
if (x == a[middle]) { //直接判断出正确结果
i = j = middle;
//cout << "\nThe retrieved number is: " << a[middle] << endl;
cout << "\nThe sequence of it is(from 0) : " << i << endl;
return true;
}
if (x > a[middle])
left = middle + 1;
else
right = middle - 1;
}
i = left - 1;
j = right;
cout << "\nTarget is between " << i
<< " and " << j + 1<< " (from 0)" << endl;
return false;
}
void SearchTag(int a[], int left, int right) {
int middle = (left + right) / 2;
if (left < right) {
if (a[middle] == middle)
cout << "The sequence that satisfies the condition is: " << middle << endl;
else if (a[middle] > middle) {
SearchTag(a, left, middle - 1);
}
else SearchTag(a, middle + 1, right);
}
else {//将边界单独提出来处理,因为可能在把边界当做要找的坐标,可能并不满足条件
if (left == right) {
if (a[middle] == middle) {
cout << "The sequence that satisfies the condition is: " << middle << endl;
}
}
}
}
void Console() {
cout << '#' << setfill('-') << setw(40) << '#'
<< "\n#" << setw(39) << left << "---Please enter your options: 1 or 2" << '#'
<< "\n#" << setw(39) << left << "1 : Search for element x in the array" << '#'
<< "\n#" << setw(39) << left << "2 : Find this subscript that t[i]=i" << '#'
<< "\n#" << setw(40) << right << '#'
<< "\nYour choice :";
}
#endif
3. 效果展示
三、补充
分治法解题步骤:
(1)分解:将要解决的问题分解为若干个规模较小、相互独立、与原问题形式相同的子问题。
(2)治理:求解各个子问题。由于各个子问题与原问题形式相同,只是规模较小而已,而当子问题划分得足够小时,就可以用较简单的方法解决。
(3)合并:按原问题的要求,将子问题的解逐层合并构成原问题的解。
分治法就是将一个难以直接解决的大问题,分割成一些规模较小的相同问题,以便各个击破,分而治之。在分治算法中,各个子问题形式相同,解决的方法也一样,因此我们可以使用递归算法快速解决,递归是彰显分治法优势的利器。
文档供本人学习笔记使用,仅供参考。