剑指Offer——数组相关

剑指Offer——数组相关

1、第1题 二维数组中的查找

题目描述:
在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。

解题思路:
本题我们需要着重的考虑数组的结构,需要关注到的是数组无论是从行还是从列的角度,都是一个单调递增的过程。我们将这个二维数组看成是一个二维的矩阵,我们考虑左下角的元素,从该元素开始,向上元素递减,向右元素递增。以该元素为起点,如果目标元素大于该元素则向右走,如果目标元素小于该元素在向上走。走到其他元素的时候的,过程与该元素类似。

代码实现:

class Solution {
public:
    bool Find(int target, vector<vector<int> > array) {
        if(array.size() == 0||array[0].size()==0)
            return false;
        int leng = array.size()-1;
        int leng2 = 0;
        while(leng>=0 && leng2 < array.size() )
        {
            if(target > array[leng][leng2])
                leng2++;
            else if(target < array[leng][leng2] )
                leng--;
            else
                return true;
        }
        return false;
    }
};
2、第6题 旋转数组中的最小数字

题目描述:

把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。

解题思路:
这道题,我们需要注意到的时候原始数组是一个非递减的数组,那么在旋转之后,可以将原始的数组分成两个部分,第一个部分是一个非递减的数组,在旋转之后的部分是一个非递减的数组,第二个数组的第一个节点就是整个数组最小的元素。

代码实现:

class Solution {
public:
    int minNumberInRotateArray(vector<int> rotateArray) {
        if(rotateArray.size()==0)
            return 0;
        for(int index =0;index<rotateArray.size()-1;index++)
        {
            if(rotateArray[index]>rotateArray[index+1])
            {
                return rotateArray[index+1];
            }
        }
        return rotateArray[0];
    }
};
3、第28题 数组中出现次数超过一半的数字

题目描述:

数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。

基本思路:

如果数组是有序的,那么超过数组一半的元素肯定会出现在数组中间的位置。下面,我们只需要将数组进行排序,然后获取到中间元素,在计算中间元素的个数是否超过了数组的一半即可。

代码实现:

class Solution {
public:
    int MoreThanHalfNum_Solution(vector<int> numbers) {
        if(numbers.size()== 0)
            return 0;
        sort(numbers.begin(),numbers.end());
        int middle = numbers[numbers.size()/2];
        int count = 0;
        for(int i=0;i<numbers.size();i++)
            if(numbers[i] == middle)
                count ++;
        if(count > (numbers.size()/2))
            return middle;
        else
            return 0;
    }
};
4、第30题 连续子数组的最大和

题目描述:

HZ偶尔会拿些专业问题来忽悠那些非计算机专业的同学。今天测试组开完会后,他又发话了:在古老的一维模式识别中,常常需要计算连续子向量的最大和,当向量全为正数的时候,问题很好解决。但是,如果向量中包含负数,是否应该包含某个负数,并期望旁边的正数会弥补它呢?例如:{6,-3,-2,7,-15,1,2,2},连续子向量的最大和为8(从第0个开始,到第3个为止)。给一个数组,返回它的最大连续子序列的和,你会不会被他忽悠住?(子向量的长度至少是1)

解题思路:

本题是是一道经典的动态规划的问题,我们来考虑这个问题中动态规划的转移情况,首先,动态规划的第一个状态是数组的第一个元素,对于下一个状态而言,如果前一个状态加上当前的元素的和要比当前元素的值要大,那么当前状态的取值就应该是前一个状态的值和当前数组元素的和,否则我们只保留当前元素即可。最后,我们找到整个状态序列中的最大的元素即可。

代码实现:

class Solution {
public:
    int FindGreatestSumOfSubArray(vector<int> array) {
            int length = array.size();
            if(length==0)
                return 0;
            int * dp = new int (length);
            dp[0] = array[0];
            int maxv = dp[0];
            for(int i=1;i<length;i++)
            {
                if((array[i]+dp[i-1]) > array[i])
                    dp[i] = array[i] + dp[i-1];
                else
                    dp[i] = array[i];
                if(dp[i] > maxv)
                    maxv = dp[i];
            }
        return maxv;
    }
};
5、第32题 把数组排成最小的数

题目描述:

输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323。

解题思路:

该题是一个排列的问题,对于两个数字串A和B而言,如果AB排序之后的结果小于BA,则将A排在前面,B排在后面。所以,我们只需要将数组中的所有字符串按照上面的方式进行排序,在将数组中的字符串拼接起来。

代码实现:

class Solution {
public:
    static bool cmp(int a,int b)
    {
         string A = "";
        string B = "";
        A += to_string(a);
        A += to_string(b);
        B += to_string(b);
        B += to_string(a);
        return A<B;
    
    }
    string PrintMinNumber(vector<int> numbers) {
        string res = "";
        if(numbers.size() == 0)
            return res;
        sort(numbers.begin(),numbers.end(),cmp);
        for(int i=0;i<numbers.size();i++)
            res += to_string(numbers[i]);
        return res;
    }
       
};
6、第35题 数组中的逆序对

题目描述:

在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P%1000000007

解题思路:

