C++牛客网剑指Offer学习笔记(一)

博主分享了算法学习的进度,提交论文后专注于算法,包括数组中重复数字、二分查找、矩阵路径、字符串处理、动态规划等问题的解法,并记录了牛客网刷题的体验。此外,还讨论了栈与队列、链表操作、二进制数的处理等数据结构问题。

论文终于提交了😭,接下来可以好好的学习算法啦,之前看书的时间相对比较少,接下来在博客中打卡。
📆 陪跑秋招。

声明

  • 本博文为”恒心“的学习笔记,引用劳烦注明出处。
  • 代码参考《剑指Offer》这本书,目前处于初学阶段,读者请慎重阅读。
  • 刷题的平台是牛客网。
  • 题目的序号按照剑指Offer的顺序进行排序

这里先罗列一下上周的笔记,后期会慢慢。

牛客网真是一个刷题的好地方,强行案例一波。 接下来每天都会将做题笔记更新在博客上。
在练习模式下,可以Debug 对新手太友好了。

正文

3 数组中重复的数字

描述

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

示例1

输入:

[2,3,1,0,2,5,3]

返回值:

2

说明:

2或3都是对的

数组中重复的数字

bool duplicate(int numbers[], int length, int* duplication) {
    // 防止输入是空指针,或者数组长度为0的情况
    if (numbers == nullptr || length <= 0) 
    {
        return false;
    }
    // 当数组中出现不在范围的数字 直接返回false
    for (int i = 0; i < length; i++) 
    {
        if (numbers[i] > length - 1 || numbers[i] < 0) 
        {
            return false;
        }
    }
    // 判断数组中是否有重复的数字,采用小标交换法
    for (int i = 0; i < length; i++)
    {
        //直到当前位置和数组下标值对应下则移动 
        while (numbers[i] != i )
        {
            if (numbers[i] == numbers[numbers[i]])
            {
                // 如果后面出现重复的,则返回重复的数字
                *duplication = numbers[i];
                return true;
            }
            // 没有出现重复的,不断进行交换
            int temp = numbers[i];
            numbers[i] = numbers[temp];
            numbers[temp] = temp;
        }
    }
    return false;
}
解题的难点:
  • 因为传入的参数比较多,需要做好非法判断

  • 判断数组中是否有重复的数字,采用的是下标交换法,这个画图可以解决

牛客网的刷题

https://www.nowcoder.com/practice/6fe361ede7e54db1b84adc81d09d8524

int duplicate(vector<int>& numbers) {
    // write code here
    if(numbers.size() <1)
        return -1;
    for(int i=0;i<numbers.size();i++)
    {
        while(i!=numbers[i])
        {
            if(numbers[i] == numbers[numbers[i]])
            {
                return numbers[i];
             }
            int temp = numbers[i];
            numbers[i] = numbers[temp];
            numbers[temp] =temp;
        }
    }
    return -1;
}

运行时间:6ms

占用内存:780KB

改进

师兄提供了两个比较妙的解法:

方一 哈希表 开辟空间

bool duplicate(int numbers[], int length, int* duplication) {
    vector<bool> result(length,false);
    for (int i = 0; i < length; ++i) {
        if (result[numbers[i]] == false) {
            result[numbers[i]] = true;
        }
        else
        {
            duplication[0] = numbers[i];
            return true;
        }
    }
    return false;
    }

方二 原地做法

bool duplicate(int numbers[], int length, int* duplication) {
    for(int i = 0;i < length; ++i){//这个方法妙在对于依次遍历过的每个数,都能在数组里记忆它出现过了。
        //比如{2,2,1,0},第一次循环index = 2,a[2]=a[2] + 4 = 5,这样,a[2]=5 > 数组长度4,就说明2这个数字出现过了。
        int index = numbers[i]%length;
        if( numbers[index] >= length){
            duplication[0] = index;
            return true;
        }
        numbers[index] += length;
    }

    return false;
}

