ch6 - 数组 Array

这篇博客详细探讨了排序数组和子数组相关的算法问题,包括归并两个有序数组、寻找两个排序数组的中位数、最大子数组和、子数组和及最接近零的子数组和等。通过实例解析和代码展示,深入讲解了解决这些问题的不同方法及其时间复杂度分析。
摘要由CSDN通过智能技术生成

数组大部分的题目是偏算法的,这部分主要讲以下两类数组问题:

  • Sorted Array - 排序数组
  • Subarray - 子数组

目录:

  1. Merge Two Sorted Arrays(6 in lintcode)(★★★★★)
  2. Median of Two Sorted Arrays(65 in lintcode)(比较难★★★★★) quick select
  3. Maximum Subarray(41 in lintcode)
  4. Subarray Sum (138 in lintcode)
  5. Subarray Sum Closest (139 in lintcode)

第一部分:Sorted Array - 排序数组

1. Merge Two Sorted Arrays(6 in lintcode)(★★★★★)

1.1求解步骤

step 1:新开辟一个放最终结果的数组,有三个指针i,j,k,分别指向A、B、C数组的下标;

step 2:i,j指向的数比较大小,将较小的数放入新数组,修改i,j指针;

step 3:将A或B剩余部分放入新数组。

1.2 代码

class Solution {
public:
    /*
     * @param A: sorted integer array A
     * @param B: sorted integer array B
     * @return: A new sorted integer array
     */
    vector<int> mergeSortedArray(vector<int> &A, vector<int> &B) {
        // write your code here
        if(A.size()==0){
            return B;
        }
        else if(B.size()==0){
            return A;
        }

        vector<int> res(A.size()+B.size(),0);
        int i = 0, j = 0, k = 0;

        while(i<A.size() && j<B.size()){
            if(A[i] < B[j]){
                res[k++] = A[i++];
            }
            else{
                res[k++] = B[j++];
            }
        }

        while(i<A.size()){
            res[k++] = A[i++];
        }
        while(j<B.size()){
            res[k++] = B[j++];
        }

        return res;
    }
};

1.3 该问题很简单,但是有很多follow up

1.3.1 follow up1:将有序小数组归并到有序大数组里,大数组中空间足够大

1).题目链接

http://www.lintcode.com/problem/merge-sorted-array/
http://www.jiuzhang.com/solutions/merge-sorted-array/

2).时间复杂度 O(m+n)

从末尾开始向开头归并

1.3.2 follow up2: 两个数组的交

0). 题目链接

http://www.lintcode.com/problem/intersection-of-two-arrays/

num1 = [1,2,2,1],num2 = [2,2], return [2]

不止一种做法,面试官希望你提出多种做法,并比较时间复杂度。

1). 方法1:quick sort + merge

先排序,后merge,merge时碰到相同的则放入结果数组。

time:O(nlogn + mlogm + n + m) = O(nlogn + mlogm)
space:O(1)

2). 方法2:hashSet

先将一个数组丢到hashSet中,在判断另一个数组中的数是不是在hashSet中。

time:O( n + m)
space:O( min(n,m))

3). 方法3:binary search

用二分法查询一个数是否在一个有序数组中。所以先对比较小的数组进行排序,然后用二分法依次判断大数组中的每个数是否在小数组中。

如果n<m
time:O(nlogn + mlogn) = O( (n+m)logn )
space:O(1)

4). 方法比较:

!!! hashSet和binary search 都是解决一个数是不是在一个集合中。

方法1比方法3稍微差一点,但是方法2和3不能直接比较。

方法1,3破坏了原数组,即破坏了输入,但方法2占用了额外空间。

1.3.3 follow up3: 数组内积(点乘)

Example [1,3] · [2,4] = 12 + 34 = 14
Follow up: 两个数组都非常大,但是其中都包含很多0
Example [1,0,0,0,0 …, 0, 2, 0,…, 0, 3] · [0,…, 0, 4, 0,…, 0, 5]

这个题和AI是有关系的,本质就是向量相乘。本题更像一个设计题,用<pos,val>存数组。如:
num1 = [<0,1>, <100,1>, <1000,1>]
num2 = [<5,1000>, <100,1>, <999,1>]

