LeetCode分类算法笔记1——分治

算法学习笔记——LeetCode分类算法

1.分而治之

1.主要思想

对一个大规模的问题运用分治策略,就是将这个大规模的问题划分成若干小规模的子问题,然后对这这些子问题进行求解,最后按划分将求得的结果进行合并,就得到了原问题的解。分治思想就是将分解,即将大问题分解为若干小问题,然后各个击破。

利用分治算法求解问题的步骤可以如下表示:

1.分:将大问题分解成众多小问题;

2.治:求解各个小问题;

3.合:将解决的问题合并

【注】分治是一种策略,如果一个大问题进行一次分解后仍然无法轻易解出,则可继续对其子问题进行划分,直至子问题解出。

分治过程可以用树状图表示如下:

分而治之

 

2.分治策略的适用情况

  1. 原问题的计算复杂度随问题规模的增加而增加
  2. 原问题能够被分解为更小的子问题
  3. 子问题的结构和性质与原问题一样,并且相互独立,子问题之间不包含公共的子子问题(子问题的子问题)
  4. 原问题分解出的子问题可以合并为该问题的解

3.分治策略的应用

1)逆序对个数

相关概念:

逆序对:一个序列(a1,a2,…,an),设有整数i、j,0 < i < j <n+1,且ai > aj,则(ai,aj)为一个逆序对。

问题描述:求序列(a1,a2,…,an)中逆序对个数。

解题思路:

1.暴力破解

选取序列最左端的元素,比较其与其后各元素的大小,但凡有比其小者将计数器加1,比完之后再对第二个元素与其后各元素进行比较,依次反复进行,直至全部找出。

2.分而治之

将序列分为左右两个子序列A、B,a、b分别为A、B中的元素,假设A中a左侧元素全部小于a,右侧全部大于a,B中b左侧元素全部小于b,右侧全部大于b,若a>b,则(a,b)为一个逆序对,同时对于B中b左侧的所有元素b1、b2、……,(a,b1)、(a,b2)……也都为逆序对。

直观描述如下图所示:

求逆序对

 

伪代码描述:

 

2)众数

问题描述:

给定一个大小为 n 的数组,找到其中的众数。众数是指在数组中出现次数大于 [n/2] 的元素。(此处假设数组是非空的,并且给定的数组总是存在众数。)

解题思路:

将数组从中间分为左右两个子数组,求左右两个子数组的众数,比较左右两数组的众数是否相同,相同则返回此众数;若不相同,则比较左右各众数在数组中出现的次数,返回次数多者。

此问题解题思路的直观描述如下:

【注】图中b1b2以及蓝色元素为数组中的众数

求众数

python实现:

