LeetCode刷题之路(一)(1~10)

突然想起有一个做题的网站(LeetCode)地址:https://leetcode-cn.com/,心血来潮,于是注册做了第一题“两数之和”

感觉非常有意思,因为它是给定你初始格式,让你来完成里面的函数,和之前完全由自己写又不一样,上来第一道题差点儿把自己

看懵了,以后会不时做几道题,并且补充相关知识点!

目录:

第一题    两数之和

第二题是链表就不说了(不过也得不时回顾,要不然就忘了!!!)

第三题    无重复字符的最长子串

第四题    先空着(不是太懂)

第五题    最长回文子串

第六题    Z字形变换

第七题    整数反转

第八题    字符串转换整数(atoi)

第九题    回文数

第十题    标为困难,先空着嘻嘻

第一题    两数之和

给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。

你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。

示例:

给定 nums = [2, 7, 11, 15], target = 9

因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]

class Solution {
public:
    vector twoSum(vector& nums, int target) {
        
    }
};

上面是一开始系统给的

居然有vector,感觉自己并不太熟,于是查了下vector的用法:

从菜鸟教程上摘了部分:

一、什么是vector?

向量是一个封装了动态大小数组的顺序容器,他能够存放各种类型的对象。可以简单的认为,向量是一个能够存放任意类型的动态数组。

二、容器特性

1、顺序序列。顺序容器中的元素按照严格的线性顺序排序。可以通过元素在序列中的位置(!!!)访问对应的元素

2、动态数组。支持对序列中的任意元素进行快速直接访问,甚至可以通过指针算述进行该操作。操供了在序列末尾相对快速地添加/删除元素的操作。

3、能够感知内存分配器的(Allocator-aware)容器使用一个内存分配器对象来动态地处理它的存储需求。

三、基本用法及函数

引用:
#include < vector> 
using namespace std;

Vector< vector< int> >v; 二维向量//这里最外的<>要有空格。否则在比较旧的编译器下无法通过

补充:网上一般都会有一维向量的去重,二维向量的去重是这样的类似,

vector< vector >ans;

vector>::iterator IE = unique(ans.begin(),ans.end());
ans.erase(IE,ans.end());

函数:

LeetCode刷题之路(一)(1~10)_第1张图片

补充vector的排序:

sort(vector.begin(),vector.end());

其他具体内容看https://www.runoob.com/w3cnote/cpp-vector-container-analysis.html

现在开始做题,于是我尝试写了一下,用了两层循环,哈哈,然后过了,发现居然还有这个功能:

LeetCode刷题之路(一)(1~10)_第2张图片

这一看就有神奇方法,于是我想了想:

感觉是可以在O(n)的时间内做完,比如对于nums中的每一个元素进行标记,然后从0到target/2,判断该数和(target-该数)是否被标记过,这里要注意类似8(4+4)这种两数相同的情况,还要注意要有负数这种情况,然后我就写了一发,哈哈,栈溢出,然后我就不会写了

看了一下题解,amazing!哈希表!怎么把他忘了!

先学学哈希表:

见W3Cschoolhttps://www.w3cschool.cn/cpp/cpp-fu8l2ppt.html

第一种方法:两遍哈希