2. Median of Two Sorted Arrays(65 in lintcode)(比较难★★★★★)

2.1 题目描述

A = [1,3,5]
B = [2,4,6]
-> [1,2,3,4,5,6]    mid = (3+4)/2 = 3.5 (中间偏左,中间偏右)

A = [1,3,5,7]
B = [2,4,6]
-> [1,2,3,4,5,6,7]    mid = 4

2.2 find median in unsorted array。

5+5230
求解方案:quick select(♥♥♥。O(n) => find median(k=size/2) => find Kth

quick select 的题目:

  • Kth Largest Element (5 in lintcode)在未排序的数组中,找从大到小的第k个数 Kth Smallest
  • Numbers in Unsorted Array (461 in lintcode)

2.3 本题的求解思路

先求解 int findKth(int[ ] A, int[ ] B, int k) //不写实现,先写框架

 主框架:double findMedianSortedArrays(vector<int> &A, vector<int> &B) {
                        int n = A.size() + B.size();
                        if(n%2 == 0){
                               return ( findKth(A, B, n/2-1) + findKth(A, B, n/2) )/2;
                        }
                        return findkth(A, B, n/2);
               }

时间复杂度:O( log(m+n) ) 这是挑战。决定了不能使用先merge sort,在找中位数

※ 根据时间复杂度推测算法:O(log(m+n)) => O( log((m+n)/2) ) => O(log(k)) T(k) = T(k/2) + O(1) => log(k) 如果能用O(1)的时间将找第k大变成第k/2大。
在这里插入图片描述
思路:

  • step 1:比较A[k/2]和B[k/2]。
  • step 2:将较小的数组中的k/2个数扔掉,用startIndex表示剩下的数
  • step 3:截断后找第(k-k/2)大的数
  • step 4:停止条件:a. A或B空,则为B或A 的第k大的数
    b. k=1,则A、B第一个数的较小者。

特殊情况:
如果A数组不够,则直接扔掉B数组的前k/2个。用INT_MAX

2.4 代码

class Solution {
public:
    /*
     * @param A: An integer array
     * @param B: An integer array
     * @return: a double whose format is *.5 or *.0
     */
    double findMedianSortedArrays(vector<int> &A, vector<int> &B) {
        // write your code here
        int n = A.size() + B.size();
        if(n%2 == 0){
            return ( findKth(A,B,0,0,n/2) + findKth(A,B,0,0,n/2+1) )/2.0;  
        }

        return findKth(A,B,0,0,n/2+1);
    }

    int findKth(vector<int> &A, vector<int> &B, int A_st, int B_st, int k){
        if(A_st >= A.size()){
            return B[B_st + k-1];
        }
        if(B_st >= B.size()){
            return A[A_st + k-1];
        }
        if(k==1){
            return min(A[A_st], B[B_st]);
        }

        int A_key = A_st + k/2 -1 < A.size()? A[A_st + k/2 -1] : INT_MAX;
        int B_key = B_st + k/2 -1 < B.size()? B[B_st + k/2 -1] : INT_MAX;

        if(A_key < B_key){
            return findKth(A,B,A_st+k/2,B_st,k-k/2);
        }
        else{
            return findKth(A,B,A_st,B_st+k/2,k-k/2);
        }
    }
};

第二部分:Subarray - 子数组(小视频)

基础知识:

1)前缀和

令PrefixSum[ i ] = A[0] + A[1] + … + A[i-1], PrefixSum[0] = 0。前缀和数组的大小为:n+1。
易知构造PrefixSum耗费O(n)的时间和 O(n)的空间。
如需计算子数组从下标i到下标j之间的所有数之和,则有 Sum[i~j] = PrefixSum[j+1] - PrefixSum[i]

PrefixSum[ i ]表示前i个数之和,即下标为0,1,2,…,i-1的数之和。

2)常见问题

哪个子数组最大?最小? 子数组之和怎么样?之差?

3. Maximum Subarray(41 in lintcode)

3.0题目

http://www.lintcode.com/zh-cn/problem/maximum-subarray/
http://www.jiuzhang.com/solution/maximum-subarray/

给定一个整数数组,找到一个具有最大和的子数组,返回其最大和。

3.1思路

