Two question about Divide and Conquer

这星期算法课刚好讲到分治算法,便在leetcode上面找了两道关于分治算法的练习题来练练手。

第一题:

169. Majority Element

Given an array of size n, find the majority element. The majority element is the element that appears more than ⌊ n/2 ⌋ times.

You may assume that the array is non-empty and the majority element always exist in the array.

解题思路:

题面很简单,只是要求找到给定的数组中的主元,这个主元就是在该数组中出现的次数大于 ⌊ n/2 ⌋次的数。
暴力求解的话需要对数组中的一半的数遍历一遍数组,复杂度为O(n*n),显然,这样的复杂度是不能接受的。因此,可以通过分治的思想来解决这道题。
首先,以一个数作为比较数,将数组中的数分成三部分,比该数小的,比该数大的,与该数相等的。而后就可以缩小搜索的范围。并在更小的范围内应用相同的方法继续操作。
分治算法的最差复杂度也是O(n*n),但是,这种情况发生的概率非常低,而通过选择合适的划分元素,可以将复杂度降到O(n),比直接使用最快的排序算法O(nlog n)还快。

代码:

#include <iostream>
#include <vector>

using namespace std;

int size;

/*此处的接口设计只是为了与leetcode上所给函数相同,自定义函数可以增加begin和end参数,以减少空间的开销*/
int findMajorityElement(vector<int>& nums) {
  int com = nums[0]; //此处偷懒,将第一个数作为划分的依据
  vector<int> v1, v2; //通过重新设置函数的参数可以减少这一部分的空间开销
  for (int i = 1; i < nums.size(); i++) {
    if (nums[i] < com) v1.push_back(nums[i]);
    else if (nums[i] > com) v2.push_back(nums[i]);
  }
  if ((size % 2 == 0 && v1.size() + v2.size() < size / 2) || 
    (size % 2 == 1 && v1.size() + v2.size() <= size / 2))
    return com;
  if (v1.size() > size / 2) return findMajorityElement(v1);
  if (v2.size() > size / 2) return findMajorityElement(v2);
}

int main() {
  vector<int> nums;
  int temp;
  cin >> size;
  for (int i = 0; i < size; i++) {
    cin >> temp;
    nums.push_back(temp);
  }
  cout << findMajorityElement(nums) << endl;
}

第二题:

215. Kth Largest Element in an Array

Find the kth largest element in an unsorted array. Note that it is the kth largest element in the sorted order, not the kth distinct element.

For example,
Given [3,2,1,5,6,4] and k = 2, return 5.

Note:
You may assume k is always valid, 1 ≤ k ≤ array’s length.

解题思路:

找出数组中的第K大的数,显然,这道题目也是与上一题的思路相同,都可以通过分治算法来加以解决。
同样是做一个划分,找到一个数作为划分的依据,然后缩小搜索的范围,同样可以将复杂度从O(nlog n)降到O(n)。
在上一题的代码中,可以发现,我直接将数组的第一个数作为划分的依据,这样的做法对于某些特殊的数组(如递增数组,递减数组)而言无法做到降低复杂度的作用。
同时,如果单纯使用第一个数作为划分依据,在这道题中的一个大的递增数组会导致超时或者爆内存。因此我改为随机生成下标的方法来规避这样的特殊数据。

代码:

#include <iostream>
#include <vector>
#include <stdlib.h>

using namespace std;

int findKthLargest(vector<int>& nums, int k) {
  vector<int> v1, v2;
  int index = rand() % nums.size();
  int com = nums[index];
  int count = 0;
  for (int i = 0; i < nums.size(); i++) {
    if (nums[i] < com) v1.push_back(nums[i]);
    else if (nums[i] > com) v2.push_back(nums[i]);
    else count++;
  }
  if (v2.size() >= k) return findKthLargest(v2, k);
  else if (v2.size() + count >= k) return com;
  else return findKthLargest(v1, k - count - v2.size());
}

int main() {
  vector<int> nums;
  int temp, n, k;
  cin >> n;
  for (int i = 0; i < n; i++) {
    cin >> temp;
    nums.push_back(temp);
  }
  cin >> k;
  cout << findKthLargest(nums, k) << endl;
}

总结:

这一类可以将大的问题转化为同类型的子问题,非常适合使用分治算法来加以解决,同时,在使用分治算法时,应该注意规避特殊的数据,且通过合适的方法减少空间的开销。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值