class Solution {
public:
    vector twoSum(vector& nums, int target) {
        vectorans;
        map mapTest;
        for(int i=0;i

LeetCode刷题之路(一)(1~10)_第3张图片

Amazing!

题解中还提供了一种一遍循环的方法,就是在边构造hash,边查找

class Solution {
public:
    vector twoSum(vector& nums, int target) {
        vectorans;
        map mapTest;
        for(int i=0;i

这种方法有个比上一个方法较好的地方就是:

我给你一组【3,3】6这个例子,你按第一个方法去做时,不好理解,因为此时map【3】=1不等于0

但是第二种办法就很好,在加入第二个3之前,就可以先找到第一个3的value,然后才将其覆盖

Amazing!原来LeetCode这么有趣!

第三题    无重复字长的最长子串

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

示例 1:

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

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

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

遇到字符串的题我就比较懵逼了,不过这题标明中等,咱就来看一下吧,这题要求的是最长的字串的长度,一想应该是这种解题思路:

给两个指针从字符串的头开始,一个先不动,一个每次动一个,当遇到一个字符在前面出现过(也就是重复)时,将那个之前不动的向后提(只能是向后提!),提到那个产生重复的前面。在整个过程中,不断更新(j-i+1)和答案之间的最大值即可。

在这里的难点应该就是如何知道是否重复,以及产生重复的元素的位置在哪儿,想了一下,Amazing!上面刚用了map,这里也可以用一下:

另外注意下C++中字符串的遍历!!!

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        int ans=0;
        mapmapTest;
        int i=0,j=0;
        for(;i=j){
                    j=mapTest[s[i]]+1;
                }
                mapTest[s[i]]=i;
                
                ans=max(ans,i-j+1);
            }else{
                mapTest[s[i]]=i;
                ans=max(ans,i-j+1);
            }
        }
        return ans;
    }
};

map挺好用的,一交发现有点儿慢:

不行,再想想:

LeetCode刷题之路(一)(1~10)_第4张图片

找了一下博客里有这样说的,然后我改用了unordered_map,结果是一样的,再想想:

不行,想不到了,看了下题解,哈哈,也使用了map,看看有啥不一样?

好像是一样的,再看看评论区吧

天呢!这里有个用了两层循环来找重复的居然用时16ms。。。

不过好像基本思路基本都是一样的,继续!

第五题    最长回文子串

好熟悉的一题:

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

示例 1:

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

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

解法一:

一开始忘了马拉车!!!所以一番思索,结果想了个O(n^2)的。。。

想的是按照回文串的性质,我以字符串中间元素作为回文串的中心,让他同时往两个方向(左右)移动,在移动过程中,同时判断以它为中心的回文串的长度,并记录最大值的左右届,然后最后根据左右届返回对应的字符串即可。这里需要注意的是由于原字符串的长度可奇可偶,所以若为偶数,则中心不太好找,所以我们将其做类似以下的扩展:

abcd             ===>      @a@b@c@d@    就是插入一个不常用的字符串即可

结果如下:

LeetCode刷题之路(一)(1~10)_第5张图片

代码如下:

class Solution {
public:
    string longestPalindrome(string s) {
        char *change=new char[2*s.length()+1];
        for(int i=0;i<2*s.length()+1;i++){
            if(i%2==1){
                change[i]=s[(i-1)/2];
            }else{
                change[i]='@';
            }
        }
        int ans=0;
        int left=0;
        int right=0;
        int middle1=s.length();
        int middle2=s.length();
        int length=s.length();
        while(middle1>0){
            int l=middle1;
            int r=middle1;
            while(l>=0&&r<2*length+1){
                if(change[l]!=change[r]){
                    break;
                }
                if((r-l)/2>ans){
                    ans=(r-l)/2;
                    left=l/2;
                    right=(r-2)/2;
                }
                l--;
                r++;
            }
            middle1-=1;
        }
        while(middle2<2*length){
            int l=middle2;
            int r=middle2;
            while(l>=0&&r<2*length+1){
                if(change[l]!=change[r]){
                    break;
                }
                if((r-l)/2>ans){
                    ans=(r-l)/2;
                    left=l/2;
                    right=(r-2)/2;
                }
                l--;
                r++;
            }
            middle2+=1;
        }
        string Answer="";
        for(int i=left;i<=right;i++){
            Answer+=s[i];
        }
        return Answer;
    }
};

然后看了一下题解!!!马拉车!!!

解法二:马拉车

我们先学习一下马拉车,这个不太好理解:

第六题    Z字形变换

又是字符串的题

LeetCode刷题之路(一)(1~10)_第6张图片

这种题我想了想,觉得一般都是有规律的,然后我找了找,果然有:

我将转化为Z字形的字符串分成了几组(按    ||        这种形式分的)

                                                                       ||    ||

                                                                       ||

比如上面第一个例子   L               是一组

                                   E       T    

                                   E

这种每组拥有的字符数量为2*numRows-2,能分s.length()/(2*numRows-2)这些组,剩余s.length()%(2*numRows-2)这些字符

然后按Z字形行遍历,找每一行添加进答案中的字符的下标的规律即可,比较难处理的就是剩余那部分字符的处理,不过你可以看成第(s.length()/(2*numRows-2)+1)的处理,然后注意边界就可以了

LeetCode刷题之路(一)(1~10)_第7张图片

class Solution {
public:
    string convert(string s, int numRows) {
        string ans="";
        if(numRows==1){
            ans+=s;
        }else{
            int sum=2*numRows-2;
            int groupNum=s.length()/sum;
            int shengyuNum=s.length()%sum;
            for(int i=0;i=numRows){
                        ans+=s[numRows-1+groupNum*sum];
                    }
                }else{
                    for(int j=0;j=numRows-temp-1){
                            ans+=s[groupNum*sum+i];
                            ans+=s[(groupNum+1)*sum-i];
                        }else{
                            ans+=s[groupNum*sum+i];
                        }
                    }
                }
            
            }
        }
        return ans;
    }
};

