leetcode编程练习

这篇博客介绍了LeetCode中关于字符串、链表和二叉树的编程挑战,包括双指针、动态规划、回溯、中心扩散法等解题方法。涉及的题目有无重复字符的最长子串、最长回文子串、正则表达式匹配、盛最多水的容器、有效的括号等。同时,博主分享了链表专题和二叉树专题的解题思路,如删除链表倒数第N个节点、合并两个有序链表、岛屿数量、课程表问题等,以及如何使用递归和回溯法解决问题。
摘要由CSDN通过智能技术生成






一、字符串,数据(包含DP、STL)专题

3. 无重复字符的最长子串(双指针)

给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。

示例 1:

输入: "abcabcbb"
输出: 3 
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。

示例 2:

输入: "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。

示例 3:

输入: "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
     请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。

厦大计算机系机试第一题(只拿了一半好像,回过头来想到是ascii-‘0’)出现的负数造成的 ,哎菜的难受(如果ascii的值比’0’小就异常了)。
本题可采用滑动窗口的方法,重点是2个指针,i和j,分别表示最长子串的首端和末端。
9.9更新: 第一种方法效率更多,可以不适用map,也较好理解,其实双指针对于此题,我认为重点就是如何理解子串开头的慢指针,我的逻辑思维不好,因此举了一个稍微特殊的例子理解了慢指针的妙用。

(结合代码)双指针演示:
i(慢指针)
j(快指针)
a  b  c  d  b  e (uniquestr = a)
i   j
a  b  c  d  b  e(uniquestr = ab)
i      j
a  b  c  d  b  e(uniquestr = abc)
i         j
a  b  c  d  b  e(uniquestr = abcd)
i            j
a  b  c  d  b  e(此时不执行if的第一个分支,i移动)
   i         j
a  b  c  d  b  e(此时不执行if的第一个分支,i移动)
       i     j(此时,j才继续移动,所以i是会移动到不重复的位置上,(uniquestr = cdb))
a  b  c  d  b  e

// 无论i怎么移动,判断子串是否变大的依据是看j,而不是看num[s[i]]
// 如果s[j] 出现了重复,其实并不需要找到s[j]相同的字符,只需要将i依次
//向前移动,并还原num[s[i]]的值,切忌不要直接去想怎么马上移动到第一个重复
//的字符,因为只要这样子移动,而判断ans的变化与否还是看的是j,只要没移动
//到重复的字符,那么j的位置就不变改变,就不用担心出现错误。
class Solution {
   
public:
    int lengthOfLongestSubstring(string s) {
   
        // i j 双指针
        int len = s.size();
        int num[256] = {
   0};
        int i = 0, j = 0;
        int ans = 0;
        while(j < len){
   
            if(num[s[j]] == 0){
   
                ans = max(ans, j - i + 1);
                num[s[j]]++;
                j++;
            }else{
   
                num[s[i]] = 0;
                i++;
            }
        }
            return ans;
    }
};

5. 最长回文子串(中心扩散法和动态规划)

给定一个字符 串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。

示例 1:

输入: "babad"
输出: "bab"
注意: "aba" 也是一个有效答案。

示例 2:

输入: "cbbd"
输出: "bb"