def Mode(nums):
    '''nums为一个实数序列'''
    if len(nums) == 0:
        return None;
    if len(nums) == 1:
        return nums[0];
    # 将序列划分为左右两部分
    left = Mode(nums[:len(nums) // 2])
    right = Mode(nums[len(nums) // 2:])
    # 判断左右两部分的众数
    if left == right:
        return left
    if nums.count(left) > nums.count(right):
        return left
    else:
        return right

C++实现:

header.h文件

/*定义数组类*/
class Array {
protected:
	double *A, *leftArray, *rightArray, left, right;
	int length;
public:
	Array(int size) {
		if (size <= 0)
			cerr << "数组长度不能为负,数组创建失败!";
		A = new double[size];
		length = size;
	}
	~Array() {
		delete[]A;
	}
	Array(int size, double &B) {
		*A = B;
		length = size;
	}
	double Mode();    //求众数函数
	int count(double a);    //求数组元素个数
};

Mode.cpp文件

// 数组中与a相同的元素个数
int Array::count(double a) {
	int count = 0;
	for (int i = 0; i < length; i++) {
		if (A[i] == a)
			count ++;
	}
	return count;
}
// 求众数
double Array::Mode() {
	if (length == 1)
		return A[0];
	//拆分问题
	leftArray = new double[length / 2];
	rightArray = new double[length - length / 2];
	for (int i = 0; i < length; i++) {
		if (i < length / 2)
			leftArray[i] = A[i];
		else
			rightArray[i - length / 2] = A[i];
	}
	Array *Left = new Array(length / 2, *leftArray);
	Array *Right = new Array(length - length / 2, *rightArray);
	left = Left->Mode();    //递归
	right = Right->Mode();
	//返回数量多的众数
	if (left == right)
		return left;
	if (Left->count(left) > Right->count(right)) {
		Left->~Array();
		Right->~Array();
		return left;
	}
	else {
		Left->~Array();
		Right->~Array();
		return right;
	}
}

3)最大子序和

问题描述:

给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

解题思路:

将数组从中间一分为二,分别求两个子数组的最大子序和,然后将二者的最大子序和相加,比较三者的大小,最大的即为原数组的最大子序和。数组的子数组同这样分治,直至数组分至最小单元。

此问题的解题思路直观描述如下:

【注】b1b2为两子序列各自的最大子序和,原数组的最大子序和及b1b2b2+b1中最大者,如图中绿色返回语句所示

最大子序和

python实现:

def maxSubArray(Array):
    n = len(Array)
    if(n == 0):
        return None
    if(n == 1):
        return Array[0]
    # 将数组拆分为左右两部分(分)
    left = maxSubArray(Array[: n // 2])
    right = maxSubArray(Array[n // 2:])
    #(治)
    # 求左子数列最大子序和
    temp = 0
    left_max = Array[n // 2 - 1]
    for i in Array[:n // 2][::-1]:
        temp += i
        left_max = max(left_max, temp)
    # 求右子数列最大子序和
    temp = 0
    right_max = Array[n // 2]
    for i in Array[n // 2:]:
        temp += i
        right_max = max(right_max, temp)
    # 返回最大结果(合)
    return max(left_max, right_max, left_max + right_max)

C++实现:

int maxSubArray(int *A) {
	int n = sizeof(A);
	if (n == 0) {
		cerr << "This array is none!";
		return 0;
	}
	if (n == 1)
		return A[0];
	// 拆分数组
	int *leftArray = new int[n / 2];
	int *rightArray = new int[n - n / 2];
	for (int i = 0; i < n; i++) {
		if (i < n / 2)
			leftArray[i] = A[i];
		else
			rightArray[i - n / 2] = A[i];
	}
	int left = maxSubArray(leftArray);
	int right = maxSubArray(rightArray);
	// 求左子序列的最大子序和
	int temp = 0;
	int left_max = leftArray[n / 2 - 1];
	for (int i = n / 2 - 1; i >= 0; i--) {
		temp += leftArray[i];
		if (temp > left_max)
			left_max = temp;
	}
	// 求右子序列的最大子序和
	temp = 0;
	int right_max = rightArray[0];
	for (int i = 0; i < n / 2; i++) {
		temp += rightArray[i];
		if (temp > right_max)
			right_max = temp;
	}
	// 求左右子序和与两和之和的最大值
	temp = left_max + right_max;
	if (left_max > temp)
		temp = left_max;
	if (right_max > temp)
		temp = right_max;
	delete[] leftArray;
	delete[] rightArray;
	return temp;
}

4)幂函数

问题描述:实现 pow(x, n) ,即计算 x 的 n 次幂函数。

解题思路:

用分治进行幂函数求解可以将幂函数分解为若干低次幂的乘积,这里使用二分,分解方法如下:

指数为偶数情况:

A^(2n) = (A^2)^n

指数为奇数情况:

A^(2n+1) = A * A^(2n)

python实现:

def Power(x, n):
    if n == 0:
        return 1
    if n == 1:
        return x
    if n < 0:
        return Power(1 / x, -n)
    # 若指数不为2的倍数,则提出一个底数
    if n % 2 == 1:
        return x * Power(x, n-1)
    # 若指数为2的倍数,则将底数乘方,指数减倍
    return Power(x * x, n / 2)

C++实现:

double Power(double x, int n) {
	if (n == 0)
		return 1;
	if (n < 0)
		return Power(1 / x, -n);
	//拆解幂函数
	if (n % 2 == 0)
		return Power(x * x, n / 2);
	else
		return x * Power(x, n - 1);
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值