169 Majority Element & 53 Maximum Subarray

摩尔定律与寻找最大子数组

  • Moore’s Voting Algorithm
  • Find the Largest Subarray

Moore’s Voting Algorithm

关于摩尔的投票算法可以点击链接在维基百科上面看到详细的解释与说明,使用Moore的投票定律,可以很方便的在O(n)的时间复杂度,O(1)的空间复杂度里面选择出一组数据里面出现次数最多的数据。

以下是题目1:
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.

关于这道题目很明显使用Moore Voting Algorithm就好了,下面直接贴出程序:

int majorityElement(int* nums, int numsSize) {
    int count = 0;
    int major = 0;
    for (int i = 0; i < numsSize; i++) {
        if (count == 0) {
            major = nums[i];
            count++;
        } else if (major == nums[i]) {
            count++;
        } else {
            count--;
        }
    }
    return major;
}

使用摩尔投票算法还可以做与此类似的另一个变形的题目:

以下是题目2:
Given an integer array of size n, find all elements that appear more than ⌊ n/3 ⌋ times. The algorithm should run in linear time and in O(1) space.

这道题目明确要求了时间复杂度是O(n)级别,空间复杂度是O(1)级别,那么除了Moore的投票算法以外,我想找不出更好的算法了。

下面贴出程序:

class Solution {
public:
    vector<int> majorityElement(vector<int>& nums) {
        int count1 = 0;
        int count2 = 0;
        int major1 = 0;
        vector<int> vec;
        int arr[2];
        arr[0] = INT_MIN;
        arr[1] = INT_MIN;
        for (vector<int>::iterator it = nums.begin(); it != nums.end(); it++) {
            if (count1 == 0 && (*it) != arr[1] ) {
                arr[0] = *it;
                count1++;
            } else if (count2 == 0 && (*it) != arr[0]) {
                arr[1] = *it;
                count2++;
            } else if ((*it) == arr[0]) {
                count1++;
            } else if ((*it) == arr[1]) {
                count2++;
            } else {
                count1--;
                count2--;
            }
        }
        count1 = 0;
        count2 = 0;
        for (vector<int>::iterator it = nums.begin(); it != nums.end(); it++) {
            if (*it == arr[0])
                count1++;
            else if (*it == arr[1])
                count2++;
        }
        if (count1 > nums.size() / 3)
            vec.push_back(arr[0]);
        if (count2 > nums.size() / 3)
            vec.push_back(arr[1]);
            return vec;
    }
};

以上的算法使用了两个for循环,时间复杂度都是O(n),然后空间复杂度的话就是对于两个count,长度为2 的一维数组arr和返回的容器vector的开销,综上的分析,可以看出我所使用的算法在实践复杂度和空间复杂度上是满足题目要求的。

Find the largest Subarray

关于使用分治算法来寻找最大子数组的问题,我在查阅了算法导论之后,明确了相关的算法,但是由于对指针的操作的不熟悉,暂时先将这道题目的伪代码贴上来,等我将程序做出来之后才将程序放上来。

以下是题目:
Find the contiguous subarray within an array (containing at least one number) which has the largest sum.
For example, given the array [-2,1,-3,4,-1,2,1,-5,4],
the contiguous subarray [4,-1,2,1] has the largest sum = 6.

这是一个很明显的分治算法的题目,首先可以将数组从中间分开变成两个部分,左和右,那么最大的子数组要么在左半部分,要么在右半部分,要么就横跨中点,再将数组切分,解决的办法也是类似的那么就可以很方便的使用分治算法了。
第一步,先看看如何求出横跨中点的最大子数组,以下是算法:

Find_Crossing_Subarray(int[] A, int low, ing mid, int high)
    left_sum = INT_MIN;
    right_sum = INT_MIN;
    left_max = 0;
    right_max = 0;
    sum = 0;
    for i mid downto 0
        sum += A[i];
        if (sum > left_sum)
            left_Sum = sum;
            left_max = i;
    sum = 0;
    for i mid + 1 upto high
        sum += A[i];
        if (sum > right_sum)
            right_sum = sum;
            right_max = i;
    return (left_max, right_max, left_sum + right_sum)

以上的算法部分的内容实现了寻找跨越中点的最大子数组的问题
接下来实现整一个的算法:

Find_Max_Subarray(int[] A, int low, int high)
    if (low == high)
        return (low, high, A[low])
    else 
        int mid = (low + high) / 2;
        (left_low, left_high, left_sum) =
            Find_Max_Subarray(A, low, mid);
        (right_low, right_high, right_sum) = 
            Find_Max_Subarray(A, mid + 1, high);
        (cross_low, cross_high, cross_sum) = 
            Find_Cross_Subarray(A, low, mid, high);
        if (left_sum>=right_sum && left_sum>=cross_sum)
            return (left_low, left_high, left_sum);
        else if (right_sum >= left_sum
            && right_sum >= cross_sum)
            return (right_low, right_high, right_sum);
        else if (cross_sum >= left_sum 
            && cross_sum >= right_sum)
            return (cross_low, cross_high, cross_sum);

这个算法的递归表达式为:T(n) = 2T(n/2) + O(n);根据大师定理可以计算出这个算法的最终的时间复杂度为:O(nlogn).

关于这道题目还有一种优化的解法,时间复杂度为O(n),但是空间复杂度也为O(n),使用动态规划的思路来做这道题可以节省很多时间,但是需要更多的空间,有一种空间换取时间的感觉。——这里有一篇关于动态规划的不错的博客
动态规划的思路是创建一个和原数组规模一样大的数组,在这个数组里面每一个数字记录到目前为止寻找到的最大的子数组,然后用一个名为max的变量来寻找到所有这些数组和里面最大的那一个。
以下是程序:


class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int* arr = new int[nums.size()];
        int index = 0;
        int* DP = new int[nums.size()];
        int max = INT_MIN;
        for (vector<int> ::iterator it = nums.begin(); it != nums.end(); it++) {
            arr[index] = *it;
            DP[index] = 0;
            index++;
        }
        DP[0] = arr[0];
        for (int i = 1; i < nums.size(); i++) {
            DP[i] = arr[i]+(DP[i-1] > 0 ? DP[i-1] : 0);
            if (DP[i] > max)
                max = DP[i];
        }
        delete arr;
        delete DP;
        return max;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值