算法设计与分析之分治法

1. 分治法简介

分治法的就是将一个规模为N的问题分解为K个规模较小的子问题,这些子问题相互独立且与原问题性质相同。求出子问题的解,就可得到原问题的解。

1.1 基本思想

当我们求解某些问题时,由于这些问题要处理的数据相当多,或求解过程相当复杂,使得直接求解法在时间上相当长,或者根本无法直接求出。对于这类问题,我们往往先把它分解成几个子问题,找到求出这几个子问题的解法后,再找到合适的方法,把它们组合成求整个问题的解法。如果这些子问题还较大,难以解决,可以再把它们分成几个更小的子问题,以此类推,直至可以直接求出解为止。这就是分治策略的基本思想。一般情况下,还会用到二分法。

二分法:利用分治策略求解时,所需时间取决于分解后子问题的个数、子问题的规模大小等因素,而二分法,由于其划分的简单和均匀的特点,是经常采用的一种有效的方法,例如二分法检索。

1.2 解决步骤

分治法解题的一般步骤如下:

(1)分解,将要解决的问题划分成若干规模较小的同类问题;

(2)求解,当子问题划分得足够小时,用较简单的方法解决;

(3)合并,按原问题的要求,将子问题的解逐层合并构成原问题的解。

1.3 具体算法

{
    
    //开始
	if(1、问题不可分)
		2、返回问题解;
  	else
  	{
    
    
   		3、从原问题中划分出含一半运算对象的子问题1;
 		4、递归调用分治法过程,求出解1;
 		5、从原问题中划出含另一半运算对象的子问题2;
 		6、递归调用分治法过程,求出解2;
   		7、将解1、解2组合成整个问题的解; 
  	} 
}//结束

1.4 应用场景

运用分治策略解决的问题一般来说具有以下特点:

1、原问题可以分解为多个子问题。这些子问题与原问题相比,只是问题的规模有所降低,其结构和求解方法与原问题相同或相似。

2、原问题在分解过程中,递归地求解子问题。由于递归都必须有一个终止条件,因此,当分解后的子问题规模足够小时,应能够直接求解。

3、在求解并得到各个子问题的解后,应能够采用某种方式、方法合并或构造出原问题的解。

不难发现,在分治策略中,由于子问题与原问题在结构和解法上的相似性,用分治方法解决的问题,大都采用了递归的形式。在各种排序方法中,如归并排序、快速排序、堆排序等,都存在有分治的思想。另外,下列问题也可用分治策略解决:

Merge Two Sorted Lists(合并两个有序链表)- LeetCode 21
Merge k Sorted Lists(合并 K 个升序链表)- LeetCode 23
Top K 问题:前 K 个高频元素 - LeetCode 347

2. 二分查找

**问题描述:**给定 n 个元素,这些元素是有序的(假定为升序),从中查找特定元素 x。

**算法思想:**将有序序列分成规模大致相等的两部分,然后取中间元素与特定查找元素 x 进行比较,如果 x 等于中间元素,则查找成功,算法终止;如果 x 小于中间元素,则在序列的前半部分继续查找,即在序列的前半部分重复分解和治理操作;否则,在序列的后半部分继续查找,即在序列的后半部分重复分解和治理操作。

算法设计:

(1)设置查找区间:

int left; // C++自动初始化为0
int right = n;

(2)若查找区间 [left, right] 不存在,则查找失败,返回;否则执行(3)。

(3)取中间位 mid = (left + right)/2,比较 x 与 a[mid] ,有三种情况:

  • 若 x < a[mid],则right = mid - 1;查找在左半区间进行,转(2);

  • 若 x > a[mid],则left = mid + 1;查找在右半区间进行,转(2);

  • 若 x = a[mid],则查找成功,返回mid的值。

时间复杂度分析:

(1)顺序查找
- 最好时间复杂度:O(1)
- 最坏时间复杂度:O(n)
(2)二分查找
- 最好时间复杂度:O(1)
- 最坏时间复杂度:O(logn)

算法实现:

#include <iostream>
#include <vector>

int binarySearch(std::vector<int> nums, int l, int r, int x) {
    
    
    if (l <= r) {
    
    
        // 计算 mid 时需要防止溢出,left+(right-left)/2和(left+right)/2的结果相同,但是有效防止了left和right太大直接相加导致溢出
        int mid = l + (r-l)/2;
        if (nums[mid] == x) {
    
    
            return mid;
        } else if (nums[mid] > x) {
    
    
            return binarySearch(nums, l, mid - 1, x);
        } else {
    
    			 
            return binarySearch(nums, mid + 1, r, x);
        }
    } else {
    
    
        return -1;			
    } 
}

int main() {
    
    
    std::vector<int> nums{
    
    2, 3, 4, 10, 40};
    int x = 10;

    int ans = binarySearch(nums, 0, nums.size() - 1, x);
    if (ans == -1) {
    
    
        std::cout << "未找到该数!" << std::endl;
    } else {
    
    
	    std::cout << "该数的索引为:" << ans << std::endl;
    }

	return 0;
}

猜你喜欢

转载自blog.csdn.net/crossoverpptx/article/details/131454614