当前位置: 首页 > article >正文

LeetCode 每周算法 10(多维动态规划、技巧)

LeetCode 每周算法 10(多维动态规划、技巧)

多维动态规划算法:

在这里插入图片描述

class Solution {  
public:  
    // 定义一个公开的方法,用于计算独特路径的数量  
    int uniquePaths(int m, int n) {  
        // 创建一个二维动态规划数组dp,用于存储到达每个网格的独特路径数量  
        // dp[i][j] 表示到达 (i, j) 网格的独特路径数量  
        vector<vector<int>> dp(m, vector<int>(n));  
        // 初始化第一列的所有网格,因为从左上角到第一列的任意网格都只有一条路径(一直向下走)  
        for (int i = 0; i < m; i++) {  
            dp[i][0] = 1;  
        }  
        // 初始化第一行的所有网格,因为从左上角到第一行的任意网格都只有一条路径(一直向右走)  
        for (int j = 0; j < n; j++) {  
            dp[0][j] = 1;  
        }  
          
        // 填充dp数组的其他部分  
        // 对于每个网格 (i, j),其独特路径数量等于从上方网格 (i-1, j) 和左侧网格 (i, j-1) 到达的路径数量之和  
        for (int i = 1; i < m; i++) {  
            for (int j = 1; j < n; j++) {  
                dp[i][j] = dp[i - 1][j] + dp[i][j - 1];  
            }  
        }    
        
        // 返回右下角网格 (m-1, n-1) 的独特路径数量  
        return dp[m - 1][n - 1];  
    }  
};

在这里插入图片描述

class Solution {  
public:  
    // 函数计算从网格左上角到右下角的最小路径和  
    int minPathSum(vector<vector<int>>& grid) {  
        // 获取网格的行数和列数  
        int m = grid.size();  
        int n = grid[0].size();  
        // 创建一个动态规划数组dp,用于存储到达每个网格位置的最小路径和  
        vector<vector<int>> dp(m, vector<int>(n));  
        // 初始化起点位置的最小路径和为网格起点的值  
        dp[0][0] = grid[0][0];  
        // 初始化第一列的最小路径和,只能从上面到达  
        for (int i = 1; i < m; i++) {  
            dp[i][0] = dp[i - 1][0] + grid[i][0];  
        }  
        // 初始化第一行的最小路径和,只能从左边到达  
        for (int j = 1; j < n; j++) {  
            dp[0][j] = dp[0][j - 1] + grid[0][j];  
        }  
          
        // 填充dp数组的其余部分,对于每个网格位置,选择从上面或左边到达的最小路径和  
        for (int i = 1; i < m; i++) {  
            for (int j = 1; j < n; j++) {  
                dp[i][j] = min(dp[i - 1][j] + grid[i][j], dp[i][j - 1] + grid[i][j]);  
            }  
        }  
          
        // 返回右下角网格位置的最小路径和  
        return dp[m - 1][n - 1];  
    }  
};

在这里插入图片描述

class Solution {  
public:  
    string longestPalindrome(string s) {      
        // 初始化结果字符串为s的第一个字符,作为最保守的初始选择。  
        // 这意味着,在最坏的情况下,我们将返回字符串中的一个单字符作为回文子串。  
        string result = s.substr(0, 1);  
        // 创建一个二维动态规划数组dp,其中dp[i][j]表示子串s[i...j](包含索引i和j的字符)是否为回文串。  
        // 数组的大小为s.size() x s.size(),因为我们需要检查所有可能的子串。  
        vector<vector<bool>> dp(s.size(), vector<bool>(s.size()));  
  
        // 从字符串的末尾开始向前遍历,这样我们可以在计算dp[i][j]时利用之前已经计算过的dp值。  
        // 这种自底向上的方法允许我们逐步构建更长的回文子串的解。  
        for (int i = s.size() - 1; i >= 0; i--) {  
            // 对于每个起始索引i,我们遍历所有可能的结束索引j(其中j > i)。  
            for (int j = i + 1; j < s.size(); j++) {  
                // 如果子串的长度小于等于2(即j - i <= 2),我们只需要检查两个字符是否相等。  
                // 在这种情况下,不需要考虑子串内部的回文性,因为子串太短了。  
                if (j - i <= 2) {  
                    dp[i][j] = (s[i] == s[j]); // 如果两个字符相等,则子串是回文串;否则不是。  
                } else {  
                    // 对于更长的子串,我们需要检查两个条件:  
                    // 1. 子串的首尾字符是否相等(s[i] == s[j])。  
                    // 2. 去掉首尾字符后的子串(s[i+1...j-1])是否是回文串(dp[i + 1][j - 1])。  
                    // 只有当这两个条件都满足时,整个子串才是回文串。  
                    dp[i][j] = (s[i] == s[j]) && dp[i + 1][j - 1];  
                }  
                  
                // 如果当前子串是回文串,并且它的长度大于我们之前找到的最长回文子串的长度,  
                // 则更新结果字符串为当前子串。  
                if (dp[i][j] && j - i + 1 > result.size()) {  
                    result = s.substr(i, j - i + 1); // 提取子串s[i...j]并更新结果。  
                }  
            }  
        }  
        
        // 返回找到的最长回文子串。  
        return result;  
    }  
};

