【剑指Offer】查找算法(1)

本文介绍了如何高效地在满足特定排序规则的二维数组中查找目标值,以及通过二分查找法寻找旋转数组中的最小数字。通过实例和代码展示了优化的搜索策略。同时讨论了使用哈希表解决字符唯一性问题和第一个只出现一次字符的查找技巧。
摘要由CSDN通过智能技术生成

今天的题开始考研算法了,题目变得有意思了许多,其中第二题的数组反转是很经典的一类例题,值得细细琢磨。

剑指 Offer 04. 二维数组中的查找

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

示例:

现有矩阵 matrix 如下:

[
  [1,   4,  7, 11, 15],
  [2,   5,  8, 12, 19],
  [3,   6,  9, 16, 22],
  [10, 13, 14, 17, 24],
  [18, 21, 23, 26, 30]
]

给定 target = 5,返回 true

给定 target = 20,返回 false

限制:

0 <= n <= 1000
0 <= m <= 1000

解法一(暴力查找)

class Solution {
public:
    bool findNumberIn2DArray(vector<vector<int>>& matrix, int target) {
        if(matrix.size() == 0)return false;
        int n = matrix.size(), m = matrix[0].size();
        for(int i = 0; i < n; ++ i){
            for(int j = 0; j < m; ++ j){
                if(matrix[i][j] == target){
                    return true;
                }
                if(matrix[i][j] > target){
                    break;
                }
            }
        }
        return false;
    }
};

算法思路:

在普通矩阵查找的基础上,逐行逐列遍历,因为行和列都有单调性,所以每一行在遍历时如果发现该元素已经大于目标值时则不必继续在这一行查询,由于单调性可以跳过这一行直接到下一行查询。这虽然用到了行递增条件,但没用到列递增这个条件,这种解法能过,但还能继续优化。

解法二(优化成线性查找)

class Solution {
public:
    bool findNumberIn2DArray(vector<vector<int>>& matrix, int target) {
        if(matrix.size() == 0)return false;
        int n = matrix.size(), m = matrix[0].size();
        if(m == 0)return false;
        int x = 0, y = m - 1;
        while(x < n && y >= 0){
            if(matrix[x][y] == target)return true;
            else if(matrix[x][y] > target)y --;
            else x ++;
        }
        return false;
    }
};

算法思路:

这次不从左上角出发,改成从右上角出发,遇到一个元素如果相同则返回true,大于目标值则左移,小于目标值则下移,当xy坐标有一越界时则返回false。下面证明一下正确性:

  • 左移(反证):假设存在目前遍历的位置在(x, y),假设此时目标值位于现在位置的右下方(x1, y1),因为遍历过程只有左移和下移,此时目标值被忽略。

    因为(x1, y1)(x, y)右下方,因此存在(x1, y)(x, y)同行,(x1, y1)同列,由题目条件可得matrix[x1][y] >matrix[x][y]matrix[x1][y]<matrix[x1][y1],当便利店到达(x1, y)时由条件得此时应该下移而不是左移,因此假设不成立。

  • 下移:与左移同理,用反证法就可以证明。

第一题结果

剑指 Offer 11. 旋转数组的最小数字

把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。例如,数组 [3,4,5,1,2][1,2,3,4,5] 的一个旋转,该数组的最小值为1。

示例 1:

输入:[3,4,5,1,2]
输出:1

示例 2:

输入:[2,2,2,0,1]
输出:0
class Solution {
public:
    int minArray(vector<int>& numbers) {
        int n = numbers.size(), ans = min(numbers[0], numbers[n - 1]);
        int l = 0, r = n - 1;
        while(l < r){
            int mid = (l + r) / 2;
            if(numbers[mid] < numbers[r]){
                r = mid;
            }else if(numbers[mid] > numbers[r]){
                l = mid + 1;
            }else r --;
        }
        return numbers[l];
    }
};

算法思路:

将一个有序序列反转变成两个序列,两个序列依旧有序,我们称两个序列为AB,前为A,后为BA可以为空,B不能为空,这道题的主要思路就是将B数组用二分法收缩,收缩到个数为1,得到结果。定义头尾指针l, r, 每次二分都会得到一个mid,当numbers[mid] < numbers[r]时,此时midBr前移到mid;当numbers[mid]>numbers[r]时,此时midA中,l后移到mid+1;当numbers[mid]==numbers[r]时,两种情况都有可能,解决的方法是r前移一位,理由是:

  • 假设此时midAr前移一位,假设B此时长度只有1r前移完长度变为0,此时变成一个有序序列,二分查找依旧可以解决问题;如果B长度大于1r前移对求B中最小值无影响。
  • 假设此时midBr前移一位,假设B此时长度只有1,此种情况只有整个序列都相等,此时r前移对结果也没影响;如果B长度大于1r前移对求B中最小值无影响。

因此通过二分的方法很快就能求出结果。

第二题结果

剑指 Offer 50. 第一个只出现一次的字符

在字符串 s 中找出第一个只出现一次的字符。如果没有,返回一个单空格。 s 只包含小写字母。

示例 1:

输入:s = "abaccdeff"
输出:'b'

示例 2:

输入:s = "" 
输出:' '

限制:

0 <= s 的长度 <= 50000
class Solution {
public:
    char firstUniqChar(string s) {
        // unordered_map<char, int> mp;
        int mp[26] = {0};
        for(auto w : s){
            mp[w - 'a'] ++;
        }
        for(auto w : s){
            if(mp[w - 'a'] == 1)return w;
        }
        return ' ';
    }
};

算法思路:

这一题就比较简单了,直接hash就可以解决,因为只有小写字母,所以开一个26大小的数组就可以存储。
第三题结果

【剑指Offer】系列:
【剑指Offer】栈
【剑指Offer】链表
【剑指Offer】字符串
【剑指Offer】查找算法

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值