不可以修改Number 数组

注意空间复杂度的解法

解题思路:

利用n个数字 数字范围只有1~n-1这个特性,采用二分法 统计每个区间的数量

范围是数字的范围 与当前数组位置的值无关

int countRange(const int* numbers, int start, int middle, int length)
{
    int count = 0;
    if (numbers == nullptr || length <= 0)
        return count;
    for (int i = 0; i < length; i++)
    {
        if (numbers[i] <= middle && numbers[i] >= start)
            ++count;
    }
    return count;
}
int getDuplication(const int* numbers, int length) {
    // 空指针以及数组长度为0的情况
    if (numbers == nullptr || length <= 0)
        return -1;
    // 由于n+1长度,所有的数字都在1~n范围,所以根据提题意必然有重复的
    // 二分查找法
    int start = 1;
    int end = length - 1;

    while (end >= start) 
    {
        // 位运算 >> 除以2的n次方 注意括号的运算顺序
        int middle = ((end - start) >> 1 ) + start;
        int count = countRange(numbers,start,middle,length);
        if (start == end)
        {
            if (count > 1)
                return start;
            else
                break;
        }

        if (count > (middle - start + 1))
            end = middle;
        else
            start = middle + 1;
    }
    return -1;
}

题4 二维数组中的查找

运行时间:11ms超过29.44% 用C++提交的代码

占用内存:1808KB超过24.52%用C++提交的代码

bool Find(int* matrix, int rows, int columns, int number)
{
    // 选择一个可以起到约束的作用的位置,左上角或者右上角
    int row = 0;
    int colum = columns - 1;

    // 判断非法条件:矩阵为空,列和行为空
    if (matrix != nullptr && rows > 0 && columns > 0)
    {
        while (row < rows && colum >=0)
        {
            // 二维写成一维的形式理解
            if (matrix[row * columns + colum] == number) {
                return true;
            }
            else if (matrix[row * columns + colum] < number)
                row = row + 1;
            else
                colum = colum - 1;
        }
    }

    return false;
}

思路

选择一个可以约束的位置,一般这种在左上角和右上角可以找到。

一维指针的二维写法

题5 替换空格

题6 从头到尾打印列表

题7 重建二叉树

8 二叉树的下一个节点

9 用两个栈来实现队列

10 斐波那契额数列

斐波那契

class Solution {
public:
    int Fibonacci(int n) {
        //因为题目定的是int 不是long long 因此要思考复杂度的问题
        int  fibResult = 0;
        int  fib1 = 0;
        int  fib2 = 1;
        if(n == 0)
            return 0;
        if(n == 1)
            return 1;
        for(int i=2;i<=n;i++){
            fibResult = fib1 + fib2;
            fib1 = fib2;
            fib2 = fibResult;
        }
        return fibResult;
    }
};

占用内存:528KB超过60.20%用C++提交的代码

运行时间:3ms超过39.46% 用C++提交的代码

青蛙跳台阶

运行时间:313ms超过29.58% 用C++提交的代码

占用内存:528KB超过65.44%用C++提交的代码

class Solution {
public:
    int jumpFloor(int number) {
        // 求一共有多少种跳法
        // 要么跳一级剩下j(n-1),要么两级剩下j(n-1)
        // 直到需要跳的阶数为0
        int jumpNum = 0;
        if(number<0)
            return 0;
        if(number==0)
            return 1;
        jumpNum = jumpFloor(number-1) + jumpFloor(number-2);
        return jumpNum;
    }
};

面试题11 旋转数组最小的数字

描述

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

示例1

输入:

[3,4,5,1,2]

复制

返回值:

1

一开始做的时候,还是做错了

运行时间:7ms超过94.97% 用C++提交的代码

占用内存:880KB超过33.20%用C++提交的代码

[5,1,2,5,5,5,5,5,5]

犯错…

        if(rotateArray[startIndex] == rotateArray[endIndex]
          == rotateArray[midIndex])
