算法导论_第四章_分治策略及部分经典例题


一、分治策略的基本想法

  在分治策略中,我们递归的求解一个问题,在每层递归中应用如下三个步骤:
  分解步骤将问题划分为一些子问题,子问题的形式与原问题一样,只是规模更小。
  解决步骤递归地解出子问题。如果子问题地规模足够小,则停止递归,直接求解。
  合并步骤将子问题的解组合成原问题的解。
在这里插入图片描述

  上述是《算法导论》中一书对分治策略步骤的归纳,怎样?是不是感觉云里雾里,不知所然。其实字面意思很好理解无非就是先分,分到足够小然后解小问题再合并,但是在实际应用中,怎么分?怎么判断最小?怎么合并?都是此算法的难点。对于这些较为抽象的算法描述,我的一贯做法是通过实例去加以解读。下面给出几个利用分治策略的典型例子,从实例中真正体会分治法的妙用。
  此外补充一个小点:当我们想到分治法时很自然的就想到了递归,这是没有问题的,分治法的思想本身也是源于递归,但是需要知道的是迭代法也可以实现分治法。

二、典型例题

1.二分检索

  问题:在有序表中(以经按照关键字非减排序),搜索给定元素。在此处我们仅考虑数组,即查找有序数组中的某一元素并返回下标。
  按照常规想法是从数组的第一个元素开始顺序查找,查到到给定元素就停止,并返回下标。此处我们采用分治法,首先将大规模的数组划分为小规模,划分到只有一个元素为最小规模,这个时候就开始解决问题,然后合并。代码如下:

#include<iostream>
#include<vector>
using namespace std;
int BinarySearch(vector<int>& A, int low, int high,int setvalue);   //二分查找函数

int main()
{
	vector<int> test_array = {1,3,5,8,9};
	cout << "测试数组中5的小标为:" << BinarySearch(test_array, 0, 4, 5)<<endl;
	return 0;
}

/*======================================================================
* 函数功能:二分查找元素,返回下边
* 形参意义:A;查找数组,low:左最小下标,high:右最大下边,setvalue:查找值
* 注意:分治法划分规模,一般划分下边即可,[low,high]为一个更小规模
========================================================================*/

int BinarySearch(vector<int>& A, int low, int high,int setvalue)
{
	int mid = (low + high) / 2;
	if (A[mid] == setvalue)
	{
		return mid;
	}
	else if (A[mid] > setvalue && low != high)
	{
		return BinarySearch(A, low, mid, setvalue);   //中值大于查找值,接下来向左[low,mid]查找
	}
	else if (A[mid] < setvalue && low != high)
	{
		return BinarySearch(A, mid + 1,high, setvalue);   //中值小于查找值,接下来向[mid+1,high]查找
	}
	else {
		return -1;   //当low==high时,并且A[mid]!=stevalue返回-1,说明查找失败,数组中没有这个值
	}
}

  结果如下:
在这里插入图片描述

2.二分归并排序

  把两个有序的序列排成一个有序序列。根据下图我对归并排序做一个解读。
在这里插入图片描述
首先是递归法的基本想法,把整体划分为小规模问题,即划分数组,以下是对上述实例套用下面代码的一个过程介绍,蓝色数字标注的就是代码实际运行中执行步骤顺序,你可以对照BinarySearch函数自己脑动跑一遍就行。
在这里插入图片描述

#include<iostream>
#include<vector>
using namespace std;
void BinaryMergeSort(int A[], int low, int high);
void Merge_guo(int A[], int low, int mid, int high);
 
int main()
{
	int testdata[9]= { 2,4,6,19,1,7,22,34,7 };
	BinaryMergeSort(testdata, 0, 8);
	for (int i = 0; i < 9; i++)
	{
		cout << "第" << i << "个数:" << testdata[i]<<endl;
	}
	return  0;
}

void BinaryMergeSort(int A[], int low, int high)
{
	int mid = 0;
	if (low < high)
	{
		mid = (low + high) / 2;
		BinaryMergeSort(A, low, mid);
		BinaryMergeSort(A, mid + 1, high);
		Merge_guo(A, low, mid, high);
	}
}


void Merge_guo(int A[], int low, int mid, int high)
{
	int n1 = mid - low + 1;
	int n2 = high - mid;
	vector<int> L(n1+1);   
	vector<int> R(n2+1);
	for (int i = 0; i < n1; i++)
	{
		L[i] = A[low + i];
	}
	for (int j = 0; j < n2; j++)
	{
		R[j] = A[mid + 1 + j];
	}
	L[n1] = INT_MAX;      
	R[n2] = INT_MAX;
	int i = 0;
	int j = 0;
	int z = 0;
	while (z<(high-low+1))
	{
		if (L[i] <= R[j])
		{
			A[z+low] = L[i];
			i++;
			z++;
		}
		else {
			A[z+low] = R[j];
			j++;
			z++;
		}
	}
}

3.最大子段和

  问题描述如下:
在这里插入图片描述

#include<iostream>
#include<vector>
using namespace std;

int findsubarray(vector<int>& nums ,int low,int mid,int high);
int findMaxsubarray(vector<int>& nums, int low, int high);

int main()
{
	vector<int> A = { 13,-3,-25,20,-3,-16,-23,18,20,-7,12,-5,-22,15,-4,7 };
	int output=findMaxsubarray(A, 0, 15);
	cout << output << endl;
	return 0;
}


int findsubarray(vector<int>& nums, int low, int mid, int high)
{
    int left_sum = INT_MIN;
    int sum = 0;
    int left = 0;
    for (int i = mid; i >= low; i--)
    {
        sum = sum + nums[i];
        if (sum > left_sum)
        {
            left_sum = sum;
            left = i;
        }
    }

    int right_sum = INT_MIN;
    sum = 0;
    int right;
    for (int j = mid + 1; j <= high; j++)
    {
        sum = sum + nums[j];
        if (sum > right_sum)
        {
            right_sum = sum;
            right = j;
        }
    }

    return left_sum + right_sum;
}

int findMaxsubarray(vector<int>& nums, int low, int high)
{

    if (low >= high)
    {
        return nums[low];
    }
    else
    {
        int mid = (low + high) / 2;
        int left_sum = findMaxsubarray(nums, low, mid);
        int right_sum = findMaxsubarray(nums, mid + 1, high);
        int across_sum = findsubarray(nums, low, mid, high);
        if (left_sum >= right_sum && left_sum > +across_sum)
        {
            return left_sum;
        }
        else if (right_sum >= left_sum && right_sum >= across_sum)
        {
            return right_sum;
        }
        else {
            return across_sum;
        }
    }
}

三、总结

在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值