// 中心扩散法:
// class Solution {
// public:
//     string longestPalindrome(string s) {
//         int n = s.size();
//         string ans = "";
//         for(int i = 0; i < n; i++)
//         {
//             int l = i, r = i + 1;
//             while(l >= 0 && r < n && s[l] == s[r]) l--, r++;
//             if(ans.size() < r - l - 1) ans = s.substr(l + 1, r - l - 1);

//             l = i - 1, r = i + 1;
//             while(l >= 0 && r < n && s[l] == s[r]) l--, r++;
//             if(ans.size() < r - l - 1) ans = s.substr(l + 1, r - l - 1);
//         }
//         return ans;
//     }
// };

在这里插入图片描述

class Solution {  
public:  
    // 函数用于计算两个字符串的最长公共子序列的长度  
    int longestCommonSubsequence(string text1, string text2) {  
        // 创建一个二维动态规划数组dp,大小为(text1.size() + 1) x (text2.size() + 1)  
        // 数组的所有元素初始化为0,dp[i][j]表示text1的前i个字符和text2的前j个字符的最长公共子序列的长度  
        vector<vector<int>> dp(text1.size() + 1, vector<int>(text2.size() + 1, 0));  
          
        // 遍历text1的所有字符  
        for (int i = 1; i <= text1.size(); i++) {  
            // 遍历text2的所有字符  
            for (int j = 1; j <= text2.size(); j++) {  
                // 如果text1的第i个字符和text2的第j个字符相等  
                if (text1[i - 1] == text2[j - 1]) {  
                    // 则dp[i][j]等于dp[i-1][j-1]加1,表示找到了一个公共字符,最长公共子序列长度加1  
                    dp[i][j] = dp[i - 1][j - 1] + 1;  
                } else {  
                    // 如果不相等,则dp[i][j]为dp[i-1][j]和dp[i][j-1]中的较大值  
                    // 这表示我们不考虑当前text1的第i个字符或者当前text2的第j个字符,看哪种情况能得到更长的公共子序列  
                    dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);  
                }  
            }  
        }  
          
        // 最后,dp[text1.size()][text2.size()]存储了text1和text2的最长公共子序列的长度  
        return dp[text1.size()][text2.size()];  
    }  
};

在这里插入图片描述

class Solution {  
public:  
    // 计算两个字符串word1和word2之间的最小编辑距离  
    int minDistance(string word1, string word2) {  
        // 创建一个二维动态规划数组dp,dp[i][j]表示word1的前i个字符转换为word2的前j个字符所需的最小编辑距离  
        vector<vector<int>> dp(word1.size() + 1, vector<int>(word2.size() + 1, 0));  
        // 初始化第一行,表示当word1为空字符串时,转换为word2的前j个字符所需的最小编辑距离(即插入操作次数)
        for (int i = 0; i <= word1.size(); i++) dp[i][0] = i;
        // 初始化第一列,表示当word2为空字符串时,将word1的前i个字符转换为空字符串所需的最小编辑距离(即删除操作次数)  
        for (int j = 0; j <= word2.size(); j++) dp[0][j] = j;
          
        // 填充dp数组的其余部分  
        for (int i = 1; i <= word1.size(); i++) { // 遍历word1的每个字符  
            for (int j = 1; j <= word2.size(); j++) { // 遍历word2的每个字符  
                if (word1[i - 1] == word2[j - 1]) {  
                    // 如果word1的第i个字符和word2的第j个字符相等,则不需要进行编辑操作  
                    dp[i][j] = dp[i - 1][j - 1]; // 继承之前的状态  
                } else {  
                    // 如果不相等,则考虑三种操作:插入、删除、替换,并取这三种操作中的最小值加1  
                    dp[i][j] = min({dp[i - 1][j - 1], dp[i - 1][j], dp[i][j - 1]}) + 1;  
                    // 分别表示替换(虽然这里没有单独写出,但替换的效果已经通过取这三个值中的最小值隐含在内了)、  
                    // 删除word1的第i个字符(dp[i - 1][j] + 1)和在word1的第i个位置插入word2的第j个字符(dp[i][j - 1] + 1)  
                }  
            }  
        }  
        
        // 返回word1转换为word2所需的最小编辑距离  
        return dp[word1.size()][word2.size()];  
    }  
};