class Solution {
public:
    int minNumberInRotateArray(vector<int> rotateArray) {
        if(rotateArray.size()==0)
            return 0;
        // 用两个标记位置标记开始和结束
        // 一般情况下 利用二分查找法,当两个位置的中间值大于起始位置移动开始位置到中间
        // 反之右边移动
        // 如果差值为1,这输出后面的位置
        // 考虑到一种情况 就是第一个位置的问题 indexMid = index
        int startIndex = 0;
        int endIndex = rotateArray.size()-1;
        // 如果第一个就是我们需要查找的则直接返回
        int midIndex = startIndex;
        // rotateArray[startIndex] >= rotateArray[endIndex] 确保旋转
        while(rotateArray[startIndex] >= rotateArray[endIndex]){
            midIndex = int((startIndex + endIndex) / 2);
            // 针对特殊的例子,采用特殊的解法
            if(rotateArray[startIndex] == rotateArray[endIndex]
              && rotateArray[startIndex] == rotateArray[midIndex])
                return minNumberR(rotateArray,startIndex,endIndex);
             
            if(rotateArray[midIndex] >=rotateArray[startIndex])
                startIndex = midIndex;
            else if(rotateArray[midIndex]<= rotateArray[endIndex])
                endIndex = midIndex;
            if(endIndex - startIndex == 1){
                midIndex = endIndex;
                 return  rotateArray[midIndex];
            }
        }
        return rotateArray[midIndex];
    }
    
   int minNumberR(vector<int> &rotateArray, int left, int right){
       int result = rotateArray[left];
       for(int i = left + 1; i < right;++i)
           if(rotateArray[i]<result){
              result = rotateArray[i];
           }
       return result;
   }
};

矩阵中的路径

描述

请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。如果一条路径经过了矩阵中的某一个格子,则该路径不能再进入该格子。 例如

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ys2uEvv9-1629620736843)(image/README/image/image-20210815193700324.png)]

矩阵中包含一条字符串"bcced"的路径,但是矩阵中不包含"abcb"路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入该格子。

示例1

输入:

[[a,b,c,e],[s,f,c,s],[a,d,e,e]],"abcced"

返回值:

true

示例2

输入:

[[a,b,c,e],[s,f,c,s],[a,d,e,e]],"abcb"

返回值:

false

备注:

0 <= matrix.length <= 200
0 <= matrix[i].length <= 200

始终过不了这个例子

[[A,B,C,E,H,J,I,G],[S,F,C,S,L,O,P,Q],[A,D,E,E,M,N,O,E],[A,D,I,D,E,J,F,M],[V,C,E,I,F,G,G,S]],“SGGFIECVAASABCEHJIGQEM”

class Solution {
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param matrix char字符型vector<vector<>> 
     * @param word string字符串 
     * @return bool布尔型
     */
    bool hasPath(vector<vector<char> >& matrix, string word) {
        // 临界条件的判断
        if(matrix.empty() || matrix[0].empty() || word.length()<=0){
            return false;
        }
        // 获取行号用于遍历
        int rows = matrix.size();
        int cols = matrix[0].size();
        int pathLength = 0;
        // 标记位置
        bool  *visited = new bool[rows * cols];
        // 这里必须要要对访问的数组初始化 否则调试结结果和运行结果不一致
        memset(visited,0,rows*cols);
        // 对于每一个位置进行是否为路径的判断
        for(int row = 0; row<rows; ++row){
            for(int col = 0; col<cols; ++col){
                // 如果最终在矩阵中找到
                if(hasCorePath(matrix,rows,cols,row,col,pathLength,word,visited)){
                    return true;
                }
            }
        }
        
        delete[] visited;
        return false;
    }
    bool hasCorePath(vector<vector<char> >& matrix,
                    int rows, int cols, int row, int col,
                    int &length,const string &word, bool *visit){
        if(word[length] == '\0'){
            return true;
        }
        bool comRound = false;
        // 如果行数和列数符合要求同时当前位置与当前目标的值相等,长度--
        // 且没有被访问过的
        if(col>=0 && row>=0 
          && col<cols && row<rows
          && !visit[row*cols + col]
          && matrix[row][col] ==  word[length]){
            length++;
            // 判断完后 设置已访问
            visit[row*cols + col] =true;
            // 左上右下
            comRound = hasCorePath(matrix,rows,cols,row,col-1,length,word,visit)
            || hasCorePath(matrix,rows,cols,row-1,col,length,word,visit)
            || hasCorePath(matrix,rows,cols,row,col+1,length,word,visit)
            || hasCorePath(matrix,rows,cols,row+1,col,length,word,visit);     
            // 回溯
            if(!comRound){
                visit[row*cols + col] =false;
                length--;
            }
        }
        return comRound;
    } 
};

