【二分搜索技术】“递归与分治策略”——《算法设计与分析(第五版)》


一、算法要求

  1. 设 a[0:n-1]是一个已排好序的数组。
    请改写二分搜索算法,使得当搜索元素 x 不在数组中时,返回小于 x 的最大元素的位置 i 和大于 x 的最小元素位置 j。
    当搜索元素在数组中 时,i 和 j 相同,均为 x 在数组中的位置。
  2. 设有 n 个不同的整数排好序后存放于 t[0:n-1]中,若存在一个下标 i,0≤i<n,使得 t[i]=i,设计一个有效的算法找到这个下标。
    要求算法在最坏的情况下的计算时间为 O(logn)。

1. 思路

  1. 问题描述:给定n个元素,这些元素是有序的(假定为升序),从中查找特定元素x。算法思想:将有序序列分成规模大致相等的两部分,然后取中间元素与特定查找元素x进行比较,如果x等于中间元素,则查找成功,算法终止;如果x小于中间元素,则在序列的前半部分继续查找,即在序列的前半部分重复分解和治理操作;否则,在序列的后半部分继续查找,即在序列的后半部分重复分解和治理操作。
  2. 算法设计:用一维数组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)合并:按原问题的要求,将子问题的解逐层合并构成原问题的解。

分治法就是将一个难以直接解决的大问题,分割成一些规模较小的相同问题,以便各个击破,分而治之。在分治算法中,各个子问题形式相同,解决的方法也一样,因此我们可以使用递归算法快速解决,递归是彰显分治法优势的利器。

文档供本人学习笔记使用,仅供参考。

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

NI'CE'XIAN

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值