算法题

算法题

数据结构

逻辑结构

  • 集合结构
  • 线性结构
  • 树形结构
  • 图形结构

物理结构

  • 顺序存储

    需要分配连续的存储空间

  • 链式存储

    不需要分配连续的存储空间,每个单元不仅要存数据也要存一个后继元素的地址

  • 哈希存储

    通过哈希算法计算存储位置

  • 索引存储

    需要一个索引表

算法题

简单

1.两数之和

问题描述:给定一个数组和目标值target,查找数组中和为target的两个整数,返回两个整数的索引。
限制:两个输入只对应一个答案;不能使用相同的元素。
解法:
双层遍历,或者外层遍历,内层二分查找,时间复杂度为O(nn), O(nlog2n)
首先字典存储所有键值,然后再进行循环查询target-nums[i],此时O(n*n),圈复杂度为 5;两个循环串行
也是使用字典,但是使用一个循环,首先查询字典中是否存在target-nums[i]键值,如果存在直接,返回索引;如果不存在,插入。相比于第二种方法好处是避免了另外起一个循环去存字典,而且能够避免使用相同的元素。

// 哈希表法
class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        unordered_map<int, int> tempMap;
        for (int i = 0; i < nums.size(); ++i) {
            if (tempMap.count(target - nums[i]) != 0) {
                return {tempMap[target - nums[i]], i};
            }
            tempMap[nums[i]] = i;
        }
        return {};
    }
};

2235.两整数相加

输入:两个整数;输出:两数之和;限制条件:无
思路:直接返回两数之和,使用constexpr直接使用编译时运算

class Solution { 
public: 
 // constexpr是c++17特性
 constexpr int sum(int num1, int num2) { 
 return num1 + num2; 
 } 
};

1929.数组串联

输入:一个数组;输出:生成一个新的数组;限制条件:新的数组为两个输入数组的串联A->AA
思路:直接向后插入
法一:更省空间

class Solution {
public:
    vector<int> getConcatenation(vector<int>& nums) {
        int n = nums.size();
        for (int i = 0; i < n; ++i) {
            nums.push_back(nums[i]);l
        }
        return nums;
    }
};

法二:感觉更好

class Solution {
public:
    vector<int> getConcatenation(vector<int>& nums) {
        vector<int> ans(nums);
        copy(nums.begin(),nums.end(),back_inserter(ans));
        return ans;
    }
};

771.宝石与石头

输入:宝石字符串,石头字符串,都是字母;输出:宝石数量;限制:字母区分大小写。
思路:1. 两层遍历O(m+n),不可取
先将宝石字符串存为哈希值,然后遍历石头,计数,O(m+n)

1480.一维数组动态和

输入:一个数组;输出:一个数组;限制:[1,2,3,4]->[1,3,6,10]
思想:
两层遍历
滚动数组更新,每一个元素都是前几个元素的和,直接在原数组上累加,每次元素都加上前一个元素,前一个元素已经是它之前元素的和了。需要一个哨兵变量。
解法:

class Solution { 
public: 
 vector<int> runningSum(vector<int>& nums) { 
 int prev = nums[0]; 
 for (int i = 1; i < nums.size(); ++i) { 
 nums[i] = nums[i] + prev; 
 prev = nums[i]; 
 } 
 return nums; 
 } 
};

709.转换成小写字母

输入:一个字符串,全是字母;输出:一个字符串;限制:所有字母都转换为小写字母

class Solution { 
public: 
 string toLowerCase(string s) { 
 for_each(s.begin(),s.end(),[](auto& item){item = tolower(item);}); 
 return s; 
 } 
};

1672.最富有资产总数

输入:一个mxn数组accounts,accounts[i][j]代表第i个人在第j家银行的资产额度
输出:最富有的资产总数
思路:
哨兵:两层遍历,使用accumulator缩短代码量,计算每一个数组的和,用一个变量保存最大值即可。

class Solution {
public:
    int maximumWealth(vector<vector<int>>& accounts) {
        int maxCount = 0;
        for (int i = 0; i < accounts.size(); ++i) {
            // accumulate累加计算,从值0开始
            maxCount = max(maxCount, accumulate(accounts[i].begin(),accounts[i].end(),0)); 
        }
        return maxCount;
    }
};

66.加一