运行时间5ms

占用内存648KB

这里有一个巨坑!!!

漏了初始化,memset()

    // 这里必须要要对访问的数组初始化 否则调试结结果和运行结果不一致
    memset(visited,0,rows*cols);
class Solution {
public:
  bool hasPath(vector<vector<char> >& board, string word) {
    const int m = board.size(), n = board[0].size();
    for (int y = 0; y < m; ++y)
      for (int x = 0; x < n; ++x)
        if (search(board, m, n, x, y, 0, word))
          return true;
     
    return false;
  }
   
private:
  bool search(vector<vector<char>>& board, int m, int n, int x, int y, int p, const string& word) {
    if (x < 0 || x == n || y < 0 || y == m || board[y][x] != word[p])
      return false;
     
    if (p == word.length() - 1)
      return true;
     
    const char t = board[y][x];
    board[y][x] = 0;
    bool found = search(board, m, n, x - 1, y, p + 1, word) ||
                 search(board, m, n, x + 1, y, p + 1, word) ||
                 search(board, m, n, x, y - 1, p + 1, word) ||
                 search(board, m, n, x, y + 1, p + 1, word);
     
    board[y][x] = t;
    return found;
  }
};

面试题13 机器人的运动范围

运行时间:4ms超过11.19% 用C++提交的代码

占用内存:676KB超过9.99%用C++提交的代码

利用书上的方法去做

但是效率太低下了

class Solution {
public:
    int movingCount(int threshold, int rows, int cols) {
        // 这道题和矩阵中路径类似,不同的是只需要遍历一次就可以了
        if(threshold < 0 || rows<=0 || cols<=0)
            return 0;
        // 设置一个访问的数组,当前的每一个位置开始遍历
        bool *visited = new bool[rows * cols];
        memset(visited,0,rows*cols);
        // 统计有效的移动范围
        int count = movingCountCore(threshold, rows,cols,0,0,visited);
        delete []visited;
        return count;
    }
    //先判断当前位置能否进入相邻 然后在判断
    int movingCountCore(int threshold,int rows, int cols, int row, int col, bool *visted)
    {
        int count =0;
        if(check(threshold, rows, cols, row, col,visted))
        {
            visted[row*cols + col] =true;
            // 左上右下
            count  = 1 + movingCountCore(threshold, rows, cols,
                                        row-1,col, visted)
                       + movingCountCore(threshold, rows, cols,
                                        row, col-1, visted)
                       + movingCountCore(threshold, rows, cols,
                                        row+1, col, visted)
                       + movingCountCore(threshold, rows, cols,
                                        row, col+1, visted);
        }
        return count;
    }
    
    bool check(int threshold,int rows, int cols, int row, int col, bool *visted)
    {
       if(row>=0 && row<rows && col>=0 && col< cols
          && getDigitSum(row)+getDigitSum(col)<=threshold
          &&!visted[row*cols +col])
              return  true;
        return false;
    }
    int getDigitSum(int num)
    {
        int sum = 0;
        while(num>0)
        {
            sum += num%10;
            num /=10;
        }       
        return sum;
    }
};

