leetcode 剑指offer1-20

文章目录

  • [剑指 Offer 03. 数组中重复的数字](https://leetcode-cn.com/problems/shu-zu-zhong-zhong-fu-de-shu-zi-lcof/)
      • 哈希表
      • 原地交换
  • [剑指 Offer 04. 二维数组中的查找](https://leetcode-cn.com/problems/er-wei-shu-zu-zhong-de-cha-zhao-lcof/)
      • 模拟
  • [剑指 Offer 05. 替换空格](https://leetcode-cn.com/problems/ti-huan-kong-ge-lcof/)
      • 字符串
      • 原地修改
  • [剑指 Offer 06. 从尾到头打印链表](https://leetcode-cn.com/problems/cong-wei-dao-tou-da-yin-lian-biao-lcof/)
      • 递归
      • 辅助栈
  • [剑指 Offer 07. 重建二叉树](https://leetcode-cn.com/problems/zhong-jian-er-cha-shu-lcof/)
      • 递归+哈希表
  • [剑指 Offer 09. 用两个栈实现队列](https://leetcode-cn.com/problems/yong-liang-ge-zhan-shi-xian-dui-lie-lcof/)
      • 双辅助栈
  • [剑指 Offer 10- I. 斐波那契数列](https://leetcode-cn.com/problems/fei-bo-na-qi-shu-lie-lcof/)
      • 动态规划
  • [剑指 Offer 10- II. 青蛙跳台阶问题](https://leetcode-cn.com/problems/qing-wa-tiao-tai-jie-wen-ti-lcof/)
      • 动态规划
  • [剑指 Offer 11. 旋转数组的最小数字](https://leetcode-cn.com/problems/xuan-zhuan-shu-zu-de-zui-xiao-shu-zi-lcof/)
      • 进阶:
      • 二分查找:
  • [剑指 Offer 12. 矩阵中的路径](https://leetcode-cn.com/problems/ju-zhen-zhong-de-lu-jing-lcof/)
      • 深搜+数组
  • [剑指 Offer 13. 机器人的运动范围](https://leetcode-cn.com/problems/ji-qi-ren-de-yun-dong-fan-wei-lcof/)
      • 深搜+数组
  • [剑指 Offer 14- I. 剪绳子](https://leetcode-cn.com/problems/jian-sheng-zi-lcof/)
      • 动态规划
      • 数学推导
  • [剑指 Offer 14- II. 剪绳子 II](https://leetcode-cn.com/problems/jian-sheng-zi-ii-lcof/)
      • 数学推导
  • [剑指 Offer 15. 二进制中1的个数](https://leetcode-cn.com/problems/er-jin-zhi-zhong-1de-ge-shu-lcof/)
      • 位运算
  • [剑指 Offer 16. 数值的整数次方](https://leetcode-cn.com/problems/shu-zhi-de-zheng-shu-ci-fang-lcof/)
      • 位运算+数学
      • 递归
  • [剑指 Offer 17. 打印从1到最大的n位数](https://leetcode-cn.com/problems/da-yin-cong-1dao-zui-da-de-nwei-shu-lcof/)
      • 深度搜索
      • 枚举
  • [剑指 Offer 18. 删除链表的节点](https://leetcode-cn.com/problems/shan-chu-lian-biao-de-jie-dian-lcof/)
      • 链表+双指针
  • [剑指 Offer 19. 正则表达式匹配](https://leetcode-cn.com/problems/zheng-ze-biao-da-shi-pi-pei-lcof/)
      • 动态规划
  • [剑指 Offer 20. 表示数值的字符串](https://leetcode-cn.com/problems/biao-shi-shu-zhi-de-zi-fu-chuan-lcof/)
      • 状态向量机
      • 模拟



剑指 Offer 03. 数组中重复的数字

找出数组中重复的数字。

在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。

示例 1:

输入:
[2, 3, 1, 0, 2, 5, 3]
输出:2 或 3 

哈希表

class Solution {
public:
    int findRepeatNumber(vector& nums) {
        unordered_map mp;
        for(auto& num:nums)
        {
            mp[num]++;
            if(mp[num]>1) return num;
        }
        return 0;
    }
};

原地交换

  • 通过原地交换,降低空间复杂度

https://leetcode-cn.com/problems/shu-zu-zhong-zhong-fu-de-shu-zi-lcof/solution/mian-shi-ti-03-shu-zu-zhong-zhong-fu-de-shu-zi-yua/

leetcode 剑指offer1-20_第1张图片

class Solution {
public:
    int findRepeatNumber(vector& nums) {
        int i = 0;//初始序号
        while(i < nums.size()) {//在范围内就不断循环
            if(nums[i] == i) {//加一个判断,当索引等于对应值,索引值自加才会触发
                i++;
                continue;
            }
            if(nums[nums[i]] == nums[i])//根据索引值来找值,发生重复
                return nums[i];
            swap(nums[i],nums[nums[i]]);
        }
        return -1;
    }
};
// 作者:jyd
// 链接:https://leetcode-cn.com/problems/shu-zu-zhong-zhong-fu-de-shu-zi-lcof/solution/mian-shi-ti-03-shu-zu-zhong-zhong-fu-de-shu-zi-yua/
// 来源:力扣(LeetCode)
// 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

剑指 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>& matrix, int target) {
        int row = matrix.size();
        if(row==0) return false;
        int col = matrix[0].size();
        for(int i=row-1,j=0;i>=0&&jtarget) i--;
            else j++;
        }
        return false;
    }
};

剑指 Offer 05. 替换空格

难度简单165

请实现一个函数,把字符串 s 中的每个空格替换成"%20"。

示例 1:

输入:s = "We are happy."
输出:"We%20are%20happy."

限制:

0 <= s 的长度 <= 10000

字符串

  • 遍历模拟一遍
class Solution {
public:
    string replaceSpace(string s) {
        string res;
        for(char& ch:s)
        {
            if(ch==' ')
            {
                res+="%20";
            }
            else
                res+=ch;
        }
        return res;
    }
};

原地修改

  • 增加一点时间开销,减小空间开销,第一遍遍历统计空格数量,第二遍遍历倒序修改。
class Solution {
public:
    string replaceSpace(string s) {
        int cnt = 0;
        int size = s.size();
        //统计空格数量
        for(char ch:s)
        {
            if(ch==' ') cnt++;
        }
        s.resize(size+cnt*2);
        int nsize = s.size();
        //更改原字符串
        for(int i=size-1,j=nsize-1;i

剑指 Offer 06. 从尾到头打印链表

难度简单195

输入一个链表的头节点,从尾到头反过来返回每个节点的值(用数组返回)。

示例 1:

输入:head = [1,3,2]
输出:[2,3,1]

递归

  • 利用递归的性质,从后往前放入值,因为枚举结束后相当于返回上一层,但是枚举多了,会消耗额外的栈空间,增加空间消耗
class Solution {
private:
    vector vct;//定义向量
    void recur(ListNode* head)//递归函数
    {
        if(head==NULL) return;//边界条件,节点为空
        recur(head->next);//递归下一节点
        vct.emplace_back(head->val);//往向量中放入对应值
    }
public:
    vector reversePrint(ListNode* head) {
        recur(head);//递归,寻找头节点
        return vct;//返回向量
    }
};

辅助栈

  • 利用栈先进后出的性质,存储这些值,然后后面利用一层遍历循环,导出栈中的元素
class Solution {
public:
    vector reversePrint(ListNode* head) {
        // recur(head);
        vector vct;
        stack st;
        while(head!=NULL)//终止条件
        {
            st.push(head->val);
            head= head->next;//不断枚举
        }
        while(!st.empty())
        {
            vct.emplace_back(st.top());//取栈顶元素
            st.pop();//弹出栈顶元素
        }
        return vct;
    }   
};

剑指 Offer 07. 重建二叉树

输入某二叉树的前序遍历和中序遍历的结果,请构建该二叉树并返回其根节点。

假设输入的前序遍历和中序遍历的结果中都不含重复的数字。

示例 1:

img

Input: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]
Output: [3,9,20,null,null,15,7]

示例 2:

Input: preorder = [-1], inorder = [-1]
Output: [-1]

限制:

0 <= 节点个数 <= 5000

递归+哈希表

class Solution {
public:
    TreeNode* buildTree(vector& preorder, vector& inorder) {
        this->preorder = preorder;
        for(int i = 0; i < inorder.size(); i++)
            dic[inorder[i]] = i;
        return recur(0, 0, inorder.size() - 1);
    }
private:
    vector preorder;
    unordered_map dic;
    TreeNode* recur(int root, int left, int right) { 
        if(left > right) return nullptr;                        // 递归终止
        TreeNode* node = new TreeNode(preorder[root]);          // 建立根节点
        int i = dic[preorder[root]];                            // 划分根节点、左子树、右子树
        node->left = recur(root + 1, left, i - 1);              // 开启左子树递归
        node->right = recur(root + i - left + 1, i + 1, right); // 开启右子树递归
        return node;                                            // 回溯返回根节点
    }
};

// 作者:Krahets
// 链接:https://leetcode-cn.com/leetbook/read/illustration-of-algorithm/99ljye/
// 来源:力扣(LeetCode)
// 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

剑指 Offer 09. 用两个栈实现队列

用两个栈实现一个队列。队列的声明如下,请实现它的两个函数 appendTail 和 deleteHead ,分别完成在队列尾部插入整数和在队列头部删除整数的功能。(若队列中没有元素,deleteHead 操作返回 -1 )

示例 1:

输入:
["CQueue","appendTail","deleteHead","deleteHead"]
[[],[3],[],[]]
输出:[null,null,3,-1]

提示:

  • 1 <= values <= 10000
  • 最多会对 appendTail、deleteHead 进行 10000 次调用

双辅助栈

  • 一个栈模拟入队列,另一个栈模拟出队列

栈st1模拟入队列,栈st2模拟出队列,当st2中没有元素的时候,再将st1中的元素装填到st2中,如果在st2中有元素的时候将st1的元素装填到st2中,就会导致st2栈顶的元素不是队首元素。

class CQueue {
private:
    stack st1,st2;//入队列,出队列
public:
    CQueue() {

    }
    
    void appendTail(int value) {
        st1.push(value);//入队列
    }
    
    int deleteHead() {
        if(st2.empty())//模拟出队列为空
        {
            if(st1.empty()) return -1;//模拟入队列为空
            while(!st1.empty())//将st1中的元素装到st2中
            {
                int cur = st1.top();
                st1.pop();
                st2.push(cur);
            }
        }
        int cur = st2.top();
        st2.pop();
        return cur;
    }
};

剑指 Offer 10- I. 斐波那契数列

写一个函数,输入 n ,求斐波那契(Fibonacci)数列的第 n 项(即 F(N))。斐波那契数列的定义如下:

F(0) = 0,   F(1) = 1
F(N) = F(N - 1) + F(N - 2), 其中 N > 1.

斐波那契数列由 0 和 1 开始,之后的斐波那契数就是由之前的两数相加而得出。

答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。

示例 1:

输入:n = 2
输出:1

示例 2:

输入:n = 5
输出:5

动态规划

  • 斐波那契经典题
class Solution {
public:
    int fib(int n) {
        if(n<2) return n;
        const int MOD = 1e9+7;
        vector F(3,0);
        F[1] = 1;
        for(int i=1;i

剑指 Offer 10- II. 青蛙跳台阶问题

一只青蛙一次可以跳上1级台阶,也可以跳上2级台阶。求该青蛙跳上一个 n 级的台阶总共有多少种跳法。

答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。

示例 1:

输入:n = 2
输出:2

示例 2:

输入:n = 7
输出:21

示例 3:

输入:n = 0
输出:1

提示:

  • 0 <= n <= 100

动态规划

  • 斐波那契经典题
class Solution {
public:
    int climbStairs(int n) {
        vector F(3,0);//构造初始数组
        F[1] = 1;//初始值定义
        F[2] = 2;
        if(n<3) return F[n];//n如果小于三就返回初始值
        for(int i=3;i<=n;i++)//遍历更新,当前阶的跳法等于前一阶和前前阶的和
        {
            F[0] = F[1];//原先的数组中的值左移,挪出2来存储最近的结果
            F[1] = F[2];
            F[2] = F[0]+F[1]; 
        }
        return F[2];//我们要的结果必然存储在F[2]
    }
};

剑指 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

进阶:

这道题是 寻找旋转排序数组中的最小值 的延伸题目。
允许重复会影响算法的时间复杂度吗?会如何影响,为什么?

答:允许重复会影响时间复杂度,重复的时候,需要遍历比当前序号小1的序号,如果重复元素很多的话,会近似为O(n)算法,因为需要从右到左逐个比较,避免遗漏掉最小值。

二分查找:

class Solution {
public:
    int findMin(vector& numbers) {
        int size = numbers.size();
        int left = 0,right = size-1;
        while(leftnumbers[right])
            {
                left = mid+1;
            }
            else if(numbers[mid]

剑指 Offer 12. 矩阵中的路径

给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中,返回 true ;否则,返回 false 。

单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。

例如,在下面的 3×4 的矩阵中包含单词 “ABCCED”(单词中的字母已标出)。

示例

示例 1:

输入:board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "ABCCED"
输出:true

提示:

  • 1 <= board.length <= 200

  • 1 <= board[i].length <= 200

  • boardword 仅由大小写英文字母组成

深搜+数组

  • 需要辅助标记数组,利用深搜,往四个方向寻找,深搜的时候需要判断是否在范围之内
class Solution {
private:
    vector> isVisited;//标记数组,表示是否访问过
    vector> board;
    string s;
    int size;
    int row;
    int col;
    bool dfs(int i,int j,int index)
    {
        if(index==size-1)//边界条件
        {   
            return true;
        }
        if(i-1>=0&&isVisited[i-1][j]==0&&board[i-1][j]==s[index+1])//往上
        {
            isVisited[i-1][j]=1;
            if(dfs(i-1,j,index+1)==true) return true;
            isVisited[i-1][j]=0;
        }
        if(i+1=0&&isVisited[i][j-1]==0&&board[i][j-1]==s[index+1])//往左
        {
            isVisited[i][j-1]=1;
            if(dfs(i,j-1,index+1)==true) return true;
            isVisited[i][j-1]=0;
        }
        if(j+1>& board, string word) {
        row = board.size();
        col = board[0].size();
        s = word;
        size = word.size();
        this->board = board;
        for(int i=0;i>(row,vector(col,0));
                if(word[0]==board[i][j])
                {
                    isVisited[i][j] = 1;
                    if(dfs(i,j,0)==true)
                        return true;
                    isVisited[i][j] = 0;
                }
            }
        }
        return false;
    }
};

剑指 Offer 13. 机器人的运动范围

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

示例 1:

输入:m = 2, n = 3, k = 1
输出:3

提示:

  • 1 <= n,m <= 100
  • 0 <= k <= 20

深搜+数组

  • 需要辅助标记数组,利用深搜,往两个方向,向右和向下寻找,深搜的时候需要判断是否在范围之内,以及是否满足题目要求的条件
class Solution {
private:
    vector> isVisited;//标记访问与否
    int k,m,n;
    int res = 0;
    bool check(int x,int y)
    {
        int ans = 0;
        while(x)
        {
            ans+=x%10;
            x/=10;
        }
        while(y)
        {
            ans+=y%10;
            y/=10;
        }
        return ans>k?false:true;
    }
    void dfs(int x,int y)
    {
        //深搜的时候需要判断范围,是否访问过,以及是否数位之和小于等于k
        if(x+1>(m,vector(n,0));
        this->k = k;
        this->m = m;
        this->n = n;
        if(check(0,0))
        {
            isVisited[0][0] = 1;
            dfs(0,0);
            res++;
        }
        return res;
    }
};

剑指 Offer 14- I. 剪绳子

给你一根长度为 n 的绳子,请把绳子剪成整数长度的 m 段(m、n都是整数,n>1并且m>1),每段绳子的长度记为 k[0],k[1]…k[m-1] 。请问 k[0]k[1]…*k[m-1] 可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。

示例 1:

输入: 2
输出: 1
解释: 2 = 1 + 1, 1 × 1 = 1

示例 2:

输入: 10
输出: 36
解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36

提示:

  • 2 <= n <= 58

动态规划

动态规划,转移方程:dp[j] = max(dp[j],max((j-i)*i,dp[j-i]*i))

class Solution {
public:
    int cuttingRope(int n) {
        vector dp(n+1,0);
        for(int i=1;i

数学推导

https://leetcode-cn.com/problems/jian-sheng-zi-ii-lcof/solution/mian-shi-ti-14-ii-jian-sheng-zi-iitan-xin-er-fen-f/

结论:长度4以下打表,长度如果大于4,就尽量按长度3来切割这样子乘积有最大值

class Solution {
public:
    int integerBreak(int n) {
        if(n==2) return 1;
        else if(n==3) return 2;
        else if(n==4) return 4;
        // const int MOD = 1e9+7;/
        long res = 1;
        while(n>4)
        {
            n-=3;
            res=res*3;
        }
        if(n==2) return (int)(res*2);
        else if(n==3) return (int)(res*3);
        else if(n==4) return (int)(res*4);
        return 0;
    }
};

剑指 Offer 14- II. 剪绳子 II

给你一根长度为 n 的绳子,请把绳子剪成整数长度的 m 段(m、n都是整数,n>1并且m>1),每段绳子的长度记为 k[0],k[1]…k[m - 1] 。请问 k[0]k[1]…*k[m - 1] 可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。

答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。

示例 1:

输入: 2
输出: 1
解释: 2 = 1 + 1, 1 × 1 = 1

提示:

  • 2 <= n <= 1000

数学推导

https://leetcode-cn.com/problems/jian-sheng-zi-ii-lcof/solution/mian-shi-ti-14-ii-jian-sheng-zi-iitan-xin-er-fen-f/

结论:长度4以下打表,长度如果大于4,就尽量按长度3来切割这样子乘积有最大值

class Solution {
public:
    int integerBreak(int n) {
        if(n==2) return 1;
        else if(n==3) return 2;
        else if(n==4) return 4;
        // const int MOD = 1e9+7;/
        long res = 1;
        while(n>4)
        {
            n-=3;
            res=res*3;
        }
        if(n==2) return (int)(res*2);
        else if(n==3) return (int)(res*3);
        else if(n==4) return (int)(res*4);
        return 0;
    }
};

剑指 Offer 15. 二进制中1的个数

编写一个函数,输入是一个无符号整数(以二进制串的形式),返回其二进制表达式中数字位数为 ‘1’ 的个数(也被称为 汉明重量).)。

提示:

  • 请注意,在某些语言(如 Java)中,没有无符号整数类型。在这种情况下,输入和输出都将被指定为有符号整数类型,并且不应影响您的实现,因为无论整数是有符号的还是无符号的,其内部的二进制表示形式都是相同的。
  • 在 Java 中,编译器使用 二进制补码 记法来表示有符号整数。因此,在上面的 示例 3 中,输入表示有符号整数 -3

示例 3:

输入:n = 4294967293 (控制台输入 11111111111111111111111111111101,部分语言中 n = -3)
输出:31
解释:输入的二进制串 11111111111111111111111111111101 中,共有 31 位为 '1'。

位运算

数1的个数

class Solution {
public:
    int hammingWeight(uint32_t n) {
        int res = 0;
        while(n!=0)
        {
            if((n&1)==1) res++;
            n>>=1;
        }
        return res;
    }
};

剑指 Offer 16. 数值的整数次方

实现 pow(x, n) ,即计算 x 的 n 次幂函数(即,xn)。不得使用库函数,同时不需要考虑大数问题。

示例 1:

输入:x = 2.00000, n = 10
输出:1024.00000

示例 2:

输入:x = 2.10000, n = 3
输出:9.26100

位运算+数学

class Solution {
    public double myPow(double x, int n) {
        if(n==0) return 1;//任何数的0次方都是0
        long b = n;//因为取绝对值可能会越界
        if(b<0)//幂为负,先取倒数
        {
            x = 1/x;
            b = -b;
        }
        double res = 1;//结果
        while(b>0)//直到把幂次用完
        {
            if((b&1)==1) res*=x;//当前位还有数,就表示要多乘以一个x
            b = (b>>1);//当最低位没了1,右移,将x扩大一倍,加快运算
            x*=x;//x扩大一倍
        }
        return  res;
    }
}

递归

当n=0,n=1,n=-1的时候判断一下,并且要额外对n取一下余数,避免漏乘

class Solution {
public:
    double myPow(double x, int n) {
        if(n==0) return 1;
        else if(n==1) return x;
        else if(n==-1) return 1/x;
        double num = myPow(x,n/2);
        return num*num*myPow(x,n%2);
    }
};

剑指 Offer 17. 打印从1到最大的n位数

输入数字 n,按顺序打印出从 1 到最大的 n 位十进制数。比如输入 3,则打印出 1、2、3 一直到最大的 3 位数 999。

示例 1:

输入: n = 1
输出: [1,2,3,4,5,6,7,8,9]

深度搜索

深搜每一位数对应的情况

class Solution {
private:
    vector res;
    void DFS(int n, int count, string record)
    {
        if (count == n)
        {
            if (record.front() != '0')  res.push_back(record);
            return;
        }
        for (int index = 0; index <= 9; index++)
        {
            if (record.size() && record.front() == '0')
            {
                DFS(n, count + 1, to_string(index));
            }
            else
            {
                DFS(n, count + 1, record + to_string(index));
            }
        }
    }
public:
    vector printNumbers(int n) {
        DFS(n, 0, "");
        vector intRes;
        for (vector::iterator iter = res.begin(); iter != res.end(); iter++)
        {
            intRes.push_back(stoi(*iter));
        }
        return intRes;
    }
};

// 作者:Coca Cola
// 链接:https://leetcode-cn.com/leetbook/read/illustration-of-algorithm/5912jv/?discussion=q7DffZ
// 来源:力扣(LeetCode)
// 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

枚举

一轮枚举所有情况

class Solution {
public:
    vector printNumbers(int n) {
        vector res;
        int maxN = 1;
        for(int i=0;i

剑指 Offer 18. 删除链表的节点

给定单向链表的头指针和一个要删除的节点的值,定义一个函数删除该节点。

返回删除后的链表的头节点。

**注意:**此题对比原题有改动

示例 1:

输入: head = [4,5,1,9], val = 5
输出: [4,1,9]
解释: 给定你链表中值为 5 的第二个节点,那么在调用了你的函数之后,该链表应变为 4 -> 1 -> 9.

链表+双指针

  • 首先确定删的是否是头节点,如果是头节点就直接返回头节点后的节点
  • 假如不是头节点,就要记录前置节点,辅助删除节点
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* deleteNode(ListNode* head, int val) {
        if(head->val==val) return head->next;
        ListNode* node = head->next;
        ListNode* pre = head;
        while(node!=NULL)
        {
            if(node->val==val)
            {
                pre->next = node->next;
                break;
            }
            pre = node;
            node = node->next;
        }
        return head;
    }
};

剑指 Offer 19. 正则表达式匹配

请实现一个函数用来匹配包含'. ''*'的正则表达式。模式中的字符'.'表示任意一个字符,而'*'表示它前面的字符可以出现任意次(含0次)。在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a""ab*ac*a"匹配,但与"aa.a""ab*a"均不匹配。

示例 1:

输入:
s = "aa"
p = "a"
输出: false
解释: "a" 无法匹配 "aa" 整个字符串。

示例 2:

输入:
s = "aa"
p = "a*"
输出: true
解释: 因为 '*' 代表可以匹配零个或多个前面的那一个元素, 在这里前面的元素就是 'a'。因此,字符串 "aa" 可被视为 'a' 重复了一次。
  • s 可能为空,且只包含从 a-z 的小写字母。

  • p 可能为空,且只包含从 a-z 的小写字母以及字符 .*,无连续的 '*'

动态规划

https://leetcode-cn.com/problems/regular-expression-matching/solution/liang-chong-shi-xian-xiang-xi-tu-jie-10-48bgj/

class Solution {
private:
  bool match(char a,char b)//匹配字符
  {
      if(a==b||b=='.') return true;//a==b或者模板串对应的字符是.就说明匹配
      else return false;//否则不匹配
  }
public:
  bool isMatch(string s, string p) {
      int sSize = s.size();//字符串s的长度
      int pSize = p.size();//字符串p的长度
      vector> dp(sSize+1,vector(pSize+1,0));//动态规划向量,初始化为全零向量
      dp[0][0] = 1;//匹配空串返回1
      for(int i=0;i<=sSize;i++)//枚举s中的每个字符
      {
          for(int j=1;j<=pSize;j++)//枚举p中的每个字符
          {
              if(p[j-1]=='*')//假如第j个字符是*号
              {
                  dp[i][j] |= dp[i][j-2];//更新dp状态
                  if(i>0&&match(s[i-1],p[j-2]))//匹配s的第i个字符和p的第j-1个字符,假如两者一致,就更新dp值
                  {
                      dp[i][j] |= dp[i-1][j];
                  }
              }else
              {
                  if(i>0&&match(s[i-1],p[j-1]))
                  {
                      dp[i][j] |= dp[i-1][j-1];
                  }
              }
          }
      }
      return dp[sSize][pSize];//返回右下角最后一个状态,就是所要的结果
  }
};

剑指 Offer 20. 表示数值的字符串

请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。

数值(按顺序)可以分成以下几个部分:

  1. 若干空格
  2. 一个 小数 或者 整数
  3. (可选)一个 'e''E' ,后面跟着一个 整数
  4. 若干空格

小数(按顺序)可以分成以下几个部分:

  1. (可选)一个符号字符('+''-'
  2. 下述格式之一:
    1. 至少一位数字,后面跟着一个点 '.'
    2. 至少一位数字,后面跟着一个点 '.' ,后面再跟着至少一位数字
    3. 一个点 '.' ,后面跟着至少一位数字

整数(按顺序)可以分成以下几个部分:

  1. (可选)一个符号字符('+''-'
  2. 至少一位数字

部分数值列举如下:

  • ["+100", "5e2", "-123", "3.1416", "-1E-16", "0123"]

部分非数值列举如下:

  • ["12e", "1a3.14", "1.2.3", "+-5", "12e+5.4"]

示例 1:

输入:s = "0"
输出:true

状态向量机

  • 需要列出状态转移方程
class Solution {
public:
    bool isNumber(string s) {
        // 利用状态机
        int state = 0;//表示状态,初始状态为0
        for(char& ch:s)//枚举每一个字符
        {
            switch(state){
                case 0://初始状态,
                if(ch==' ') state = 1;//输入空格前往状态1
                else if(ch=='+'||ch=='-') state = 2;//输入正负号前往状态2
                else if(ch>='0'&&ch<='9') state = 3;//输入0到9前往状态3
                else if(ch=='.') state = 10;//输入小数点,前往状态10
                else return false;//出现了其它输入,判错
                break; 
                case 1:
                if(ch==' ') state = 1;//输入空格保持原样
                else if(ch=='+'||ch=='-') state = 2;//输入正负号前往状态2
                else if(ch>='0'&&ch<='9') state = 3;//输入0到9前往状态3
                else if(ch=='.') state = 10;//输入小数点,前往状态10
                else return false;//出现了其它输入,判错
                break; 
                case 2:
                if(ch>='0'&&ch<='9') state = 3;//输入0到9前往状态3
                else if(ch=='.') state = 10;//输入小数点,前往状态10
                else return false;//出现了其它输入,判错
                break; 
                case 3:
                if(ch>='0'&&ch<='9') state = 3;//输入0到9保持原样
                else if(ch=='.') state = 4;//输入小数点前往状态4
                else if(ch=='e'||ch=='E') state = 6;//输入E,前往状态6
                else if(ch==' ') state = 9;//输入空格前往状态9
                else return false;//出现了其它输入,判错 
                break; 
                case 4:
                if(ch>='0'&&ch<='9') state = 5;//输入0到9前往状态5
                else if(ch=='e'||ch=='E') state = 6;//输入E,前往状态6
                else if(ch==' ') state = 9;//输入空格,前往状态9
                else return false;//出现其他输入,判错
                break; 
                case 5:
                if(ch>='0'&&ch<='9') state = 5;//输入0到9保持原样
                else if(ch=='e'||ch=='E') state = 6;//输入E,前往状态6
                else if(ch==' ') state = 9;//输入空格前往状态9
                else return false;//出现其他输入,判错
                break; 
                case 6:
                if(ch>='0'&&ch<='9') state = 7;//输入0到9前往状态7
                else if(ch=='+'||ch=='-') state = 8;//输入正负号前往状态8
                else return false;//出现其他输入,判错
                break; 
                case 7:
                if(ch>='0'&&ch<='9') state = 7;//输入0到9保持原样
                else if(ch==' ') state = 9;//输入空格前往状态9
                else return false;//出现其他输入,判错
                break;
                case 8:
                if(ch>='0'&&ch<='9') state = 7;//输入0到9前往状态7
                else return false;//出现其他输入,判错
                break; 
                case 9:
                if(ch==' ') state = 9;//输入空格保持原样
                else return false;//出现其他输入,判错
                break; 
                case 10:
                if(ch>='0'&&ch<='9') state = 11;//输入0到9前往状态11
                // else if(ch=='e'||ch=='E') state = 15;//输入E,前往状态15
                else return false;//出现其他输入,判错
                break; 
                case 11:
                if(ch>='0'&&ch<='9') state = 11;//输入0到9保持原样
                else if(ch==' ') state = 9;//输入空格前往状态9
                else if(ch=='e'||ch=='E') state = 12;//输入E,前往状态12
                else return false;//出现其他输入,判错
                break; 
                case 12:
                if(ch>='0'&&ch<='9') state = 13;//输入0到9前往状态13
                else if(ch=='+'||ch=='-') state = 13;//输入正负号前往状态13
                else return false;//出现其他输入,判错
                break; 
                case 13:
                if(ch>='0'&&ch<='9') state = 13;//输入0到9保持不变
                else if(ch==' ') state = 14;//输入空格前往状态14
                else return false;//出现其他输入,判错
                break; 
                case 14:
                if(ch==' ') state = 14;//输入空格保持不变
                else return false;//出现其他输入,判错
                break; 
                default:
                break;
            }
        }
        if(state==3||state==4||state==5||state==7||state==9||state==11||state==13||state==14) return true;//假如最后的状态是3,说明结尾是一个数字,假如最后的状态是4,说明是一个小数点,假如最后的状态是5,说明结尾是个数字,并且是小数,假如最后的状态为7,就说明是E后面跟了整数,假如最后状态为9,就说明结尾是空格,假如最后状态为11说明是“.数字”的格式
        return false;//否则说明不是
    }
};

模拟

  • 模拟题目要求的流程,依次进行情况判断
class Solution {
public:
    int integer(string& s, int n) {
        while (s[n] != '\0') {
            if (s[n] < '0' || s[n] > '9') break;
            n ++;
        }
        // n指向下一个
        return n;
    }

    int integerAOrDecimal(string& s, int n){
        int n_orig1 = n;
        n = integer(s, n);
        int n_vary1 = n;
        // 判断是否是整数
        if (s[n] != '.'){
            return n_vary1 == n_orig1 ? -1 : n_vary1;
        }
        // 判断是否是小数
        // 跨过小数点
        n ++;
        int n_orig2 = n;
        n = integer(s, n);
        int n_vary2 = n;
        if (n_orig1 == n_vary1 && n_orig2 == n_vary2) return -1;
        return n_vary2;
    }

    bool isNumber(string s) {
        int n = 0;
        // 处理前面的若干空格
        while (s[n] == ' ') n ++;
        // 跨过正负号
        if (s[n] == '+' || s[n] == '-') n ++;
        // 判断小数或者整数
        n = integerAOrDecimal(s, n);
        if (n == -1) return false;
        // 可选是不是存在e\E
        if (s[n] == 'e' || s[n] == 'E') {
            n ++;
            if (s[n] == '+' || s[n] == '-') n ++;
            int n_orig = n;
            n = integer(s, n);
            if (n_orig == n) return false;
        }
        // 判断结尾空格
        while (s[n] != '\0') {
            if (s[n] != ' ') return false;
            n ++;
        }
        return true;
    }
};

你可能感兴趣的:(c/c++,算法设计,leetcode,算法)