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<int> twoSum(vector<int>& nums, int target) {
        
    }
};

上面是一開始系統給的

居然有vector,感覺自己並不太熟,於是查了下vector的用法:

從菜鳥教程上摘了部分:

一、什麼是vector?

向量是一個封裝了動態大小數組的順序容器,他能夠存放各種類型的對象。可以簡單的認爲,向量是一個能夠存放任意類型的動態數組。

二、容器特性

1、順序序列。順序容器中的元素按照嚴格的線性順序排序。可以通過元素在序列中的位置(!!!)訪問對應的元素

2、動態數組。支持對序列中的任意元素進行快速直接訪問,甚至可以通過指針算述進行該操作。操供了在序列末尾相對快速地添加/刪除元素的操作。

3、能夠感知內存分配器的(Allocator-aware)容器使用一個內存分配器對象來動態地處理它的存儲需求。

三、基本用法及函數

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

Vector< vector< int> >v; 二維向量//這裏最外的<>要有空格。否則在比較舊的編譯器下無法通過

補充:網上一般都會有一維向量的去重,二維向量的去重是這樣的類似,

vector< vector <int> >ans;

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

函數:

補充vector的排序:

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

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

現在開始做題,於是我嘗試寫了一下,用了兩層循環,哈哈,然後過了,發現居然還有這個功能:

這一看就有神奇方法,於是我想了想:

感覺是可以在O(n)的時間內做完,比如對於nums中的每一個元素進行標記,然後從0到target/2,判斷該數和(target-該數)是否被標記過,這裏要注意類似8(4+4)這種兩數相同的情況,還要注意要有負數這種情況,然後我就寫了一發,哈哈,棧溢出,然後我就不會寫了

看了一下題解,amazing!哈希表!怎麼把他忘了!

先學學哈希表:

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

第一種方法:兩遍哈希

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        vector<int>ans;
        map<int,int> mapTest;
        for(int i=0;i<nums.size();i++){
            mapTest[nums[i]]=i;
        }
        for(int i=0;i<nums.size();i++){
            if(mapTest.find(target-nums[i])!=mapTest.end()&&mapTest[target-nums[i]]!=i){
                ans.push_back(i);
                ans.push_back(mapTest[target-nums[i]]);
                break;
            }
        }
        return ans;
    }
};

Amazing!

題解中還提供了一種一遍循環的方法,就是在邊構造hash,邊查找

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        vector<int>ans;
        map<int,int> mapTest;
        for(int i=0;i<nums.size();i++){
            if(mapTest.find(target-nums[i])!=mapTest.end()){
                ans.push_back(i);
                ans.push_back(mapTest[target-nums[i]]);
            }
            mapTest[nums[i]]=i;
        }
        return ans;
    }
};

這種方法有個比上一個方法較好的地方就是:

我給你一組【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;
        map<char,int>mapTest;
        int i=0,j=0;
        for(;i<s.length();i++){
            if(mapTest.find(s[i])!=mapTest.end()){
                //只能往前移
                if(mapTest[s[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挺好用的,一交發現有點兒慢:

不行,再想想:

找了一下博客裏有這樣說的,然後我改用了unordered_map,結果是一樣的,再想想:

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

好像是一樣的,再看看評論區吧

天呢!這裏有個用了兩層循環來找重複的居然用時16ms。。。

不過好像基本思路基本都是一樣的,繼續!

第五題    最長迴文子串

好熟悉的一題:

給定一個字符串 s,找到 s 中最長的迴文子串。你可以假設 s 的最大長度爲 1000。

示例 1:

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

輸入: "cbbd"
輸出: "bb"

解法一:

一開始忘了馬拉車!!!所以一番思索,結果想了個O(n^2)的。。。

想的是按照迴文串的性質,我以字符串中間元素作爲迴文串的中心,讓他同時往兩個方向(左右)移動,在移動過程中,同時判斷以它爲中心的迴文串的長度,並記錄最大值的左右屆,然後最後根據左右屆返回對應的字符串即可。這裏需要注意的是由於原字符串的長度可奇可偶,所以若爲偶數,則中心不太好找,所以我們將其做類似以下的擴展:

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

結果如下:

代碼如下:

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字形變換

又是字符串的題

這種題我想了想,覺得一般都是有規律的,然後我找了找,果然有:

我將轉化爲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)的處理,然後注意邊界就可以了

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;i++){
                if(i==0){
                    for(int j=0;j<groupNum;j++){
                        ans+=s[j*sum];
                    }
                    if(shengyuNum!=0){
                        ans+=s[groupNum*sum];
                    }
                }else if(i==numRows-1){
                    for(int j=0;j<groupNum;j++){
                        ans+=s[numRows-1+j*sum];
                    }
                    if(shengyuNum>=numRows){
                        ans+=s[numRows-1+groupNum*sum];
                    }
                }else{
                    for(int j=0;j<groupNum;j++){
                        ans+=s[i+j*sum];
                        ans+=s[(j+1)*sum-i];
                    }
                    if(shengyuNum<=numRows){
                        if((groupNum*sum+i)<s.length()){
                            ans+=s[groupNum*sum+i];
                        }
                    }else {
                        int temp=shengyuNum-numRows;
                        if(i>=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;
    }
};

速度不快,看看人家題解怎麼寫的:

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(i<str.length()){
            if(str[i]!=' '){
                break;
            }
            i++;
        }
        while(i<str.length()){
            s+=str[i++];
        }
        //進行處理
        if(s.length()==0){
            ans=0;
        }else if(s[0]<48||s[0]>57){
            if(s[0]!='+'&&s[0]!='-'){
                ans=0;
            }else{
                //這種情況一開始是+、-號
                if(s[0]=='-'){
                    for(int g=1;g<s.length();g++){
                        if(s[g]<48||s[g]>57){
                            break;
                        }
                        if(ans<INT_MIN/10||(ans==INT_MIN/10&&-1*(s[g]-'0')<-8)){
                            ans=INT_MIN;
                            break;
                        }
                        if(ans>0){
                            ans=ans*-1;
                        }
                        ans=ans*10+-1*(s[g]-'0');
                    }
                }else{
                    for(int g=1;g<s.length();g++){
                        if(s[g]<48||s[g]>57){
                            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;h<s.length();h++){
                if(s[h]<48||s[h]>57){
                    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;
    }
};

有點兒慢,難道這題有什麼好方法???我去看看題解:

正則表達式!!!我在這就不說了,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多一位循環才結束

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章