面试题14 剪绳子

动态规划做出来

但是效率并不高

运行时间:4ms超过21.99% 用C++提交的代码

占用内存:568KB超过32.41%用C++提交的代码

class Solution {
public:
    int cutRope(int number) {
        int product[number+1];
        // 写几个例子后发现 有递归的规律
        // 先把特殊绳子直接返回
        if(number == 2)
            return 1;
        if(number == 3)
            return 2;
        if(number == 4)
            return 4;
        // 大于4之后的 可以通过保存最优解决来解决
        product[0] = 0;
        product[1] = 1;
        product[2] = 2;
        product[3] = 3;
        int max  = product[3];
        for(int i=4; i<=number; i++)
        {
            max = 0;
            // 由于我们需要改变的第4个之后的,所以还需要加一个循环
            for(int j=0;j<=i/2; j++){
                product[i] = product[j]*product[i-j];
                if(product[i] >max)
                {
                    max = product[i];
                }
            }
            // 当前当前状态最优解
            product[i] = max;
        }
        return max;
        
    }
};

贪心算法求解

运行时间:4ms超过21.99% 用C++提交的代码

占用内存:592KB超过29.38%用C++提交的代码

class Solution {
public:
    int cutRope(int number) {
        // 找规律用贪心算法,原理就是 尽可能的多剪成3份
        if(number==2)
            return 1;
        if(number==3)
            return 2;
        if(number==4)
            return 4;
        // 尽可能剪成3份
        int timesOf3 = number/3;
        // 如果剩下的是4的情况就不该剪成3而是2;此时长度-3 =1
        int timesOf2 = 0;
        if(number - timesOf3*3 == 1)
        {
            timesOf3--;
        }
        timesOf2 = (number - timesOf3*3)/2;
        return pow(3,timesOf3) * pow(2,timesOf2);
        
    }
};

面试题15 二进制中的1的个数

注意

防止负数number

运行时间:4ms超过7.70% 用C++提交的代码

占用内存:608KB超过18.76%用C++提交的代码

class Solution {
public:
     int  NumberOf1(int n) {
         int length = 0;
         unsigned int flag = 1;
         // 循环结束的终止条件是32位整数的第32位
         while(flag)
         {
             if(n & flag)
                 length++;
             flag = flag<<1;
         }
         return length;
     }
};

小结:
把一个整数减去1后,再和原来整数做与运算,会把该整数最右边的1变成0

比如 5

1101

(1101-1) & 1101 = 1100

(1100-1) & 1100 = 1000

(1000-1) & 1000 = 0

class Solution {
public:
     int  NumberOf1(int n) {
        //比如 5   1101
        //(1101-1)  & 1101 = 1100
        //(1100-1)  &  1100 = 1000
        //(1000-1)  & 1000  = 0 
         int count = 0;
         while(n)
         {
             count++;
             n = (n-1)&n;
         }
         return count;
     }
};

面试题16 数值的整数次方

运行时间:5ms超过3.86% 用C++提交的代码

占用内存:572KB超过22.73%用C++提交的代码

基于规则

class Solution {
public:
    double Power(double base, int exponent) {
        int exponentAbs = exponent;
        // 任何非0的次方
        if(exponent==0)
            return 1.00;
        // 关于底数为负数的正次方的问题
        else if((base<=0.0 && exponent>0) || (base>=0.0 && exponent>0))
        {
            if(exponent <0)
                exponentAbs =-exponent;
            return PowerWithEx(base,exponentAbs);
        }
        // 当底数为负数 指数为负数的时候 倒数的平方
        else if(base<0.0 && exponent<0)
        {
            exponentAbs =-exponent;
            return 1.0/PowerWithEx(base,exponent);
        }
        // 当底数为正数,指数为负数
        else 
        {
            exponentAbs =-exponent;
            return 1.0/PowerWithEx(base,exponentAbs);
        }
    }
    
