算法——分治

  分治

分治是一种策略,是把一个较大较复杂的问题分解成多个独立的较小较简单的问题,然后再把这些小问题的结果组装成原始问题
的结果。把大问题分解成小问题,到底分解成多小的问题呢?一般来说,当问题分解成能直接解决就可以了。很明显,分治要一层层地
把大问题分解分更小的问题,所以分治经常使用递归来实现。分治就是把大问题分解成和原问题相同,但规模更小的问题,而递归恰好
有这种特性,递归就是自己调用自己(递归的实现主要还是要归功于汇编,因为汇编把代码也变成了数据,要递归,只需要把调用前的数据
入栈,再回到函数代码数据的初始地址即可)。
根据分治的特性,可以总结了分治的适用场景。分治用于那些可以把大问题分解成相同意义且独立的小问题的场景。这里的意义相同
是指问题的根源相同,比如说分别求10,15,7,2,35和2,8,1,4,25,16,34两个序列中的最小值,如果把两个序列合成一个大序列。求这个大
序列中的最小值。求大序列中的最小值和求小序列中的最小值,这两个问题其实是相同意义的问题。只是数据的规模不同而已。那么
继续分解,直到序列中只有两个数时,那么只需要把两个数比较一次就可以求出最小值了,再然后合并两个结果,只需要把两个数中更小
的那个当做最小值,这样把所有小问题合并,最后就得到了原来大序列中的最小值了。可以发现大序列和小序列求最小值其实是一回事,
所以叫做具有相同意义的问题。再者,把一个大序列分成两个小序列,这两个小序列分别求最小值其实是独立的,在一个小序列中求最小
值不需要用到另一个小序列中最小值的任意当前数据、中间数据和结果数据。只有满足了具有相同意义的子问题并且所有子问题独立的场景
才能使用分治的策略。
下面来分析一下分治策略的时间复杂度。由于分治策略每次都可以把一个大问题分解成多个小问题,然后再递归地进行分解,因此可以
发现分治策略的时间复杂度是基于对数的。对数是一种非常优秀的时间复杂度,因为对数函数的曲线图象可以看出,当x很大时,y值增长得
会越来越慢,这样图象的函数的时间复杂度对于大规模的数据处理非常友好。
下面举个分治最经典的例子——二分查找。二分查找的条件是查找的序列必须是完全有序的。比如说是增序的。
二分查找是每次都从序列的最中间元素开始找,如果相等,则找到,如果小于中间元素,则说明查找的值在中间元素的左边序列中,
则要从左边子序列中找,如果大于中间元素,则说明查找的值在右边子序列中,要从右边子序列中找。而从左边子序列或右边子序列中
找又是一个从一个有序序列中查找元素的问题,这个问题其实和原来的问题意义相同,因此可以递归进行。
下面是一个二分查找的代码,从一个增序的数组中查找某个值的下标,如果找到,则返回下标,如果没找到,则返回-1

#pragma once
#ifndef SEARCH_H
#define SEARCH_H
namespace algorithm 
{
	template<typename T>
	int BinarySearch(T data[],const T t,int left,int right)
	{
		if(left==right)
		{
			if (t == data[left]) 
			{
				return left;
			}
			else
			{
				return -1;
			}
		}
		else
		{
			int mid = (right-left) / 2 + left;//可以防止left+right撑爆int
			if(data[mid]==t)
			{
				return mid;
			}
			else if(data[mid]>t)
			{
				BinarySearch(data, t, left, mid - 1);
			}
			else
			{
				BinarySearch(data, t, mid + 1, right);
			}
		}
	}
}
#endif SEARCH_H

// Algorithm.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <iostream>
#include "search.h"
using namespace std;
using namespace algorithm;

int main()
{
	int data[10];
	for (int i = 0;i < 10;i++)
	{
		data[i] = i;
	}

	cout << BinarySearch(data, 3, 0, 9) << endl;
	system("pause");
    return 0;
}

这里是用递归来求解的,可以发现代码特别简单,递归就是有可以最简化代码的作用,但递归是有些耗费资源的,因此每次递归调用
时都相当于执行一个函数,需要把当前函数压入栈,需要保存当前执行执行位置,函数执行完之后还要回收内存和返回结果之类的。总
的来说递归比相同的非递归函数更消耗资源,但递归的优点就是代码简单,可读性和可理解性都比较高。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值