以下标 j 结尾的数组有 j 个,根据Sum[i~j] = PrefixSum[j+1] - PrefixSum[i],第一项不变,第二项改变,所以要想结果大,则第二项足够小.

以下标4的数结尾的最大子数组和为:PrefixSum[4] - min{ PrefixSum[0], PrefixSum[1], PrefixSum[2], PrefixSum[3] }

得到下标4的数结尾的最大子数组和,即需知道以0,1,2,3,4结尾的最小子数组和,于是可以计算以下标5结尾的子数组和,时间为O(1)。

从前往后记录最小子数组和。

3.2 代码

class Solution {
public:
    /*
     * @param nums: A list of integers
     * @return: A integer indicate the sum of max subarray
     */
    int maxSubArray(vector<int> &nums) {
        if(nums.size()==0){
            return 0;
        }

        int preSum = 0, maxsa = INT_MIN, minSum = 0;
        for(int i=0;i<nums.size();++i){
            preSum += nums[i];
            maxsa = max(maxsa, preSum-minSum);
            minSum = min(minSum, preSum);
        }
        return maxsa;
    }
};

3.3 follow up- minimum SubArray

直接将所有数取相反数,求Maximum- SubArray

3.3 follow up- minimum SubArray

 直接将所有数取相反数,求Maximum- SubArray

4. Subarray Sum (138 in lintcode)

4.1 题目

http://www.lintcode.com/zh-cn/problem/subarray-sum/
http://www.jiuzhang.com/solution/subarray-sum/

给定一个整数数组,找到和为零的子数组。你的代码应该返回满足要求的子数组的起始位置和结束位置

4.2 思路

 Sum[i~j] = PrefixSum[j+1] - PrefixSum[i] = 0  ==》   PrefixSum[j+1] = PrefixSum[i]

4.3 代码

class Solution {
public:
    /*
     * @param nums: A list of integers
     * @return: A list of integers includes the index of the first number and the index of the last number
     */
    vector<int> subarraySum(vector<int> &nums) {
        // write your code here
        unordered_map<int,int> m;
        vector<int> res;

        int sum = 0;
        m[sum] = -1;
        for(int i=0;i<nums.size();++i){
            sum += nums[i];
            if(m.find(sum) != m.end()){
                res.push_back(m[sum] + 1);
                res.push_back(i);
                return res;
            }
            m[sum] = i;
        }

        return res;
    }
};

5.Subarray Sum Closest (139 in lintcode)

5.1 题目

http://www.lintcode.com/zh-cn/problem/subarray-sum-closest/
http://www.jiuzhang.com/solution/subarray-sum-closest/

给定一个整数数组,找到一个和最接近于零的子数组。返回第一个和最有一个指数。你的代码应该返回满足要求的子数组的起始位置和结束位置

给出[-3, 1, 1, -3, 5],返回[0, 2],[1, 3], [1, 1], [2, 2] 或者 [0, 4]。

5.2 思路

PrefixSum[j+1] 和 PrefixSum[i] 尽量接近。

暴力求解:O(N^2)

将所有PrefixSum 排序,找尽量接近的两个数。

5.3 代码

class Solution {
public:
    /*
     * @param nums: A list of integers
     * @return: A list of integers includes the index of the first number and the index of the last number
     */

    struct node{
        int val,pos;
        node(int _val, int _pos):val(_val),pos(_pos){}
        bool operator<(const node &o) const{
            return (val < o.val)||(val == o.val && pos<o.pos);
        }
    };

    vector<int> subarraySumClosest(vector<int> &nums) {
        //write your code here
        vector<node> s;
        vector<int> results(2);

        s.push_back(node(0,-1));
        int sum = 0, len = nums.size();
        for (int i = 0; i < len ; ++i) {
            sum += nums[i];     
            s.push_back(node(sum, i));
        }

        sort(s.begin(), s.end());

        len = s.size();
        int ans = INT_MAX;
        for (int i = 0; i < len-1; ++i)
            if (abs(s[i+1].val - s[i].val) < ans) { 
                ans = abs(s[i+1].val - s[i].val);
                results[0] = min(s[i].pos, s[i+1].pos)+1;
                results[1] = max(s[i].pos, s[i+1].pos);
            }
        return results;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值