    double PowerWithEx(double base, int exponent)
    {
        double result = base;
        for(int i=1; i<exponent; ++i)
        {
            result *= base;
        }
        return result;
    }
};

与斐波那契数列一样,这部分依然有值得优化的地方

运行时间:3ms超过33.19% 用C++提交的代码

占用内存:592KB超过21.59%用C++提交的代码

class Solution {
public:
    double Power(double base, int exponent) {
        int exponentAbs = exponent;
        // 任何非0的次方
        if(exponent==0)
            return 1.00;
        // 关于底数为负数的正次方的问题
        else if((base<=0.0 && exponent>0) || (base>=0.0 && exponent>0))
        {
            if(exponent <0)
                exponentAbs =-exponent;
            return PowerWithEx(base,exponentAbs);
        }
        // 当底数为负数 指数为负数的时候 倒数的平方
        else if(base<0.0 && exponent<0)
        {
            exponentAbs =-exponent;
            return 1.0/PowerWithEx(base,exponent);
        }
        // 当底数为正数,指数为负数
        else 
        {
            exponentAbs =-exponent;
            return 1.0/PowerWithEx(base,exponentAbs);
        }
    }
    
    double PowerWithEx(double base, int exp)
    {
        // 采用Fib的优化方法的时候,需要注意exp=0.exp=1的情况
        if(exp==0)
           return 1;
        if(exp==1)
            return base;
        double result = PowerWithEx(base, exp>>1);
        result *= result;
        if(exp & 0x1 ==1)
            return result *= base;
        return result;
    }
};

面试题17 大数问题 从1到最大的N位数

大数问题

牛客网好像没有找到这一题的解法。

面试题18 删除链表的节点

删除重复的链表的节点

面试题19 正则表达式匹配

运行时间:3ms超过85.25% 用C++提交的代码

占用内存:432KB超过84.79%用C++提交的代码

描述

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

示例1

输入:

"aaa","a*a"

返回值:

true
class Solution {
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param str string字符串 
     * @param pattern string字符串 
     * @return bool布尔型
     */
    bool match(string str, string pattern) {
        // write code here
        //
        // 边界判断
        if(str.length()<0 || pattern.length()<0)
            return false;
        return matchCore(str,pattern,0,0);
    }
    bool matchCore(string str, string pattern,int i,int j){
        //如果两个字符串都到达了末尾 trues
        if(str[i]=='\0' && pattern[j]=='\0')
            return true;
        // 如果只有其中一个到达末尾则 false  这是不对的
        // 错误 写法|| (str[i]!='\0' && pattern[j]=='\0')
        if((str[i]!='\0' && pattern[j]=='\0'))
        {
           return false;
        }
        // 如果当前位置的下一个是* 则分为3种情况
        if(pattern[j+1]=='*')
        {
            // 一种是当前位置和需要匹配的相等则
            if(str[i]==pattern[j] || (pattern[j]=='.'&&str[i]!='\0'))
            {
                // 一种是str移动两个位置,模式+1
                // 一种算是str移动一个 模式保持不变
                return matchCore(str,pattern,i+1,j+2) ||
                       matchCore(str, pattern, i+1, j) ||
                       matchCore(str, pattern, i, j+2);
            }
            // 一种是直接忽略* 以及其前面的
            else
            {
                return matchCore(str, pattern, i, j+2);
            }
                
        }
        
        // 匹配才移动 直接判断是否匹配,然后一定各+1
        if(str[i]==pattern[j] ||(pattern[j]=='.' && str[i]!='\0'))
        {
            return matchCore(str, pattern, i+1, j+1);
        }
        return false;
       
    }
};

20 表示数值的字符串

描述

请实现一个函数用来判断字符串str是否表示数值(包括科学计数法的数字,小数和整数)。

科学计数法的数字(按顺序)可以分成以下几个部分:

1.若干空格

2.一个整数或者小数

3.(可选)一个 ‘e’ 或 ‘E’ ,后面跟着一个整数(可正可负)

