剑指Offer_题解 C++

1.二维数组中的查找

题目描述

 

 

 
 
思路,本来想着暴力写一遍,但是暴力估计面试的时候直接被pass,所以思考了优化,首先能确定这个二维数组的规模是一个矩形,也就是每个一维的长度都相等的二维数组。然后尝试先确定答案的所在行,然后确定答案的所在列。但是这是个错误思路错误的原因是因为题目只分别保证了横向递增和纵向递增,不保证横纵同时递增,所以没法分别确定行和列。随后看了一下讨论区的思路,十分明确,
/* 思路
* 矩阵是有序的,从左下角来看,向上数字递减,向右数字递增,
* 因此从左下角开始查找,当要查找数字比左下角数字大时。右移
* 要查找数字比左下角数字小时,上移
*/

class Solution {
public:
    bool Find(int target, vector<vector<int> > array) {
            if(array.size()==0){return false;}
             if(array[0].size()==0){return false;}
             int i = array[0].size()-1;
             int j = 0;
             bool flag = false;
             while(i>=0 && j<array.size()){
                if(array[j][i]==target){return true;}
                if(array[j][i]>target){i--;}
                else{j++;}
             }
             return flag;

    }
};

以上是我的代码,在调试的过程中写出了一万遍运行错误RE,最后只能手写了测试样例。
int main()
{
    vector<int>vp;
    vector<vector<int> >v;
    for(int i=0;i<5;i++){
        vp.push_back(i);
    }
    v.push_back(vp);
    vp.clear();
    for(int i=0;i<5;i++){
        vp.push_back(i+5);
    }

    v.push_back(vp);
    vp.clear();
    for(int i=0;i<5;i++){
        vp.push_back(i+10);
    }
    v.push_back(vp);


    Solution s;
    bool flag = s.Find(11,v);
    if(flag){
        cout<<"yes"<<endl;
    }else{
        cout<<"NO"<<endl;

    }

    return 0;
}

 

以上是测试样例代码,最后发现测试数据里存在空数据。所以

if(array.size()==0){return false;}
if(array[0].size()==0){return false;}

需要加上这两句话,考虑特殊情况。


2.不修改数组找出重复的数字

 

 首先明确,最简单的方法是暴力,用O(N^2)的时间复杂度,两个for嵌套对于数组中的每一个元素都去判断数组中有没有和他相同的元素。

然后我们进行优化,假如不考虑空间复杂度,我们可以通过桶排序的原理进行解答,遍历一遍数组,统计每个数字出现的次数,然后随便输出一个出现次数大于等于2的元素就行。

但是,假如只能使用O(1)的额外空间,那么桶排序肯定就不可以了,我们继续对题目进行分析,由于原数组不能改变,所以不能进行排序。那么怎么办呢,我们想到了分治。

利用二分的方法去解决这个问题,那么我们需要找到用于二分的依据,也就是寻找问题的单调性,很明显我们不能根据数组下标进行二分,因为数组元素是乱序的。但是数组元素的范围给定了,根据抽屉原理,1-n的数据范围内存在n+1个元素,那么必然有至少一个元素是重复的,所以我们二分数据范围,假如在这个数据范围内存在的元素数量大于这个范围的数据量,例如1-5的范围里存在了6个元素,那么必然存在重复的数字。

采用分治的思想,将每个数的取值的区间[1, n]划分成[1, n/2]和[n/2+1, n]两个子区间,然后分别统计两个区间中数的个数。
注意这里的区间是指 数的取值范围,而不是 数组下标。

c++

class Solution {
public:
    int duplicateInArray(vector<int>& nums) {
         //划分二分的边界,范围是1-n,因为Vector有n+1个元素,所以右边界取的是size-1;
         int l = 1,r =  nums.size()-1;
         //二分模板
         while(l<r){
            int mid = l + r >> 1;
            int cnt = 0;//统计有多少数
            for(auto x:nums){
                cnt += x>=l && x<=mid;
            }
            if(cnt >= mid - l + 1) r = mid;//重复的数在左区间
            else l = mid+1;//重复的数在右区间
         }
         return l;
    }
};
 



posted @ 2019-11-15 09:42  FLydoggie  阅读(229)  评论(0)    收藏  举报