剑指offer (C++版)分类整理(一):数组类

本文详细介绍了《剑指Offer》中涉及的C++数组类问题,包括二维数组查找、数组顺序调整、顺时针打印矩阵、出现次数超过一半的数字等经典算法题目的解题思路和解决方案,涵盖了二分查找、双指针、动态规划等多种算法技巧。
摘要由CSDN通过智能技术生成

1. JZ1 二维数组中的查找 (LeetCode 240)

题目描述:

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

这是一个二维有序数组的查找问题,主要考察二分查找。但是也可以根据数组的特点解决。

思路一:二分查找,数组的每一行都是有序的,可以对每一行进行二分查找,时间复杂度O(MlogN),M是矩阵行数,N是矩阵列数,空间复杂度O(1)。

class Solution {
   
public:
    int binary_search(vector<int>& v, int target)
    {
   
    	//二分查找函数
        int left = 0;
        int right = v.size()-1;
        while(left<=right){
   
            int mid = left + (right - left)/2;
            if(v.at(mid) == target)    
            	return true;
            else if(target > v.at(mid))    
            	left = mid + 1;
            else 
            	right = mid - 1;
        }
        return -1;
    }
    bool Find(int target, vector<vector<int> > array) 
    {
   
        
        if(array.empty())    return false;
        for(int i = 0;i<array.size();i++){
   
        	//这里可以优化下
        	//对于每一行最后一个数字,如果小于target,可以直接跳过
            if(binary_search(array.at(i),target) != -1)
                return true;
        }
        return false;
    }
};

思路二:根据矩阵特点,对于左下角,往右走数字变大,往上走数字变小。对于右上角类似。
那么我们从左下角出发,target比当前值大,我们就往右走,target比当前值小,我们就往上走,这样一次可以淘汰一行或者一列,若target存在一定会找到。时间复杂度O(M+N),M是矩阵行数,N是矩阵列数,空间复杂度O(1)。

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

2. JZ13 调整数组顺序使奇数位于偶数前面

题目描述:

输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变

思路:这是一个对数组按规则排序的排序的算法。考虑双指针,类似于插入排序
一个指针从前往后寻找第一个偶数,另一个指针第一个指针往后寻找第一个奇数。由于要保持相对位置:存储次偶数的值,将两指针间的元素从后往前依次后移,将奇数移到第一个指针对应的位置,第一指针后移,此时第一指针前面所有元素都为奇数,完成一次操作。

class Solution {
   
public:
    void reOrderArray(vector<int> &array) {
   
        int len = array.size();
        if(len <= 1){
    // 数组空或长度为1
            return;
        }
        int i = 0;
        while(i < len){
   
            int j = i + 1;
            if(array[i]%2 == 0) // a[i]为偶数,j前进,直到替换
            {
   
                while(array[j]%2 == 0) // array[j]为偶数,前进
                {
   // i为偶数,j也为偶数,一直后移到了末尾,证明后面都是偶数
                    if(j==len-1)
                         return;
                    j++;
                }
                // 此时array[j]为奇数
                int count = j-i;  //需要后移的元素数
                int temp = array[i]; //保存偶数值,防止下一步被覆盖
                array[i] = array[j];  //奇数前移
                while(count>1)  //这里不能使用count--
                {
   
                    array[i+count] = array[i+count-1];//数组后移
                    count--;
                }
                //循环中array[i+1] = array[i],是奇数,temp的作用就是现在
                array[i+1] = temp;
            }
            i++;  //自增,不管array[i]是奇数还是偶数,都要进行下一步
        }
    }
};

3. JZ19 顺时针打印矩阵

题目描述:

输入一个矩阵,按照从外向里顺时针的顺序依次打印出每一个数字,例如,如果输入如下4 X 4矩阵: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 则依次打印出数字1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10.

思路:顺时针打印矩阵,注意结果是一维数组,首先确定矩阵的行树row以及列数col。打印过程中需要维护四个变量,即数组的上、下、左、右边界。执行四次循环,直到边界重合。

class Solution {
   
public:
    vector<int> printMatrix(vector<vector<int> > matrix) {
   
        int row = matrix.size();
        int col = matrix[0].size();
        vector<int> result;  //结果数组
        if(row == 0||col == 0)
            return result;
        //数组的上、下、左、右边界
        int left = 0,right = col - 1,top = 0,btm = row - 1;
        while(left <= right&&top <= btm) //不使用 = 可能会漏掉某行或某列
        {
   
            
            for(int i = left;i <= right;i++)
                result.push_back(matrix[top][i]);
            if(top < btm)  //避免重复打印
                for(int i = top + 1;i <= btm;i++)
                    result.push_back(matrix[i][right]);
            if(top < btm&&left < right)
                for(int i = right - 1;i >= left;i--)
                    result.push_back(matrix[btm][i]);
            if(top < btm&&left < right)
                for(int i = btm - 1;i >= top + 1;i--)
                    result.push_back(matrix[i][left]);
            left++;
            right--;
            top++;
            btm--;
        }
        return result;
    }
};

4. JZ28 数组中出现次数超过一半的数字

题目描述:

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

思路一:采用阵地攻守的思想
第一个数字作为第一个士兵,守阵地;count = 1;遇到相同元素,count++;遇到不相同元素,即为敌人,同归于尽,count–;当遇到count为0的情况,又以新的i值作为守阵地的士兵,继续下去,到最后还留在阵地上的士兵,有可能是主元素。再加一次循环,记录这个士兵的个数看是否大于数组一般即可

class Solution {
   
public:
    int
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值