4.若干空格

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

1.若干空格

2.(可选)一个符号字符(’+’ 或 ‘-’)

\3. 可能是以下描述格式之一:

3.1 至少一位数字,后面跟着一个点 ‘.’

3.2 至少一位数字,后面跟着一个点 ‘.’ ,后面再跟着至少一位数字

3.3 一个点 ‘.’ ,后面跟着至少一位数字

4.若干空格

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

1.若干空格
2.(可选)一个符号字符(’+’ 或 ‘-’)

\3. 至少一位数字

4.若干空格

例如,字符串["+100",“5e2”,"-123",“3.1416”,"-1E-16"]都表示数值。

但是[“12e”,“1a3.14”,“1.2.3”,“±5”,“12e+4.3”]都不是数值。

提示:

1.1 <= str.length <= 20

2.str 仅含英文字母(大写和小写),数字(0-9),加号 ‘+’ ,减号 ‘-’ ,空格 ’ ’ 或者点 ‘.’ 。

3.如果怀疑用例是不是能表示为数值的,可以使用python的print(float(str))去查看

示例1

输入:

"123.45e+6"

返回值:

true

运行时间:3ms超过87.76% 用C++提交的代码

占用内存:508KB超过22.89%用C++提交的代码

class Solution {
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param str string字符串 
     * @return bool布尔型
     */
    // 数字的格式可以用A[.[B]][e|EC]或者.B[e|EC] 表示
    // 其中A,C 都是整数(可以有正负号) 而B是一个无符号整数
    bool isNumeric(string str) {
        // write code here
        if(str.length()<0 || str.empty())
            return false;
        bool flag = hasNumber(str,0);
        if(!flag)
            return false;
        // 定义一个下标判断正负号部分
        int i=0;
        // 对于一个数字都没有的字符串直接返回
            
        bool numeric = scanInteger(str,i);
        // 如果出现'.' 则接下来是数字的小数部分
        if(str[i]=='.')
        {
            ++i;
            // 下面一行代码用||的原因:
            //1. 小数可以没有整数部分,如.123等于0.123
            //2. 小数点后面可以诶呦数字,如2333. 等于 2333.0
            //3。 小数点前面和后面可以都有数字 如 233.66
            numeric = scanUnsignedInter(str,i) || numeric;
        }
        // 如果出现'e' 或则‘E’, 则接下来时数字的指数部分
        if(str[i] == 'e' || str[i] == 'E')
        {
            ++i;
            // 下面一行代码用&&的原因
            // 1. 当e或E前面没有数字的时候,整个字符串不能表示数字,如.e1
            // 2. 当e或E后面没有整数时,整个字符串不能表示数字,如12e,12e+5.4
            numeric = numeric && scanInteger(str,i);
            
        }
        return numeric && str[i]=='\0';
    }
    
    // 特殊案例检测
    bool hasNumber(string str, int j)
    {
        while(str[j])
        {
            if(str[j]>='0' && str[j]<='9')
                return true;
            j++;
        }
        return false;
    }
    
    // 扫描可能以+-开始的起始数位 A,C
    bool scanInteger(string str, int &i)
    {
        while(str[i]==' ' && str[i]!='\0')
            i++;
        if(str[i]=='+' || str[i]=='-')
            ++i;
        return scanUnsignedInter(str,i);
    }
    
    //扫描字符串中0-9的 数位 A B C 
    bool scanUnsignedInter(string str, int &i)
    {
        const int berforeI  = i;
        //题目要求可以有若干个空格
        while((str[i]!='\0' && str[i]>='0' && str[i]<='9')
              || str[i]==' ')
            ++i;
        // 当str中存在0-9 则下标会变大,返回true;
        return i>berforeI;
    }
};

栈与队列

栈和队列的一些基本语法

std::stack<int> S;

S.empty()

S.push(10)

S.pop()

S.top()

队列

std::queue<int> Q;

Q.push(5)

Q.pop()

