算法设计与分析 hws & labs
这门课的课本是《算法导论》,hws 就是老师布置的一些课后作业,labs 就是提供的五个实验,在这里记录的目的主要是帮助自己理解算法的设计与分析,而不仅仅是作为应付作业的需求。
Lab 1 Division and Conquer
分治法,主要思想是讲问题分解为子问题,当子问题足够在 O(n) 以内的时间复杂度解决时即可返回。
leetcode 4.寻找两个正序数组的中位数:
- 首先将为题转变为寻找两个正序数组的第 k 小个数
- 分解为子问题:给定两个数组的起始位置,从此位置向后寻找第 k 小个数
- 子问题解决方式:每次考察两个数组中距离起始位置 k / 2 的数的大小,对比此值较小的那个数组的前 k / 2 个(数必定会存在于最终的 k 个数中,因此可以去掉这 k / 2 个(也有可能全被抛弃)数,直接从剩余的所有树中寻找第 k - (抛弃的个数) 小的数
点击查看代码
class Solution {
public:
int findKth(vector<int>& nums1, int i, vector<int>& nums2, int j, int k) {
if (nums1.size() - i > nums2.size() - j) {
return findKth(nums2, j, nums1, i, k);
}
if (nums1.size() == i)
return nums2[j + k - 1];
if (k == 1)
return min(nums1[i], nums2[j]);
int si = min((int)nums1.size(), i + k / 2), sj = j + k / 2;
if (nums1[si - 1] > nums2[sj - 1]) {
return findKth(nums1, i, nums2, sj, k - k / 2);
} else {
return findKth(nums1, si, nums2, j, k - (si - i));
}
}
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
int tot = nums1.size() + nums2.size();
if (tot % 2 == 0) {
int l = findKth(nums1, 0, nums2, 0, tot / 2);
int r = findKth(nums1, 0, nums2, 0, tot / 2 + 1);
return (l + r) / 2.0;
} else
return findKth(nums1, 0, nums2, 0, tot / 2 + 1);
}
};
Leetcode53. 最大子数组和:
这个题目的思路是来自于课本第四章分治策略的一个例子,原例子讲的是股票市场的最大交易方式,这个题目具有同样的思路:
给定数组和左右边界,以其中点为分割,则最大子数组要么存在于中点左侧(包括中点),要么存在于中点右侧(包括中点),要么跨越中点。
以此为子问题,用左右边界来分解问题,其中跨越中点的最大子数组和要用到一次遍历,分别计算 l 到 mid 和 mid 到 r 两部分的最大和。
点击查看代码
class Solution {
public:
int cal(vector<int>& nums, int l, int r) {
if (l == r)
return nums[l];
int mid = (l + r) >> 1;
int lmax = nums[mid], rmax = nums[mid + 1];
int lsum = 0, rsum = 0;
for (int i = mid; i >= l; i --) {
lsum += nums[i];
lmax = max(lmax, lsum);
}
for (int i = mid + 1; i <= r; i ++) {
rsum += nums[i];
rmax = max(rmax, rsum);
}
int lr = max(cal(nums, l, mid), cal(nums, mid + 1, r));
return max(lr, lmax + rmax);
}
int maxSubArray(vector<int>& nums) {
int n = nums.size();
return cal(nums, 0, n - 1);
}
};