【剑指Offer】11-15题(二分+DFS+BFS+DP+简单位运算)

🍉旋转数组的最小数字

剑指 Offer 11. 旋转数组的最小数字 - 力扣(LeetCode)

大意:在 3 4 5 0 1 2这种数组里找出最小的数字。

类似的数组还有[1 2 3 4 5];

[2,2,2,2,2];

[6,9,11,1,2]

第一反应,找最小值,直接遍历数组不就行了。。。(剑指Offer上会有这么简单的题目吗

之后看了题解,思想是二分。可以把这种数组看做一种结构,这种结构可以分为左右数组。

以[6,9,11,1,2]为例,分为左右数组,左数组为6,9,11;右数组为1,2。我们要找的就是右数组中的第一个数。

可以看出左数组任意一个值都大于右数组,右数组中最右边的又是最大的值,所以左数组任意一个值都大于右数组最右边的值。

由此我们可以用二分,记左右两边为left和right,mid=left+(right-left)/2。

mid=(left+right)/2;和mid=left+(right-left)/2相比,显然前一种情况更容易溢出

num[right]就是右数组最右边的值。

  • 当num[mid]< num[right],因为左数组任意一个数都会大于右数组,所以mid肯定在右数组中。此时mid右边全是右数组的值,所以right=mid。

  • 当num[mid]>num[right],同理mid肯定在左数组中,所以mid左边的值肯定要舍掉,因为我们要找右数组的第一个。即left=mid+1.(注意:left=mid的写法可能导致死循环)

  • 当num[mid]==num[right]时,right–,为什么?此时我们不能判断最小值是在左数组还是右数组,比如[1,0,1,1,1]和[1,1,1,0,1]这两个数组在符合当前条件下时最小值一个在左数组,一个在右数组。此时我们的解决办法是把right–。

right–。因为 在符合条件left<right时有mid!=right,我们去掉了num[right],与它相等的num[mid]仍然在二分区间内。所以如果num[right]是我们要找的值,二分区间内也有一个和他相等的数替代它。

class Solution {
public:
    int minArray(vector<int>& numbers) {
        int left=0;
        int right=numbers.size()-1;
        while(left<right)
        {
            int mid=left+(right-left)/2;
            if(numbers[mid]<numbers[right])//mid在右区间内
            {
                right=mid;
            }
            else if(numbers[mid]>numbers[right])//mid在左区间内
            {
                left=mid+1;
            }
            else
            {
                right--;
            }
        }
        return numbers[left];
    }
};

🍉矩阵中的路径

剑指 Offer 12. 矩阵中的路径 - 力扣(LeetCode)

典型的dfs迷宫问题。四个方向去走,找到一条合理的出路即可。

class Solution {
public:
    bool exist(vector<vector<char>>& board, string word) {
        Init(board);
        for(int i=0;i<row;i++)
        {
            for(int j=0;j<col;j++)
            {
                bool ret=dfs(board,word,i,j,0);//没有规定起点是(0,0)
                if(ret)
                {
                    return ret; 
                }
            }
        }
       return false;
    }
private:
    vector<vector<bool>> visit;
    int col,row;
    void Init(vector<vector<char>>& board)
    {          
        row=board.size();//行
        col=board[0].size();//列
        visit.resize(row,vector<bool>(col)); //初始化visit
    }
    bool dfs(vector<vector<char>>& board, string& word,int i,int j,int k)
    {
        if(i>=row||i<0||j>=col||j<0||board[i][j]!=word[k]||visit[i][j])//数组越界,字母不相等,已经访问过都返回false
        {
            return false;
        }
        if(k==word.size()-1)//找完了,返回true
        {
            return true;
        }
        visit[i][j]=1;//标记走过的位置
        bool ret=dfs(board,word,i+1,j,k+1)||
        dfs(board,word,i-1,j,k+1)||
        dfs(board,word,i,j+1,k+1)||
        dfs(board,word,i,j-1,k+1);//四个方向去走
        if(ret==false) visit[i][j]=0;//走进死胡同了,回退(回溯),即取消标记的位置
        return ret;
    }

};

🍉机器人的运动范围

剑指 Offer 13. 机器人的运动范围 - 力扣(LeetCode)

🍅DFS

DFS,,但是只要两个方向。

判断为什么只需要两个方向,画图可知机器人可以到达的点呈三角形分布,则可以一直往下走,走到三角形的最下面再回溯往右走。可以看看力扣里大佬画的图,清晰明了。

class Solution {
public:
    int movingCount(int m, int n, int k) {
        visit.resize(m,vector<bool>(n));      
        return dfs(m,n,k,0,0);;
    }
private:
    vector<vector<bool>>visit;
    int cnt=0;
    int dfs(int m,int n,int k,int i,int j)
    {
        if(i==100) i=1;
        if(j==100) j=1;//题目给的数据范围里有100,对其进行特殊处理,但是这里只是因为是边界才这么处理,如果有101,102这么做就不行了。
        int ans=i%10+i/10+j%10+j/10;
        if(i<0||i>=m||j<0||j>=n||ans>k||visit[i][j])
        {
            return 0;
        }       
        visit[i][j]=1;//标记访问
        return 1+dfs(m,n,k,i+1,j)+dfs(m,n,k,i,j+1);//先往下一直走然后回溯往右走     
    }
};

🍅BFS

广度优先搜索

借助队列实现,力扣题解有大佬画的图也十分清晰易懂。

不怎么用广度优先搜索,所以不怎么熟练,代码不会的点是对着题解解决的,相当于对着题解敲了一遍。

不过这题用BFS好像慢很多。

class Solution {
public:
    int movingCount(int m, int n, int k) {
        vector<vector<bool>>visit(m,vector<bool>(n));
        int cnt=0;
        queue<vector<int>>q;
        q.push({0,0,0,0});//i,j,si,sy
        while(!q.empty())
        {
            vector<int> tmp=q.front();
            q.pop();        
            int i=tmp[0],j=tmp[1];
            int si=tmp[2],sj=tmp[3];     
            if(i>=m||j>=n||si+sj>k||visit[i][j])
            {
                continue;
            }
            visit[i][j]=1;
            cnt++;
            q.push({i+1,j,(i+1)%10==0?si-8:si+1,sj});
            q.push({i,j+1,si,(j+1)%10==0?sj-8:sj+1});
        }
        return cnt;
    }
};

🍉剪绳子

剑指 Offer 14- I. 剪绳子 - 力扣(LeetCode)

dp,总感觉dp像找规律。

记f(n)为绳子长为n时最大值,找到的公式如下

f(n)=max( f(i)*(n-i),f(i)*f(n-i))

自己列表比较n-i和f(n-i)发现,当n>=4时,始终有f(n-i)>(n-i)。所以我们再自己列出前三个数字就能进行递推了。

由上面可知当n>=4时,f(n)=f(i)*f(n-i)。

class Solution {
public:
    int cuttingRope(int n) {
        vector<int>dp(60);
        if(n==2) return 1;
        if(n==3) return 2;
        dp[1]=1;
        dp[2]=2;
        dp[3]=3;//前面这三个数字是为了后面的递推准备的
        for(int i=4;i<=n;i++)
        {
            //算dp[i]
            int ans=-1;
            for(int j=1;j<=i/2;j++)
            {
                ans=max(ans,dp[j]*dp[i-j]);
            }
            dp[i]=ans;
        }
        return dp[n];
    }
};

这题题解有大佬用贪心做,但是贪心要用数学证明可行性.

🍉二进制中1的个数

剑指 Offer 15. 二进制中1的个数 - 力扣(LeetCode)

简单位运算

class Solution {
public:
    int hammingWeight(uint32_t n) {
        int cnt=0;
        for(int i=0;i<32;i++)
        {
            if((n>>i)&1)
            {
                cnt++;
            }
        }
        return cnt;
    }
};

🍅巧解

一个数减1再&自己会把自己最右边的1变成0,操作几次相当于有几个1。

class Solution {
public:
    int hammingWeight(uint32_t n) {
        int cnt=0;
        while(n)
        {
            n=(n-1)&n;//会把这个数最右边的1变成0
            cnt++;
        }
        return cnt;
    }
};
<think>嗯,用户想了解Excel中的VLOOKUP函数的用法和参数含义。首先,我需要回忆一下VLOOKUP的基本结构。VLOOKUP是垂直查找函数,用于在表格的首列查找指定的值,然后返回该行中指定列的数据。它的四个参数分别是查找值、表格范围、列索引号和匹配模式。 接下来,我得确认每个参数的具体作用。第一个参数是lookup_value,也就是用户要查找的值,比如产品ID或者姓名。第二个参数是table_array,即查找的范围,这里要注意的是,查找值必须位于这个范围的第一列,否则函数无法正确工作。第三个参数是col_index_num,也就是用户想要返回的数据在表格范围中的第几列,这个列数是从表格范围的第一列开始算起的,不是整个工作表的列。第四个参数是range_lookup,决定是精确匹配还是近似匹配,通常用FALSE或0来指定精确匹配,避免错误。 然后,用户可能需要一个具体的例子来理解如何使用。比如,假设有一个产品价格表,用户想根据产品ID查找对应的价格。这时候,VLOOKUP就可以在ID列查找,然后返回价格列的数据。同时,要提醒用户注意表格范围最好使用绝对引用,比如$A$2:$C$100,这样在拖动公式时范围不会变化。 可能遇到的问题也需要考虑。比如,如果查找值不在表格的第一列,或者列索引号超过了表格的列数,函数会返回错误。此外,数据格式不一致也可能导致问题,比如查找值是数字,而表格中是文本格式的数字,这时候需要统一格式。还有,当出现#N/A错误时,可能是查找值不存在,或者匹配模式设置不正确,需要检查参数是否正确。 用户可能还会问到如何避免常见的错误,比如部分数据匹配出错的情况。这时候需要检查是否有多余的空格,或者数据格式不一致,或者表格范围没有锁定导致拖动时偏移。此外,XLOOKUP作为VLOOKUP的升级版,可能在某些情况下更方便,比如可以向左查找或者返回多列数据,这也是可以提及的内容。 最后,整理步骤,从基础语法到参数说明,再到具体案例,逐步引导用户理解如何使用VLOOKUP函数。同时,提供常见错误和解决方法,帮助用户在实际应用中避免问题。如果有必要,还可以比较VLOOKUP和其他查找函数的区别,比如HLOOKUP或XLOOKUP,但根据用户的问题,主要集中在VLOOKUP本身即可。</think>### Excel VLOOKUP函数详解 #### 一、基础语法与参数说明 VLOOKUP函数的语法为: $$=VLOOKUP(lookup\_value, table\_array, col\_index\_num, [range\_lookup])$$ 包含4个参数: 1. **lookup_value**(必填):要查找的值(如单元格引用或具体值) 2. **table_array**(必填):包含数据的表格范围(必须包含查找列和返回列) 3. **col_index_num**(必填):返回值所在列的序号(从table_array第一列开始计数) 4. **range_lookup**(可选):匹配类型 - `TRUE`/`1`:近似匹配(默认值,需数据升序排列) - `FALSE`/`0`:精确匹配(常用选项) [^1][^2] #### 二、使用步骤演示(工资表查询案例) 假设需要根据员工编号查询工资: 1. 建立查询单元格(如`B12`) 2. 输入公式: ```excel =VLOOKUP(A12, $A$2:$D$100, 4, 0) ``` - `A12`:待查询的员工编号 - `$A$2:$D$100`:锁定数据区域(绝对引用) - `4`:返回第4列(工资列) - `0`:精确匹配 [^2][^3] #### 三、常见错误与解决方法 | 错误现象 | 原因 | 解决方案 | |---------|------|---------| | #N/A | 查找值不存在 | 检查数据源或改用`IFERROR`容错 | | #REF! | 列序号超出范围 | 确认col_index_num ≤ 表格列数 | | 部分匹配失败 | 数据格式不一致 | 统一数值/文本格式 | | 结果错位 | 表格未锁定 | 使用`$`符号固定区域引用 | [^3][^4] #### 四、进阶技巧 1. **多条件查询**: 使用辅助列合并多个条件字段 ```excel =VLOOKUP(A2&B2, $D$2:$F$100, 3, 0) ``` 2. **通配符匹配**: `"*"`匹配任意字符,`"?"`匹配单个字符 ```excel =VLOOKUP("张*", $A$2:$C$100, 3, 0) ``` 3. **跨表查询**: 引用其他工作表数据 ```excel =VLOOKUP(A2, Sheet2!$A$2:$D$100, 4, 0) ``` [^1][^4]
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

喜欢乙醇的四氯化碳

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值