技巧算法:

在这里插入图片描述

// 定义一个名为Solution的类  
class Solution {  
public:  
    // 定义一个成员函数,用于找出数组中只出现一次的数字  
    // 输入参数是一个整数类型的向量(数组)  
    // 返回值是一个整数,表示数组中只出现一次的数字  
    int singleNumber(vector<int>& nums) {  
        // 初始化一个变量result为0,用于存储最终的结果  
        // 这里利用了异或运算的性质:a ^ a = 0, a ^ 0 = a  
        // 以及异或运算的交换律和结合律,使得我们可以将所有相同的数字异或成0  
        // 而只出现一次的数字与0异或后仍然是它本身  
        int result = 0;  
          
        // 遍历数组中的每一个数字  
        for (auto num: nums) {  
            // 对result和当前数字num进行异或运算  
            // 由于异或运算的性质,所有成对出现的数字都会被异或成0  
            // 而只出现一次的数字会被保留下来  
            result ^= num;  
        }  
          
        // 返回最终结果,即数组中只出现一次的数字  
        return result;  
    }  
};

在这里插入图片描述

// 定义一个名为Solution的类  
class Solution {  
public:  
    // 定义一个成员函数,用于找出数组中的多数元素(出现次数超过一半的元素)  
    // 输入参数是一个整数类型的向量(数组)  
    // 返回值是一个整数,表示数组中的多数元素  
    int majorityElement(vector<int>& nums) {  
        // 初始化候选元素为0,计数器也为0  
        // 这里利用了Boyer-Moore投票算法的思想  
        int candidate = 0;  
        int count = 0;  
          
        // 遍历数组中的每一个数字  
        for (int num : nums) {  
            // 如果当前数字与候选元素相同,则计数器加1  
            if (num == candidate) {  
                ++count;  
            }   
            // 如果当前数字与候选元素不同,则计数器减1  
            // 如果计数器减到0以下,说明候选元素可能不是多数元素  
            // 因此更新候选元素为当前数字,并将计数器重置为1  
            else if (--count < 0) {  
                candidate = num;  
                count = 1;  
            }  
        }  
          
        // 由于Boyer-Moore投票算法的特性  
        // 在一轮遍历后,候选元素要么是多数元素,要么是多数元素的候选者之一  
        // 为了确保正确性,我们通常需要再遍历一次数组来验证候选元素的出现次数  
        // 但在这个特定的问题中,由于题目已经保证了存在一个多数元素  
        // 因此我们可以直接返回候选元素作为结果  
        // 注意:在一般情况下,如果没有多数元素存在的保证,这一步验证是必要的  
          
        // 返回候选元素作为多数元素  
        return candidate;  
    }  
};

在这里插入图片描述

// 定义一个名为Solution的类  
class Solution {  
public:  
    // 定义一个成员函数,用于对包含0、1、2的数组进行排序  
    // 输入参数是一个整数类型的向量(数组),通过引用传递,允许直接修改原数组  
    void sortColors(vector<int>& nums) {  
        // 初始化三个指针:left指向0的右侧,mid用于遍历数组,right指向2的左侧  
        int left = 0;       // left指针左边的元素都是0  
        int mid = 0;        // mid指针用于遍历数组  
        int right = nums.size() - 1; // right指针右边的元素都是2  
          
        // 当mid指针没有超过right指针时,继续循环  
        while (mid <= right) {  
            // 如果当前元素是0,将其与left指针指向的元素交换,并将left和mid指针都向右移动一位  
            if (nums[mid] == 0) {  
                swap(nums[left++], nums[mid++]);  
            }  
            // 如果当前元素是2,将其与right指针指向的元素交换(注意此时mid指针不动)  
            // 因为交换过来的元素可能是0、1或2,需要mid指针再次检查这个新元素  
            // 而right指针向左移动一位,因为它已经确定了一个2的位置  
            else if (nums[mid] == 2) {  
                swap(nums[right--], nums[mid]); // 注意这里没有增加mid,因为新元素未检查  
            }  
            // 如果当前元素是1,则只需要将mid指针向右移动一位  
            // 因为1已经处于正确的位置(在0和2之间),无需交换  
            else {  
                mid++;  
            }  
        }  
          
        // 循环结束后,数组已经按照0、1、2的顺序排序好了  
        // 注意:由于题目要求原地排序,因此没有使用额外的数组空间  
    }  
};