输入:一个数组,存储的是一个整数的每一位,每一位在0~9之间
输出:加一之后的,数组
限制:除0之外无前导0
思路:
其实就是向前传递进位,但是要注意999->1000,需要添加1
所以,先反转数组,则多余的进位只需要压入1即可。因为不知道进位有多少次,所以使用while循环进行,其实就是两数加法

class Solution {
public:
    vector<int> plusOne(vector<int>& digits) {
        reverse(digits.begin(), digits.end());
        int sum = digits[0] + 1;
        digits[0] = sum % 10;
        int carry = sum / 10;
        int i = 1;
        while (carry > 0) {
            if (i == digits.size()) {
                digits.push_back(carry);
                break;
            }
            sum = digits[i] + carry;
            digits[i] = sum % 10;
            carry = sum / 10;
            i++;
        }
        reverse(digits.begin(), digits.end());
        return digits;
    }
};

724.寻找数组中心下标

问题描述:寻求数组中心下标,其左侧所有元素和等价于右侧所有元素和。
输入:一个整数数组,每个元素在-1000~1000之间
输出:数组中心下标
思路:
1、遍历数组,使用accumulate函数分别对左右元素加和,然后求是否相等即可。没找到默认返回-1;初始即可设置index=-1;O(n),圈复杂度为2,
456ms。

class Solution {
public:
    int pivotIndex(vector<int>& nums) {
        int index = -1; // 默认返回下标-1
        for (auto it = nums.begin(); it != nums.end(); ++it) {
            int lnum = accumulate(nums.begin(), it, 0);
            int rnum = accumulate(it + 1, nums.end(), 0);
            if (lnum == rnum) {
                index = it - nums.begin();
                break;
            }
        }
        return index;
    }
};

2、分别利用两个循环从正反两个方向进行一维动态数组求和,然后再采用一个循环,进行比较即可, 4ms。2

中等

198.数组轮转

问题描述:要求数组向后进行轮转,例如[1,2,3,4,5,6,7],向后轮转3位,变为[5,6,7,1,2,3,4]
输入:一个数组,一个整数代表轮转位数
输出:无返回值
思路:
1、首先避免多余的轮转操作,先计算最小的k,然后先前插入,但是这样带来的后果是会产生begin(), begin()+k元素的移位操作,超时作法:这种超时是由移位操作带来的

