剑指offer专题——数组(查找)(持续更新)

剑指offer 01——二维数组的查找

题目描述

在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
[
[1, 2, 8, 9],
[2, 4, 9, 12],
[4, 7,10,13],
[6, 8 11,15]
]
给定 target = 7,返回 true。
给定 target = 3,返回 false。

解题思路

二分法
右上角向左下角查找
大于查找值:x-1
小于查找值:y+1

代码

bool Find(int target, vector<vector<int> > array) {
    if(array.size() == 0 || array[0].size() == 0)
        return false;
    int x_len = array.size();
    int y_len = array[0].size();
    for(int x = 0, y = y_len - 1; x < x_len && y >= 0;){
        if(array[x][y] == target)
            return true;
        else if(array[x][y] > target)
            --y;
        else
            ++x;
    }
    return false;
}

剑指offer 06——旋转数组的最小数字

题目描述

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

输入
[3,4,5,1,2]
返回值
1

解题思路

二分法
每次取中间位置与末尾位置相比
1.mid > r 说明位于 mid+1 到 r 之间
2.mid < r 说明位于 l 到 mid 之间
3.mid = r ,r–,进一步判断

代码

int minNumberInRotateArray(vector<int> rotateArray) {
    if(rotateArray.size() == 0) 
        return 0;
    int l = 0, r = rotateArray.size() - 1;
    while(l < r) {
        int mid = l + ((r - l) >> 1);
        if(rotateArray[mid] > rotateArray[r])
            l = mid + 1;
        else if(rotateArray[mid] < rotateArray[r])
            r = mid;
        else
            r--;
    }
    return rotateArray[l];
}

剑指offer 13——调整数组顺序使奇数位于偶数前面

题目描述

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

输入
[1,2,3,4]
返回值
[1,3,2,4]

解题思路

out-place操作:新定义一个vector,遍历两次容器,第一次放进奇数,第二次放进偶数
in-place操作:双指针方法,头指针指向第一个奇数,尾指针向后遍历,需要初始化操作一下,容器的头位置必须为奇数

代码

//out-place操作
vector<int> reOrderArray(vector<int>& array) {
        // write code here
        vector<int> res;
        for(int i = 0; i < array.size(); ++i){
            if(array[i] & 1)
                res.push_back(array[i]);
        }
        for(int i = 0; i < array.size(); ++i){
            if(! (array[i] & 1))
                res.push_back(array[i]);
        }
        return res;
    }

剑指offer 19——顺时针打印矩阵

题目描述

输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下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.

输入
[[1,2],[3,4]]
返回值
[1,2,4,3]

解题思路

循环打印步骤为下图所示:
在这里插入图片描述
打印过程注意边界条件即可

代码

void print(vector<vector<int>> &matrix, vector<int> &res, int l_x, int r_x, int l_y, int r_y){
    //第一步:
    for(int j = l_y; j <= r_y; ++j) res.push_back(matrix[l_x][j]);
    //第二补:
    for(int i = l_x + 1; i <= r_x; ++i) res.push_back(matrix[i][r_y]);
    //第三步:
    int high = r_x - l_x;
    if(high)
        for(int j = r_y - 1; j >= l_y; --j) res.push_back(matrix[r_x][j]);
    //第四步:
    int width = r_y - l_y;
    if(width)
        for(int i = r_x - 1; i > l_x; --i) res.push_back(matrix[i][l_y]);
}

vector<int> printMatrix(vector<vector<int> > matrix) {
    vector<int> res;
    if(!matrix.size() || !matrix[0].size())
        return res;
    int l_x = 0, l_y = 0;
    int r_x = matrix.size() - 1;
    int r_y = matrix[0].size() - 1;
    while(l_x <= r_x && l_y <= r_y) {
        print(matrix, res, l_x++, r_x--, l_y++, r_y--);
    }
    return res;
}

剑指offer 34——第一个只出现一次的字符

题目描述