对于该题,我们需要借助归并排序中的思想,先将数组进行分的过程,这个过程将原来的数组不断的进行分解。比较是否逆序对。然后是合并的过程,这个过程中我们借助一个辅助的数组copy,在合并的过程中同时统计两个数组之间的逆序对的数量。在这个合并的过程中,我们需要对数组进行排序,以免在后续的统计中重复统计。

代码实现:

class Solution {
public:
    int InversePairs(vector<int> data) {
        int length = data.size();
        if(length <= 0)
            return 0;
        vector<int> copy;
        for(int i=0;i<length;i++)
            copy.push_back(data[i]);
        long long count = InversePairCore(data,copy,0,length-1);
        return count % 1000000007;
    }
    long long InversePairCore(vector<int> &data,vector<int> &copy,int start,int end)
    {
        if(start == end)
        {
            copy[start] = data[start];
            return 0;
        }
        int length = (end - start) /2;
        long long left = InversePairCore(copy,data,start,start+length);
        long long right = InversePairCore(copy,data,start+length+1,end);
        
        int i = start + length;
        int j = end;
        int indexcopy = end;
        long long count = 0;
        while((i >= start) && (j>=start+length+1))
        {
            if(data[i]>data[j])
            {
                copy[indexcopy--] = data[i--];
                count = count+j-start-length;
            }
            else
                copy[indexcopy--] = data[j--];
        }
        for(;i>=start;i--)
            copy[indexcopy--] = data[i];
        for(;j>=start+length+1;j--)
            copy[indexcopy--] = data[j];
        return left+right+count;
    }
};
7 第37题、数字在排序数组中出现的次数

题目描述:

统计一个数字在排序数组中出现的次数。

基本思路:

一次遍历,计算次数即可。

代码实现:

class Solution {
public:
    int GetNumberOfK(vector<int> data ,int k) {
        int count = 0;
        for(int i=0;i<data.size();i++)
            if(data[i] == k)
                count += 1;
        return count;
    }
};
8 第40题、数组中只出现一次的数字

题目描述:

一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。

基本思路:

这里,介绍一个基本的知识,两个数字A,B,如果A,B相同,则AB异或的结果为0。依照这个思路,我们一次对数组中的元素进行异或,最后所剩的结果一定是只出现一次的两个元素。将结果视为二进制,则其中的1表示的就是只出现1次的两个元素A,B的不同的位。下一步我们按照二进制结果中的第一次出现1的位置来将数据分成两个数组。此时AB两个元素肯定在不同的数组中,并且相同的元素肯定在一个数组中,在按照这个规律进行异或。获取最后的结果。

代码实现:

class Solution {
public:
    void FindNumsAppearOnce(vector<int> data,int* num1,int *num2) {
            if(data.size() == 0)
                return ;
            int res = 0;
            for(int i=0;i<data.size();i++)
                 res ^= data[i];
            int index = 1;
            while((index & res) ==0)
                index = index << 1;
            int result1 = 0;
            int result2 = 0;
            for(int i=0;i<data.size();i++)
            {
                if((index & data[i])==0)
                    result1 = result1 ^ data[i];
                else
                    result2 = result2 ^ data[i];
            }
            num1[0] = result1;
            num2[0] = result2;
            return ; 
    }
};
9、 第50题 数组中重复的数字

题目描述:

在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是第一个重复的数字2。

解题思路:

本题的思路为采用map映射的方式,遍历一遍数组,记录数组中每一个元素的个数,在遍历的过程中找到数组元素个数不为1的元素。

代码实现:

class Solution {
public:
    // Parameters:
    //        numbers:     an array of integers
    //        length:      the length of array numbers
    //        duplication: (Output) the duplicated number in the array number
    // Return value:       true if the input is valid, and there are some duplications in the array number
    //                     otherwise false
    bool duplicate(int numbers[], int length, int* duplication) {
        if(length <= 0)
            return false;
        map<int,int> mp;
        for(int i=0;i<length;i++)
        {
            if(mp.find(numbers[i])!=mp.end())
            {
                *duplication = numbers[i];
                return true;
            }
            else
            {
                mp[numbers[i]] ++;
            }
        }
        return false;
    }
};
10 第51题 构建乘积数组

题目描述:

给定一个数组A[0,1,…,n-1],请构建一个数组B[0,1,…,n-1],其中B中的元素B[i]=A[0]A[1]…*A[i-1]A[i+1]…*A[n-1]。不能使用除法。(注意:规定B[0] = A[1] * A[2] * … * A[n-1],B[n-1] = A[0] * A[1] * … * A[n-2];)

解题思路:

本题的思路是双向计算的过程,首先是前向计算,对于前向计算,首先从B[0]=1,开始B[i] = B[i-1] *A[i-1]。下一个部分是反向计算,其中B[i] = B[i] *A[i+1],…A[n-1],这样就避开了A[i]元素。

代码实现:

class Solution {
public:
    vector<int> multiply(const vector<int>& A) {
        vector<int> B;
        if(A.size() == 0)
            return B;
        B.push_back(1);
        for(int i=1;i<A.size();i++)
            B.push_back(B.back() * A[i-1]);
        int temp = 1;
        for(int i = A.size()-1;i>0;i--)
        {
            temp *= A[i];
            B[i-1] *= temp;
        }
        return B;
        
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值