程序员面试最难的部分就是手写代码,手写代码测试不但能够考察算法运用能力,思维能力,还能看出代码编写习惯。
想去大厂,还是需要及早刷题,这次有机会参与读算法。
本节是书中的第二章,分治法。
图书试阅读地址
https://www.epubit.com/onlineEbookReader?id=fb652b37-b7a6-4843-a933-5ed68b2395c1
算法知识点
分治的本质是将问题分为若干个小问题。
其实分治算法介绍的文绉绉的,我觉得简单的来说,就是算法里面的递归。
比如书里介绍的二分法搜索,文章中的实现并没有用到递归,但是实际上可以写成递归。
相关算法题型题目总结
分治算法题目挺多,包括leetcode上面也有很多。我本身算法功底,数学功底还是偏弱,短时间内可能不适合看难题。本节就看一下中等难度的题目是怎么样的。
算法题目描述
给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
子数组 是数组中的一个连续部分。
示例 1:
输入:nums = [-2,1,-3,4,-1,2,1,-5,4]
输出:6
解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。
示例 2:
输入:nums = [1]
输出:1
示例 3:
输入:nums = [5,4,-1,7,8]
输出:23
题目分析
此题采用分治法进行解决,假设有一段数组,下标为[left,right];
定义数组的中点位置为 mid = (left+right)/2;
那么这一段数组的最大值子数组可能出现的位置只有三个:
在[left,mid]的范围内出现
在[mid+1,right]的范围内出现
在[i,j]的范围内出现,其中 left < i < mid && mid+1 < j < right;
所以我们只需要求解出这三段范围内各自的最大值,进行比较后返回三者中的最大值即可。
具体代码如下。
模板代码
class Solution {
public:
int maxSub(vector<int>&nums,int left,int right){
//参数nums代表数组 在我测试的过程中 此处要加&(取地址符)
//倘若直接传入数组 利用分治法会超时
//left为左端点 right为右端点
if(left == right) //当只有一个元素时 直接返回
return nums[left];
int mid = (left+right)/2; //定义中点
int leftMaxSum = maxSub(nums,left,mid); //获取左侧[left,mid]的最大值
int rightMaxSum = maxSub(nums,mid+1,right); //获取右侧[mid+1,right]的最大值
int toLeftSum = 0,toRightSum = 0; //这两个值暂存向左和向右遍历时的数组和
int leftSum = INT_MIN,rightSum = INT_MIN; //这两个值储存向左和向右遍历时的|最大|数组和
//初始化为INT_MIN是因为左侧/右侧的数组和中的最大值可能为负数
for(int i=mid;i>=left;i--){
toLeftSum+=nums[i]; //从中点向左遍历 求整段数组和
if(toLeftSum>=leftSum){ //获取最大值
leftSum = toLeftSum;
}
}
for(int i=mid+1;i<=right;i++){
toRightSum+=nums[i]; //从中点向右遍历 求整段数组和
if(toRightSum>rightSum){ //获取最大值
rightSum = toRightSum;
}
}
//如果[left,mid]的数组和最大 返回此值
if(leftSum+rightSum<leftMaxSum && rightMaxSum < leftMaxSum )
return leftMaxSum;
//如果[mid+1,right]的数组和最大,返回此值
if(leftSum+rightSum<rightMaxSum)
return rightMaxSum;
return leftSum+rightSum;
}
int maxSubArray(vector<int>& nums) {
if(nums.size()==1)
return nums[0];
return maxSub(nums,0,nums.size()-1);
}
};