在这里插入图片描述

class Solution {  
public:  
    // 定义一个函数,用于找到并修改给定数组的下一个排列  
    void nextPermutation(vector<int>& nums) {  
        // 从倒数第二个元素开始向前遍历,找到第一个不满足从前往后递增的元素  
        int i = nums.size() - 2;  
        while (i >= 0 && nums[i] >= nums[i + 1]) {  
            i--;  
        }  
          
        // 如果找到了这样的元素(即i >= 0),说明从i+1到数组末尾是递减的  
        // 我们需要找到i之后(不包括i)比nums[i]大的最小元素,并与nums[i]交换  
        if (i >= 0) {  
            int j = nums.size() - 1;  
            // 从数组末尾开始向前遍历,找到第一个比nums[i]小的元素  
            while (j >= 0 && nums[i] >= nums[j]) {  
                j--;  
            }  
            // 交换nums[i]和nums[j],这样nums[i]之后的序列仍然是递减的,但nums[i]变大了  
            swap(nums[i], nums[j]);  
        }  
          
        // 最后,将i之后的所有元素反转,使它们变为递增序列  
        // 这样做是为了得到比当前排列大且最接近的下一个排列  
        reverse(nums.begin() + i + 1, nums.end());  
    }  
};

在这里插入图片描述

class Solution {  
public:  
    // 定义一个函数,用于在一个包含n+1个整数的数组中找到任意一个重复的数字  
    int findDuplicate(vector<int>& nums) {  
        // 初始化快慢指针,都从数组的第一个元素开始  
        int slow = nums[0], fast = nums[nums[0]];  
          
        // 使用do-while循环来移动快慢指针,直到它们相遇  
        // 快指针每次移动两步(nums[nums[fast]]),慢指针每次移动一步(nums[slow])  
        // 当快慢指针不相遇时,继续循环  
        while (slow != fast) {  
            slow = nums[slow];  // 慢指针移动一步  
            fast = nums[nums[fast]];  // 快指针移动两步  
        }
          
        // 当快慢指针相遇时,我们知道存在一个环(即重复的数字)  
        // 为了找到环的起点(即重复的数字),我们将慢指针重新设置为数组的起始位置  
        // 然后同时移动快慢指针,直到它们再次相遇,相遇点即为环的起点(重复的数字)  
        slow = 0;  
          
        // 使用while循环来同时移动快慢指针,直到它们再次相遇  
        while (slow != fast) {  
            slow = nums[slow];  // 慢指针移动一步  
            fast = nums[fast];  // 快指针也移动一步(此时不需要再移动两步,因为我们已经知道存在环)  
        }  
          
        // 返回相遇点的值,即为重复的数字  
        return slow;  
    }  
};

http://www.kler.cn/news/366144.html

相关文章:

  • ThinkPhp配置中间件解决跨域问题
  • redis的配置文件解析
  • Qt设置浏览器为父窗口,嵌入播放器窗口
  • ue5实现数字滚动增长
  • 设计模式(二)工厂模式详解
  • 使用js-enumerate报错Cannot set properties of undefined
  • 银行客户贷款行为数据挖掘与分析
  • 入侵检测算法平台部署LiteAIServer视频智能分析平台行人入侵检测算法
  • 分布式光伏发电系统电气一次部分设计(开题报告3)
  • GFF: Gated Fully Fusion for Semantic Segmentation门控融合语义分割-论文阅读笔记
  • ChainLink 预言机学习
  • 华为OD机试真题-矩形绘制-2024年OD统一考试(E卷)
  • 依赖关系是危险的
  • 深入探讨 HTTP 请求方法:GET、POST、PUT、DELETE 的实用指南
  • 泛型的特点
  • C++《vector》
  • 【实战案例】Django框架使用模板渲染视图页面及异常处理
  • Vscode连接WSL2(Ubuntu20.04)
  • P2818 天使的起誓
  • Linux: network: wireshark IO图的一个问题
  • Matlab学习02-matlab中的数据显示格式及符号变量
  • Flask-SQLAlchemy 组件
  • 排序算法 —— 直接选择排序
  • 在 Vue 渲染模板时,如何保留模板中的 HTML 注释?
  • 界面控件DevExpress WPF中文教程:Data Grid——表格视图概述
  • 家政服务管理系统小程序ssm+论文源码调试讲解