写的很麻烦!

第七题    整数反转

总算遇到个被标为简单的题了

给出一个 32 位的有符号整数,你需要将这个整数中每位上的数字进行反转。

示例 1:

输入: 123
输出: 321
 示例 2:

输入: -123
输出: -321
示例 3:

输入: 120
输出: 21
注意:

假设我们的环境只能存储得下 32 位的有符号整数,则其数值范围为 [−2^31,  2^31 − 1]。请根据这个假设,如果反转后整数溢出那么就返回 0。

这题一看,不就是整数反转吗,但是这题有个非常重要的就是他给定了最后答案的数据范围,若超过了,只能标为0,然后我就在每一遍答案*10的过程中进行检测(用了long感觉有点儿投机取巧)

class Solution {
public:
    int reverse(int x) {
        long ans=0;
        long temp=x;
        while(temp!=0){
            long y=temp%10;
            ans+=y;
            if((ans*10<-2147483648&&temp<=-10)||(ans*10>=2147483648)&&temp>=10){
                ans=0;                     
                break;
            }else{
                if(temp>=10||temp<=-10){
                    ans*=10;    
                }
                    temp=temp/10;
                }
        
        }
        return ans;
    }
};

LeetCode刷题之路(一)(1~10)_第8张图片

速度不快,看看人家题解怎么写的:

class Solution {
public:
    int reverse(int x) {
        int rev = 0;
        while (x != 0) {
            int pop = x % 10;
            x /= 10;
            if (rev > INT_MAX/10 || (rev == INT_MAX / 10 && pop > 7)) return 0;
            if (rev < INT_MIN/10 || (rev == INT_MIN / 10 && pop < -8)) return 0;
            rev = rev * 10 + pop;
        }
        return rev;
    }
};

提前检测,大体思路差不多,不过这种将我那种*10检测,转为了/10了,避免了*10导致int溢出,而换用long的操作了。。!!

第八题    字符串转换整数(atoi)

这题是一道字符串处理题吧,有点儿复杂。。

请你来实现一个 atoi 函数,使其能将字符串转换成整数。

首先,该函数会根据需要丢弃无用的开头空格字符,直到寻找到第一个非空格的字符为止。

当我们寻找到的第一个非空字符为正或者负号时,则将该符号与之后面尽可能多的连续数字组合起来,作为该整数的正负号;假如第一个非空字符是数字,则直接将其与之后连续的数字字符组合起来,形成整数。

该字符串除了有效的整数部分之后也可能会存在多余的字符,这些字符可以被忽略,它们对于函数不应该造成影响。

注意:假如该字符串中的第一个非空格字符不是一个有效整数字符、字符串为空或字符串仅包含空白字符时,则你的函数不需要进行转换。

在任何情况下,若函数不能进行有效的转换时,请返回 0。

说明:

假设我们的环境只能存储 32 位大小的有符号整数,那么其数值范围为 [−231,  231 − 1]。如果数值超过这个范围,qing返回  INT_MAX (231 − 1) 或 INT_MIN (−231) 。

示例 1:

输入: "42"
输出: 42
示例 2:

输入: "   -42"
输出: -42
解释: 第一个非空白字符为 '-', 它是一个负号。
     我们尽可能将负号与后面所有连续出现的数字组合起来,最后得到 -42 。
示例 3:

输入: "4193 with words"
输出: 4193
解释: 转换截止于数字 '3' ,因为它的下一个字符不为数字。
示例 4:

输入: "words and 987"
输出: 0
解释: 第一个非空字符是 'w', 但它不是数字或正、负号。
     因此无法执行有效的转换。
示例 5:

输入: "-91283472332"
输出: -2147483648
解释: 数字 "-91283472332" 超过 32 位有符号整数范围。 
     因此返回 INT_MIN (−231) 。