在一个字符串(0<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,并返回它的位置, 如果没有则返回 -1(需要区分大小写).(从0开始计数)

输入
“google”
返回值
4

解题思路

使用数组作为辅助容器
1.第一次遍历找出每个字符出现的次数
2.第二次遍历找到目标字符

代码

int FirstNotRepeatingChar(string str) {
    //定义数组存储字符出现的次数
    int array[128] = {0};
    for(char c : str) {
        ++array[c];
    }
    //查找出现一次的数组
    for(int i = 0; i < str.size(); ++i) {
        if(array[str[i]] == 1)
            return i;
    }
    return -1;
}

剑指offer 35——数组中的逆序对

题目描述

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

输入
[1,2,3,4,5,6,7,0]
返回值
7

解题思路

利用归并排序的思想
对于 (m, n)和 (i, j)两个有序的区间,若m>i, 则 (m+1, n)都大于i
并且内层的逆序对不影响外层的逆序对
具体逻辑见代码

代码

int InversePairs(vector<int> data) {
    int ret = 0;
    merge_sort(data, ret, 0, data.size()-1);
    return ret;
}

//归并排序(递归)
void merge_sort(vector<int> &data, int &ret, int l, int r) {
    if(l >= r)
        return;
    int mid = l + ((r-l)>>1);
    merge_sort(data, ret, l, mid);
    merge_sort(data, ret, mid+1, r);
    merge(data, ret, l, mid, r);
}

//子区间合并(计算逆序对的部分)
void merge(vector<int> &data, int &ret, int l, int mid, int r) {
    vector<int> tmp(r - l + 1);
    int i = l, j = mid + 1, k = 0;
    while(i <= mid && j <= r) {
        if(data[i] > data[j]){
            tmp[k++] = data[j++];
            //计算逆序对
            ret += (mid - i + 1);
            ret %= 1000000007;
        }
        else {
            tmp[k++] = data[i++];
        }
    }
    while(i <= mid)
        tmp[k++] = data[i++];
    while(j <= r)
        tmp[k++] = data[j++];
    //排序结果写回到原数组
    for(k = 0, i = l; i <= r; ++i, ++k) {
        data[i] = tmp[k];
    }
}

剑指offer 41——和为S的连续正数序列

题目描述

小明很喜欢数学,有一天他在做数学作业时,要求计算出9~16的和,他马上就写出了正确答案是100。但是他并不满足于此,他在想究竟有多少种连续的正数序列的和为100(至少包括两个数)。没多久,他就得到另一组连续正数和为100的序列:18,19,20,21,22。现在把问题交给你,你能不能也很快的找出所有和为S的连续正数序列? Good Luck!

输入
9
返回值
7[[2,3,4],[4,5]]

解题思路

滑动窗口
定义滑动窗口的开始/结束位置,以及当前窗口内的数字之和

代码

vector<int> GetRes(int l, int r){
    vector<int> res;
    for(int i = l; i <= r; ++i) 
        res.push_back(i);
    return res;
}

vector<vector<int>> FindContinuousSequence(int sum) {
    vector<vector<int>> res;
    int l = 1, r = 1, tmp = 1;
    while(r < sum){
    	//形成一个窗口,窗口右移
        if(tmp == sum) {
            res.push_back(GetRes(l, r));
            ++r;
            tmp += r;
        }
        //窗口之和过大
        else if(tmp < sum){
            ++r;
            tmp += r;
        }
        //窗口之和过小
        else{
            tmp -= l;
            ++l;
        }
    }
    return res;
}

剑指offer 42——和为S的两个数字

题目描述

输入一个递增排序的数组和一个数字S,在数组中查找两个数,使得他们的和正好是S,如果有多对数字的和等于S,输出两个数的乘积最小的。

输入
[1,2,4,7,11,15],15
返回值
[4,11]

解题思路

双指针
左指针指向0,右指针指向size()-1
定义保存当前乘积的变量

代码

vector<int> FindNumbersWithSum(vector<int> array,int sum) {
    pair<int, int> res;
    if(array.size() <= 1) return vector<int>();
    //当前乘积的最小值
    int tmp = INT_MAX;
    int l = 0, r = array.size() - 1;
    while(l < r) {
        if(array[l] + array[r] == sum) {
            if(array[l] * array[r] < tmp){
                res = {l, r};
                tmp = array[l] * array[r];
            }
            //不要忘了这一步
            ++l, --r;
        }
        else if(array[l] + array[r] < sum)
            ++l;
        else
            --r;
    }
    if(res.first == res.second) return vector<int>();
    return vector<int>{array[res.first], array[res.second]};
}

剑指offer 45——扑克牌顺子

题目描述

LL今天心情特别好,因为他去买了一副扑克牌,发现里面居然有2个大王,2个小王(一副牌原本是54张_)…他随机从中抽出了5张牌,想测测自己的手气,看看能不能抽到顺子,如果抽到的话,他决定去买体育彩票,嘿嘿!!“红心A,黑桃3,小王,大王,方片5”,“Oh My God!”不是顺子…LL不高兴了,他想了想,决定大\小 王可以看成任何数字,并且A看作1,J为11,Q为12,K为13。上面的5张牌就可以变成“1,2,3,4,5”(大小王分别看作2和4),“So Lucky!”。LL决定去买体育彩票啦。 现在,要求你使用这幅牌模拟上面的过程,然后告诉我们LL的运气如何, 如果牌能组成顺子就输出true,否则就输出false。为了方便起见,你可以认为大小王是0。

输入
[0,3,2,6,4]
返回值
true

解题思路

1.对牌进行排序
2.除了0,找出最大的和最小的数,差值小于5说明可以组成顺子
3.如果响铃的两张牌相等,说明不能组成顺子

代码

bool IsContinuous( vector<int> numbers ) {
    if(numbers.size() < 5) return false;
    sort(numbers.begin(), numbers.end());
    int i = 0, sz = numbers.size();
    for(int j = 0; j < sz; ++j) {
        if(numbers[j] == 0) {
            ++i;
            continue;
        }
        if(j < sz - 1 && numbers[j] == numbers[j+1])
            return false;
    }
    return (numbers[sz-1] - numbers[i]) < 5;
}

剑指offer 50——数组中重复的数字

题目描述

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

输入
[2,3,1,0,2,5,3]
返回值
2或3

解题思路

1.使用map存储数字出现的次数
2.当前数字出现了2次,跳出循环

代码

int duplicate(vector<int>& numbers) {
        // write code here
        map<int, int> mp;
        for(int k : numbers) {
            ++mp[k];
            if(mp[k] == 2)
                return k;
        }
        return -1;
    }

剑指offer 51——构建乘积数组

题目描述

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

输入
[1,2,3,4,5]
返回值
[120,60,40,30,24]

解题思路

1.B[i] = left[i] * right[i]
2.先求出left[i],
3。再求出right[i],边求边算B[i],因为A[i]已经构建好了
代码

vector<int> multiply(const vector<int>& A) {
    vector<int> B(A.size(), 1);
    for(int i = 1; i < A.size(); ++i) {
        //left[i] 用B[i]代替
        B[i] = B[i-1] * A[i-1];
    }
    int tmp = 1;
    for(int j = A.size()-2; j >= 0; --j) {
        //right[i]使用tmp来代替
        tmp *= A[j+1];
        B[j] *= tmp;
    }
}

剑指offer 64——滑动窗口的最大值

题目描述

给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在6个滑动窗口,他们的最大值分别为{4,4,6,6,6,5}; 针对数组{2,3,4,2,6,2,5,1}的滑动窗口有以下6个: {[2,3,4],2,6,2,5,1}, {2,[3,4,2],6,2,5,1}, {2,3,[4,2,6],2,5,1}, {2,3,4,[2,6,2],5,1}, {2,3,4,2,[6,2,5],1}, {2,3,4,2,6,[2,5,1]}。
窗口大于数组长度的时候,返回空

输入
[2,3,4,2,6,2,5,1],3
返回值
[4,4,6,6,6,5]

解题思路

1.暴力方法

代码

vector<int> maxInWindows(const vector<int>& num, unsigned int size) {
    vector<int> ret;
    if(num.size() < size) return ret;
    int len = num.size();
    
    for(int i = 0; i + size - 1 < len; ++i){
        int j = i + size -1;
        int max_val = num[j];
        for(int k = i; k < j; ++k) {
            max_val = max(num[k], max_val);
        }
        ret.push_back(max_val);
    }
    return ret;
}

剑指offer 65——矩阵中的路径

题目描述

请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。如果一条路径经过了矩阵中的某一个格子,则该路径不能再进入该格子。 例如下图矩阵中包含一条字符串"bcced"的路径,但是矩阵中不包含"abcb"路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入该格子。
在这里插入图片描述

输入
[[a,b,c,e],[s,f,c,s],[a,d,e,e]],“abcced”
返回值
true

解题思路

  1. dfs + 剪枝操作

代码

int rows, cols;

bool dfs(vector<vector<char>> &matrix, string word, int i, int j, int k) {
    //剪枝操作
    if(i >= rows || i < 0 || j >= cols || j < 0 || matrix[i][j] != word[k])
        return false;
    if(k == word.size() - 1) return true;
    //当前相等,不能在进入这个格子
    matrix[i][j] = '\0';
    bool res = dfs(matrix, word, i+1, j, k + 1) || dfs(matrix, word, i-1, j, k + 1) ||
        dfs(matrix, word, i, j+1, k + 1) ||dfs(matrix, word, i, j-1, k + 1);
    //回溯
    matrix[i][j] = word[k];
    return res;
}

bool hasPath(vector<vector<char> >& matrix, string word) {
    // write code here
    rows = matrix.size();
    cols = matrix[0].size();
    //每一个格子都可以作为起点
    for(int i = 0; i < rows; ++i) {
        for(int j = 0; j < cols; ++j) {
            if(dfs(matrix, word, i, j, 0)) return true;
        }
    }
    return false;
}

剑指offer 66——机器人的运动范围

题目描述

地上有一个m行和n列的方格。一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于k的格子。 例如,当k为18时,机器人能够进入方格(35,37),因为3+5+3+7 = 18。但是,它不能进入方格(35,38),因为3+5+3+8 = 19。请问该机器人能够达到多少个格子?

输入
5,10,10
返回值
21

解题思路

dfs

代码

using v = vector<int>;
using vv = vector<v>;
int dir[5] = {-1, 0, 1, 0, -1};

int check(int n)
{
    int sum = 0;
    while(n){
        sum += (n%10);
        n /= 10;
    }
    return sum;
}

void dfs(int x, int y, int sho, int r, int c, int &ret, vv &mark){
    //检查下标                                  是否已经被访问
    if(x < 0 || x >= r || y < 0 || y >= c || mark[x][y] == 1)
        return;
    //当前坐标不满足条件
    if(check(x) + check(y) > sho)
        return;
    mark[x][y] = 1;
    ret += 1;
    
    for(int i = 0; i < 4 ;++i){
        dfs(x+dir[i], y+dir[i+1], sho, r, c, ret, mark);
    }
}


int movingCount(int threshold, int rows, int cols) {
    if(threshold <= 0){
        return 0;
    }
    vv mark(rows, v(cols, -1));
    int ret = 0;
    dfs(0, 0, threshold, rows, cols, ret, mark);
    return ret;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值