(1)中心扩散法
需要两次判断,用来检测以x为中心的回文是奇数还是偶数的情况。巩固了C语言欠缺的知识(太差了,一脸泪,字符指针的返回值无法使用局部变量 ,必须动态分配内存给字符指针malloc函数,或者使用static作为全局变量,并且在使用malloc的时候,先必须申请的内存空间为返回的字符串的长度len+1,切一定要加上 ans[len] = '\0’结束符,否则无法编译通过。)

 char * longestPalindrome(char * s){
   
    int maxlen = 0;
    int start = 0;
    int len = strlen(s);
    if(len < 2){
   
        return s;
    }
    for(int h = 0; h < len; h++){
   
		int i = h;
		int j = i;
        while(i >= 0 && j <= len && s[i] == s[j] ){
   
            i--;
            j++;
        }
        if(maxlen < j - i -1){
   
            maxlen = j - i - 1;
            start = i + 1;
        }
        i = h;
        j = h + 1;
        while(i >= 0 && j <= len && s[i] == s[j] ){
   
            i--;
            j++;
        }
        if(maxlen < j - i -1){
   
            maxlen = j - i - 1;
            start = i + 1;
        }
     }
    char * ans =(char *) malloc(sizeof(char) * (maxlen + 1 ));
    strncpy(ans, s + start, maxlen);
    ans[maxlen] = '\0';
   // strcpy(s,ans);
    return ans;   
}```

(2)动态规划法
在这里插入图片描述

//动态规划
//C语言版本,不过这个leetcode通过不了,“cbbd” 本地这个这个回文串是“bb”,在服务器上却是“cbb”,
//无语了,不过对比了底下的C++版本可以通过,我觉得思路应该是没问题的,恳请批评指正
//dp[i][j] 表示 从尾i 到 头j组成的字符串是否为回文字符串,分别用1与0表示。
char * longestPalindrome(char * s){
   
  int dp[1001][1001];
  int len = strlen(s);
  int maxlen = 1;
  int start = 0;
  int end = 0;
  //首先,初始化,自己本身就是个长度为1的回文串。
  for(int i = 0; i < len; i++){
   
      dp[i][i] = 1;
  }
  //两层for循环,首先i从头遍历,j是一个重点,她是从i的位置向前遍历,
  //重点判断dp[i - 1][j + 1],而不是dp[i + 1][j - 1]]。
  for(int i = 0; i < len; i++){
   
      for(int j = i; j >= 0; j--){
    
          if((s[i] == s[j]) && (i - j < 2)){
   //这种是特殊情况,表示i与j相邻的情况。
              dp[i][j] = 1;
          }
          else if((s[i] == s[j]) && (dp[i - 1][j + 1] == 1)){
   //核心
                  dp[i][j] = 1;
              }          
          if((dp[i][j] == 1) && (i - j + 1 > maxlen)){
   //判断长度是否超过最大
              maxlen = i - j + 1;
              start = j ;
              end = i;
          }
     }
  }   
  char * ans =(char *) malloc(sizeof(char) * (maxlen + 1 ));
  strncpy(ans, s + start, maxlen);
  ans[maxlen] = '\0';
  return ans;  

}

//C++ 版本

class Solution {
   
public:
    string longestPalindrome(string s) {
   
        int len = s.length();
        if (len == 0)
            return s;
        bool dp[len][len];
        int start = 0, end = 0;
        for (int i = 0; i <len; i++)
            dp[i][i] = true;
        for (int r = 0; r < len; r++)
            for (int l = 0; l < r; l++)
                if (s[r]==s[l] && (r-l==1 || dp[l+1][r-1])) {
   
                    dp[l][r] = true;
                    if (r-l > end-start) {
   
                        start = l; 
                        end = r;
                    }
                    continue;
                }else{
   
                    dp[l][r] = false;
                }
        string ans = s.substr(start, end-start+1);
        return ans;
        
    }
};

10. 正则表达式匹配(动态规划)

思路:借鉴leetcode大佬的评论:@袁雷洋 今天提交完答案之后,又在网上看了几个解答的方法,还是感觉这个递归的方法逻辑最清晰,但是思路很容易就迷糊了.在这里稍微梳理一下。
首先建立了一个全部为False的二维矩阵保存递归的结果,行数是(字符串长度+1),列数是(p的长度+1).这里为什么要多建一行一列呢?因为需要考虑到p和s为空的情况.
其中dp[i][j]表明字符串s[:i]与p[:j]的匹配结果.(其中s[:i]最后一个元素为s[i-1],p[:j]最后一个元素为p[j-1],搞懂这一点才看得懂代码的)
首先处理i为0(s==’’)或者j为0(p==’’)的情况.当p==’‘时,只有s==’‘时二者才能匹配,所以首列中,只有首个元素为0,即dp[0][0]=0. 当s==’‘时,只有p==’‘或者p==‘x*y*’(或类似形式)时,二者才能匹配. 所以有
dp[0][j]= =( j >= 2) and (p[j-1] = =’*’) and (dp[0][j-2]).
有必要对上面这个式子解释: 首先j>=2,因为j=1时,p不为空,也不存在首个元素就为*的情况,所以此时p与s肯定无法匹配.dp[0][j]对应p的子串,最后一个元素为p[j-1],只有p[j-1]= =’*'时才有可能匹配成功.同时需要dp[0][j-2]同样为True时,dp[0][j]才能为True.(这个地方好好理解一下,对下面的总代码理解很重要)
下面就使用双重循环开始递归了.dp[i][j]对应s的子串,最后一个元素为s[i-1],对应p的子串,最后一个元素为p[j-1].
(1)首先判断p[j-1]是否为’*’.
如果p[j-1] == '’,那么’‘前面肯定是有一个字母的(比如说b吧),那么需要同时考虑’b*’,此时分两种情况:
①’b*'是无用的:
比如s=‘aaaa’,p=‘a*b*’.此时s与p的配对结果与s与’a*'配对的结果是一样的.所以有 dp[i][j] = dp[i][j-2].
②’b*'是有用的:
比如s=‘aabb’,p=‘a*b*’.之前s=‘aab’,p='a*b’时二者已经配对过了,所以s=‘aab’,p=‘a*b*‘时,二者同样能配对,同样需要判断s[i-1]是否与p[j-2]相同,或者p[j-2]==’.’(即可以代替任何字符)。
所以有 dp[i][j] = dp[i-1][j] and (p[j-2] = = s[i-1] or p[j-2] = = ‘.’)
1与2两种情况有一种成立就行,即取或.
if p[j-1] == ‘*’: dp[i][j] = dp[i][j-2] or (dp[i-1][j] and (p[j-2] = = s[i-1] or p[j-2] == ‘.’))
(2)如果p[j-1]不为’*’.
判断起来就相对简单了.首先要考虑之前的子串是否匹配成功了,即dp[i-1][j-1]的值,同时要考虑dp[i][j]对应的s的子串最后一位s[i-1],p的子串p[j-1]是否相等, p[j-1] == '.‘时同样满足情况,毕竟’.'是万能匹配符. 所以就有 else: dp[i][j] = dp[i-1][j-1] and(p[j-1] == s[i-1] or p[j-1] == ‘.’)
希望能对大家有所帮助。

#include <stdio.h>
bool isMatch(char * s, char * p) {
   
    int sl = strlen(s);
    int pl = strlen(p); 
    //首先建立了一个全部为False的二维矩阵保存递归的结果,行数是(字符串长度+1),列数是(p的长度+1).这里为什么要多建一行一列呢?因为需要考虑到p和s为空的情况. 其中dp[i][j]表明字符串s[:i]与p[:j]的匹配结果.(其中s[:i]最后一个元素为s[i-1],p[:j]最后一个元素为p[j-1],搞懂这一点才看得懂代码的)
    bool dp[sl + 1][pl + 1];
    memset(dp, false, sizeof(dp));
   //初始化 三步
    dp[0][0] = true;
    
    for(int i = 1; i < strlen(s) + 1; i++){
   
        dp[i][0] = false;
    }
    for(int j = 1; j < strlen(p) + 1; j++){
   
        dp[0][j] = j > 1 && dp[0][j - 2]   && p[j - 1] == '*';
    }
    // 有必要对上面这个式子解释: 首先j>=2,因为j=1时,p不为空,也不存在首个元素就为*的情况,所以此时p与s肯定无法匹配.dp[0][j]对应p的子串,最后一个元素为p[j-1],只有p[j-1]=='*'时才有可能匹配成功.同时需要dp[0][j-2]同样为True时,dp[0][j]才能为True.(这个地方好好理解一下,对下面的总代码理解很重要)
    
    for(int i = 1; i < strlen(s) + 1; i++){
   
        for(int j = 1; j < strlen(p) + 1; j++){
   
            if(p[j - 1] == '*'){
   
                dp[i][j] = dp[i][j - 2] ||( (p[j - 2] == s[i - 1] || p[j - 2] =='.')  && dp[i - 1][j]);
            }else{
   
                dp[i][j] = dp[i - 1][j - 1] &&( (s[i - 1] == p[j - 1]) || p[j - 1] == '.');
            }
        }
    }
    return dp[strlen(s)][strlen(p)];
}

11. 盛最多水的容器

给定 n 个非负整数 a1,a2,…,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0)。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。

说明:你不能倾斜容器,且 n 的值至少为 2。
在这里插入图片描述
示例:

输入: [1,8,6,2,5,4,8,3,7]
输出: 49
//双指针 i, j
class Solution {
   
public:
    int maxArea(vector<int>& height) {
   
        if(height.size() <= 1) return -1;
        int i = 0, j = height.size() - 1, res = 0;
        while(i < j){
   
            int h = min(height[i], height[j]);
            res = max(res, h * (j - i));
            if(height[i] < height[j]) ++i;
            else --j;
        }
        return res;
    }
};

15. 三数之和(双指针)

双指针的运用,减少时间复杂度。我之前的方法不够好,多次运用vector和set去重,下方为大神写的,简洁易懂。

class Solution {
   
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
   
        int len = nums.size();
        vector<vector<int>> ans;
        sort(nums.begin(),nums.end());
        set<vector<int>> s;
        if(len < 3){
   
            return ans;
        }
        int i, j, k;
        for(i = 0; i < len; i++){
   
            if(nums[i] > 0)
                break;         
            vector<int> res = nums;
            res.erase(res.begin() + i);
            j = 0;
            k = res.size() - 1;
            while(j < k &&(i == 0  ||  nums[i] > nums[i - 1])){
   
                if(res[j] + res[k] + nums[i] == 0)
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合;、下 4载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合;、下载 4使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合;、下载 4使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
LeetCode是一个著名的在线编程库,可以帮助计算机程序员提升编程能力。LeetCode目涵盖了算法、数据结构、字符串、数组、链表等多个方面的知识,对于求职面试以及算法竞赛准备非常有帮助。 LeetCode上的编程目分为简单、中等和困难三个难度级别。每个目都有详细的目描述、输入输出示例以及参考答案等内容。在解决每个问时,我们需要仔细阅读目,并根据目要求提供正确的解答。 通常,我们需要使用编程语言如Python、C++等来实现解思路。在编码之前,我们需要先分析问,并找到合适的算法或数据结构来解决问。一般来说,我们可以使用递归、迭代、动态规划等方法来解决常见的编程。 在LeetCode上,我们可以提交解答,并测试解答是否通过了所有的测试用例。如果通过了所有的测试用例,我们就可以得到目的AC(Accepted)结果,并获得该目的通过证书。如果没有通过所有的测试用例,我们可以检查自己的解答,查找解答中的错误或者不完善之处,并进行修改和优化。 总之,LeetCode编程是一个优秀的学习和练习编程的平台。通过解答LeetCode上的编程目,我们可以提升自己的编程能力,并且培养解决问的思维方式。无论是求职面试还是算法竞赛,LeetCode编程都是非常有帮助的资源。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值