这题我的思路就是先去掉原串中字符前面的空格,然后进行处理即可,里面题目中的溢出,我们就可以用第七题中的检测方法来做了!!!

class Solution {
public:
    int myAtoi(string str) {
        int ans=0;
        string s="";
        //先删掉所有字符前的空格吧
        int i=0;
        while(i57){
            if(s[0]!='+'&&s[0]!='-'){
                ans=0;
            }else{
                //这种情况一开始是+、-号
                if(s[0]=='-'){
                    for(int g=1;g57){
                            break;
                        }
                        if(ans0){
                            ans=ans*-1;
                        }
                        ans=ans*10+-1*(s[g]-'0');
                    }
                }else{
                    for(int g=1;g57){
                            break;
                        }
                        if(ans>INT_MAX/10||(ans==INT_MAX/10&&(s[g]-'0')>7)){
                            ans=INT_MAX;
                            break;
                        }
                        ans=ans*10+(s[g]-'0');
                    }
                }
            }
        }else{
            //这种情况从一开始就是数字,而且是正的
            for(int h=0;h57){
                    break;
                }
                //检测溢出,用刚做过一题的方法
                if(ans>INT_MAX/10||(ans==INT_MAX/10&&(s[h]-'0')>7)){
                    ans=INT_MAX;
                    break;
                }
                ans=ans*10+(s[h]-'0');
                
            }
        }
        
        return ans;
    }
};

LeetCode刷题之路(一)(1~10)_第9张图片

有点儿慢,难道这题有什么好方法???我去看看题解:

正则表达式!!!我在这就不说了,python很万能哈哈,想了解的同学可以看一下我之前写的一篇博客

https://blog.csdn.net/weixin_42412973/article/details/98480252

第九题    回文数

判断一个整数是否是回文数。回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。

示例 1:

输入: 121
输出: true
示例 2:

输入: -121
输出: false
解释: 从左向右读, 为 -121 。 从右向左读, 为 121- 。因此它不是一个回文数。
示例 3:

输入: 10
输出: false
解释: 从右向左读, 为 01 。因此它不是一个回文数。
进阶:

你能不将整数转为字符串来解决这个问题吗?

题目中“你能不将整数转为字符串来解决这个问题吗?”,一想,那就将整数倒置比对吧,可能会int溢出,所以改为long(不过考虑空间的话就不行了),然后就过了:

class Solution {
public:
    bool isPalindrome(int x) {
        bool ans=false;
        if(x<0){
            ans=false;
        }else{
            long temp=0;
            long temp2=x;
            while(x!=0){
                int y=x%10;
                x/=10;
                temp=temp*10+y;
            }
            if(temp==temp2){
                ans=true;
            }
        }
        return ans;
    }
};

看了下题解,果然自己太年轻了:

这里需要考虑如何到一半了?题解中(C#)给出的很精妙,同时考虑了是否为回文串:

public class Solution {
    public bool IsPalindrome(int x) {
        // 特殊情况:
        // 如上所述,当 x < 0 时,x 不是回文数。
        // 同样地,如果数字的最后一位是 0,为了使该数字为回文,
        // 则其第一位数字也应该是 0
        // 只有 0 满足这一属性
        if(x < 0 || (x % 10 == 0 && x != 0)) {
            return false;
        }

        int revertedNumber = 0;
        while(x > revertedNumber) {
            revertedNumber = revertedNumber * 10 + x % 10;
            x /= 10;
        }

        // 当数字长度为奇数时,我们可以通过 revertedNumber/10 去除处于中位的数字。
        // 例如,当输入为 12321 时,在 while 循环的末尾我们可以得到 x = 12,revertedNumber = 123,
        // 由于处于中位的数字不影响回文(它总是与自己相等),所以我们可以简单地将其去除。
        return x == revertedNumber || x == revertedNumber/10;
    }
}

这里的while(x>revertedNumber)咱想想为啥这个可以当作已经到一半的标志:

对于偶数位的数:在此循环中,要想x<=revertedNumber只能是当revertedNumber的位数超过或等于原数的一半,同时也可以判断是否为回文串了,若是的话,应该是x==revertedNumber

对于奇数位的数同理,不过此时revertedNumber一定比x多一位循环才结束

你可能感兴趣的:(LeetCode刷题)