class Solution {
public:
    void rotate(vector<int>& nums, int k) {
        k %= nums.size();
        while(k > 0) {
            int tmp = nums.back();
            nums.pop_back();
            nums.insert(nums.begin(), tmp);
            k--;
        }
    }

2、避免移位操作的做法:利用一个额外数组存储元素,然后将更新后的元素按顺序复制到新的数组中,然后对数组进行交换。

class Solution {
public:
    void rotate(vector<int>& nums, int k) {
        vector<int> fnums(nums);
        k %= nums.size();
        copy(fnums.end()-k, fnums.end(), nums.begin());
        copy(fnums.begin(), fnums.end()-k, nums.begin() + k);
    }
};

swap可以交换两个容器

48.旋转图像

问题描述:图像顺时针旋转90度
输入:一个矩阵
输出:无
限制:原位旋转
思路:这道题的思路其实比较简单主要是需要看出,要将第一行旋转到最后一列,第二列旋转到倒数第二列。

class Solution {
public:
    void rotate(vector<vector<int>>& matrix) {
        vector<vector<int>> tmpMatrix(matrix);
        int m = tmpMatrix.size(), n = tmpMatrix[0].size();
        for (int i = 0; i < m; ++i) {
            for (int j = 0; j < n; ++j) {
                matrix[j][n -1 -i] = tmpMatrix[i][j];
            }
        }
    }
};

6.Z字型变换

算术评级:4

将一个给定字符串 s 根据给定的行数 numRows ,以从上往下、从左到右进行 Z 字形排列。
比如输入字符串为 "PAYPALISHIRING" 行数为 3 时,排列如下:
P A H N
A P L S I I G
Y I R
之后,你的输出需要从左往右逐行读取,产生出一个新的字符串,比如:"PAHNAPLSIIGYIR"。
请你实现这个将字符串进行指定行数变换的函数:
string convert(string s, int numRows);
示例 1:
输入:s = "PAYPALISHIRING", numRows = 3
输出:"PAHNAPLSIIGYIR"
示例 2:
输入:s = "PAYPALISHIRING", numRows = 4
输出:"PINALSIGYAHRPI"
解释:
P I N
A L S I G
Y A H R
P I
示例 3:

输入:s = "A", numRows = 1
输出:"A"

class Solution {
public:
    string convert(string s, int numRows) {
        // 考虑到字符串可能只有1个
        if (s.size() == 1) return s;

        // 用一个容器存储,每行的字符串,可能会出现行数比字符串长的情况
        vector<string> rows(min(numRows, int(s.size())));
        int currentRow = 0;
        bool goingDown = false;

        for (auto c : s) {
            // 先完成将字符放进去的逻辑
            rows[currentRow] += c;
            // 处理边界
            if ( currentRow == 0 or currentRow == numRows -1 ) {
                goingDown = !goingDown;
            }
            // 以方向转变做判断,确定下一行行号
            currentRow = goingDown? ++currentRow : --currentRow;
        }

        // 生成结果字符串
        // string result;
        // for (auto item : rows) {
        //     result += item;
        // }
        string result = accumulate(rows.begin(), rows.end(), string());
        return result;
        
    }
};

7.整数反转

class Solution {
public:
    int reverse(int x) {
        int rev = 0;
        int max_div_10 = INT_MAX / 10; // 214748364
        
        while (x != 0) {
            int pop = x % 10;
            x /= 10;
            // 检查是否溢出
            if (rev > max_div_10 || rev < (-1 * max_div_10)) {
                return 0;
            }
            
            rev = rev * 10 + pop;
        }
        
        return rev;
    }
};

8.字符串转为整数

class Solution {
public:
    int myAtoi(std::string s) {
        int i = 0; // 用于遍历字符串
        int n = s.size(); // 字符串长度
        int sign = 1; // 符号,默认为正
        long long num = 0; // 使用 long long 防止溢出

        // 1. 跳过前导空格
        while (i < n and s[i] == ' ') {
            i++;
        }

        // 2. 检查符号
        if (i < n && (s[i] == '-' || s[i] == '+')) {
            sign = (s[i] == '-') ? -1 : 1;
            i++;
        }

        // 3. 读取数字
        while (i < n && isdigit(s[i])) {
            int digit = s[i] - '0'; // 将字符转换为数字
            num = num * 10 + digit;

            // 4. 检查是否超出 32 位整数范围
            if (num * sign > INT_MAX) {
                return INT_MAX;
            } else if (num * sign < INT_MIN) {
                return INT_MIN;
            }
            i++;
        }

        // 5. 应用符号并返回结果
        return num * sign;
    }
};

困难

10.正则表达式匹配


#include <iostream>
#include <vector>
#include <string>

class Solution {
public:
    bool isMatch(std::string s, std::string p) {
        int m = s.size(); // 字符串 s 的长度
        int n = p.size(); // 模式 p 的长度

        // dp[i][j] 表示 s 的前 i 个字符是否能被 p 的前 j 个字符匹配
        std::vector<std::vector<bool>> dp(m + 1, std::vector<bool>(n + 1, false));

        // 空字符串和空模式匹配
        dp[0][0] = true;

        // 处理模式 p 中的 '*',使得 dp[i][j] 可以由 dp[i][j-2] 转移而来
        for (int j = 1; j <= n; j++) {
            if (p[j - 1] == '*') {
                dp[0][j] = dp[0][j - 2];
            }
        }

        // 动态规划填表
        for (int i = 1; i <= m; i++) {
            for (int j = 1; j <= n; j++) {
                if (p[j - 1] == '*') {
                    // '*' 表示前面的字符可以出现零次或多次
                    dp[i][j] = dp[i][j - 2]; // '*' 表示前面的字符出现零次
                    if (s[i - 1] == p[j - 2] || p[j - 2] == '.') {
                        dp[i][j] = dp[i][j] || dp[i - 1][j]; // '*' 表示前面的字符出现多次
                    }
                } else if (s[i - 1] == p[j - 1] || p[j - 1] == '.') {
                    // 当前字符匹配
                    dp[i][j] = dp[i - 1][j - 1];
                }
            }
        }

        return dp[m][n];
    }
};
posted @ 2025-04-26 23:17  LemHou  阅读(1)  评论(0)    收藏  举报