Q.top()

9 用两个栈实现队列

方法做起来很简单,但是题目居然出奇的没看懂。。。

class CQueue {
public:
    std::stack<int> s;
    std::stack<int> temp_s;
    CQueue() {

    }
    
    void appendTail(int value) {
        s.push(value);
    }
    
    int deleteHead() {
        if(s.empty())
            return -1;
        while(s.size()!=1){
            temp_s.push(s.top());
            s.pop();
        }
        int value = s.top();
        s.pop();
        while(!temp_s.empty()){
            // 这部分要把临时栈的数据还给原来的栈
            s.push(temp_s.top());
            temp_s.pop();
        }
        return value;

    }
};

/**
 * Your CQueue object will be instantiated and called as such:
 * CQueue* obj = new CQueue();
 * obj->appendTail(value);
 * int param_2 = obj->deleteHead();
 */

30 包含min函数的栈

class MinStack {
public:
    /** initialize your data structure here. */
    std::stack<int> stack;
    std::stack<int> min_stack;
    MinStack() {
        min_stack.push(INT_MAX);    
    }
    
    void push(int x) {
        stack.push(x);
        if(x>min_stack.top()){
            x=min_stack.top();
        }
          min_stack.push(x);
    }
    
    void pop() {
        stack.pop();
        min_stack.pop();
    }
    
    int top() {
       return stack.top();
    }
    
    int min() {
        return min_stack.top();
    }
};

21 调整数组顺序

书本上的做法

class Solution {
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param array int整型vector 
     * @return int整型vector
     */
    vector<int> reOrderArray(vector<int>& array) {
        // write code here
        // 通过两个位置,开始和结尾
        int startI = 0; 
        int endI = array.size()-1;
        while(startI < endI)
        {
            // 第一个位置向后移动,直到为偶数的时候停下来
            while(startI < endI && (array[startI] & 0x1)!=0)
                startI++;
            // 遇到奇数停下来
            while(startI < endI && (array[endI] & 0x1)==0)
                endI--;
            if(startI<endI)
            {
                int temp = array[startI];
                array[startI] = array[endI];
                array[endI] = temp;
            }
        }
        return array;
    }
};

描述

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

示例1

输入:

[1,2,3,4]

返回值:

[1,3,2,4]

运行时间:11ms超过54.01% 用C++提交的代码

占用内存:1644KB超过28.57%用C++提交的代码

class Solution {
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param array int整型vector 
     * @return int整型vector
     */
    vector<int> reOrderArray(vector<int>& array) {
        // write code here
        // 定义一个新的容器,偶数的放在前面 奇数的放在后面
        vector<int> sortArray = {};
        // 奇数在前
        for(int i=0; i<array.size(); ++i)
        {
            if(array[i] %2 != 0)
                sortArray.push_back(array[i]);
        }
        // 偶数在后
        for(int i=0; i<array.size(); ++i)
        {
            if(array[i] % 2 ==0)
                sortArray.push_back(array[i]);
        }
        return sortArray;
    }
};

改进但也没提升多少啊

class Solution {
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param array int整型vector 
     * @return int整型vector
     */
    vector<int> reOrderArray(vector<int>& array) {
        // write code here
        // 定义一个新的容器,偶数的放在前面 奇数的放在后面
        vector<int> endArray(array.size(), 0);
        // 将前后两部分一次性存下来,但是注意处理作用域
        int frontIndex = 0;
        int endIndex = 0;
        for(auto a : array)
        {
            if((a & 1)==1)
                array[frontIndex++] = a;
            else
                endArray[endIndex++] = a;
        }
        // 偶数在后
        for(int i=0; i<endIndex; ++i)
        {
            array[frontIndex++] = endArray[i];
        }
        return array;
    }
};

超过45.41% 用C++提交的代码

占用内存:1800KB超过7.19%用C++提交的代码

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

忆_恒心

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

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

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

打赏作者

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

抵扣说明:

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

余额充值