• 【剑指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];
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    🍉矩阵中的路径

    剑指 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;
        }
    
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46

    🍉机器人的运动范围

    剑指 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);//先往下一直走然后回溯往右走     
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    🍅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;
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    🍉剪绳子

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

    dp,总感觉dp像找规律。

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

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

    自己列表比较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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

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

    🍉二进制中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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    🍅巧解

    一个数减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;
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
  • 相关阅读:
    【FFmpeg】ffmpeg+nginx-rtmp实现视频流转发
    MYSQL不常用但好用写法
    C. Carrying Conundrum(思维 + 奇偶数位)
    qt 在下安装闪退 退出的解决方案
    Linux 命令
    经典算法之顺序查找(Sequential Search)
    oracle11g表+数据完美迁移到10g解决方案
    前端如何写进度条(上传或者下载文件的时候)
    上周热点回顾(3.11-3.17)
    用DFS方法解决不定项列举问题
  • 原文地址:https://blog.csdn.net/m0_53005929/article/details/125621442