算法学习笔记——LeetCode分类算法
1.分而治之
1.主要思想
对一个大规模的问题运用分治策略,就是将这个大规模的问题划分成若干小规模的子问题,然后对这这些子问题进行求解,最后按划分将求得的结果进行合并,就得到了原问题的解。分治思想就是将分解,即将大问题分解为若干小问题,然后各个击破。
利用分治算法求解问题的步骤可以如下表示:
1.分:将大问题分解成众多小问题;
2.治:求解各个小问题;
3.合:将解决的问题合并
【注】分治是一种策略,如果一个大问题进行一次分解后仍然无法轻易解出,则可继续对其子问题进行划分,直至子问题解出。
分治过程可以用树状图表示如下:
2.分治策略的适用情况
- 原问题的计算复杂度随问题规模的增加而增加
- 原问题能够被分解为更小的子问题
- 子问题的结构和性质与原问题一样,并且相互独立,子问题之间不包含公共的子子问题(子问题的子问题)
- 原问题分解出的子问题可以合并为该问题的解
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] 的元素。(此处假设数组是非空的,并且给定的数组总是存在众数。)
解题思路:
将数组从中间分为左右两个子数组,求左右两个子数组的众数,比较左右两数组的众数是否相同,相同则返回此众数;若不相同,则比较左右各众数在数组中出现的次数,返回次数多者。
此问题解题思路的直观描述如下:
【注】图中b1、b2以及蓝色元素为数组中的众数
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 ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
解题思路:
将数组从中间一分为二,分别求两个子数组的最大子序和,然后将二者的最大子序和相加,比较三者的大小,最大的即为原数组的最大子序和。数组的子数组同这样分治,直至数组分至最小单元。
此问题的解题思路直观描述如下:
【注】b1、b2为两子序列各自的最大子序和,原数组的最大子序和及b1、b2、b2+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 次幂函数。
解题思路:
用分治进行幂函数求解可以将幂函数分解为若干低次幂的乘积,这里使用二分,分解方法如下:
指数为偶数情况:
指数为奇数情况:
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);
}