【leetcode刷题之路】初级算法——数组+字符串

1 数组

1.1 【双指针】删除有序数组中的重复项

https://leetcode.cn/problems/remove-duplicates-from-sorted-array/

这道题目用到了双指针,下面结合代码来讲解一下是怎么用

  • i:目前所遍历到的数组的位置
  • j:目前应该进行替换的位置

刚开始是i和j都指向数组的第二个元素(如果有两个的话,没有直接输出1),从数组的第二个位置开始,如果此时的数字与前面(num)相同,则i继续向后遍历,直到与num不同,此时把i所在位置的数字赋值给j所在位置和num,然后j向后移动一位,重复上述操作,直到遍历到数组的最后一个元素为止。

class Solution {
    
    
public:
    int removeDuplicates(vector<int>& nums) {
    
    
        int length = nums.size();
        int i = 1, j = 1, num = nums[0];
        int ans = 1;
        for(;i<length;i++)
        {
    
    
            if(nums[i] != num)
            {
    
    
                nums[j] = nums[i];
                num = nums[j];
                j++;
                ans++;
            }
            else continue;
        }
        return ans;
    }
};

1.2 【双指针】【贪心】买卖股票的最佳时机 II

https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-ii/

1.2.1 双指针

还是继续使用了双指针,low_price指的是目前遍历到的最低价格,初始为第一个元素,high_price指的是目前遍历到的最高价格,当找到最高价格的时候,说明产生了利润,这是计算本次的利润,并修改两个指针的值再次向后遍历,直到遍历完所有情况,不过这里还要考虑一下特殊情况,比如如果股票价格一天比一天低,最后利润应该为0,在这里定义了一个flag用来判断这个特殊情况。

class Solution {
    
    
public:
    int maxProfit(vector<int>& prices) {
    
    
        int low_price = prices[0];
        int high_price = 0;
        int sum = 0;
        int length = prices.size();
        bool flag = false;
        for(int i=1;i<length;i++)
        {
    
    
            if(prices[i] < low_price) 
            {
    
    
                low_price = prices[i];
                flag = false;
            }
            else
            {
    
    
                high_price = prices[i];
                sum = sum + high_price - low_price;
                low_price = prices[i];
                flag = true;
            }
        }
        if(sum==0 && !flag) return 0;
        else return sum;
    }
};

1.2.2 贪心

贪心算法是一种在每一步选择中都采取在当前状态下最好或最优(即最有利)的选择,从而希望导致结果是最好或最优的算法。对于本题而言,我们希望买卖股票可以挣钱,所以可以把这个问题分成若干个子问题,最终问题是如何在整个时间内收益最大,而子问题是把整个时间分为若干份,在每一份时间内保持最大收益,这里我的子问题是每两天计算一次收益,如果收益大于0就计入总收益,因为最后的总收益也是这些子问题内收益大于0的求和。

class Solution {
    
    
public:
    int maxProfit(vector<int>& prices) {
    
    
       int length = prices.size();
       int sum = 0;
       for(int i=1;i<length;i++)
       {
    
    
           int ssum = prices[i] - prices[i-1];
           if(ssum > 0) sum += ssum;
           else continue;
       }
       return sum;
    }
};

1.3 【暴力】轮转数组

https://leetcode.cn/problems/rotate-array/

暴力解决,遍历元素挨个赋值,如下所示

class Solution {
    
    
public:
    void rotate(vector<int>& nums, int k) {
    
    
        int n = nums.size();
        vector<int> ans = nums;
        for(int i=0;i<n;i++)
        {
    
    
            nums[(i+k)%n] = ans[i];
        }
    }
};

1.4 【位运算】只出现一次的数字

这个题目我想的有点讨巧了,因为重复的数字只出现两次,基本上一加一减就没了,所以先对数组进行排序,然后遍历时判断与前一个元素是否相同,不同加相同减,最后剩下的那个就是只出现一次的数字(貌似好像只适合这道题)

https://leetcode.cn/problems/single-number/

class Solution {
    
    
public:
    int singleNumber(vector<int>& nums) {
    
    
        sort(nums.begin(),nums.end());
        int n = nums.size();
        int ans = nums[0];
        for(int i=1;i<n;i++)
        {
    
    
            if(nums[i] != nums[i-1]) ans += nums[i];
            else ans -= nums[i];
        }
        return ans;
    }
};

1.5 【双指针】两个数组的交集 II

https://leetcode.cn/problems/intersection-of-two-arrays-ii/

双指针应该是这道题的常规思路了,先对两个数组进行排序,然后定义两个指针挨个比较大小,把相同元素存到ans中最后输出,不过要考虑好遍历结束的条件,其中一个数组遍历完之后就可以直接return了。

class Solution {
    
    
public:
    vector<int> intersect(vector<int>& nums1, vector<int>& nums2) {
    
    
        vector<int> ans;
        sort(nums1.begin(),nums1.end());
        sort(nums2.begin(),nums2.end());
        int n1 = nums1.size();
        int n2 = nums2.size();
        for(int i=0,j=0;i<n1,j<n2;)
        {
    
    
            if(i==n1) return ans;
            if(j==n2) return ans;

            if(nums1[i] > nums2[j]) j++;
            else if(nums1[i] < nums2[j]) i++;
            else
            {
    
    
                ans.push_back(nums1[i]);
                i++;
                j++;
            } 
        }
        return ans;
    }
};

1.6 【暴力】加一

https://leetcode.cn/problems/plus-one/

简单的进位加法题目,从数组的最后一个元素往前遍历即可,满十进一,不过要判断一下数组的第一个元素是否满十进一。

class Solution {
    
    
public:
    vector<int> plusOne(vector<int>& digits) {
    
    
        int n = digits.size();
        int x = 1;
        for(int i=n-1;i>=0;i--)
        {
    
    
            if(digits[i] + x > 9)
            {
    
    
                digits[i] = digits[i] + x - 10;
                x = 1;
            }
            else
            {
    
    
                digits[i] = digits[i] + x;
                x = 0;
            }
        }
        if(x==1) digits.insert(digits.begin(),1);
        return digits;
    }
};

1.7 【双指针】移动零

https://leetcode.cn/problems/move-zeroes/

采用双指针
(1)index为目前指向的元素,从头开始遍历,每次找到不为零的元素之后就赋值给index位置,最后将index之后的所有原始全部赋值为0即可。
(2)定义两个指针left和right,left左边的元素都为非0,left到right之间的元素都为0,刚开始时用right遍历数组,left指向0元素,当right遍历到的元素不为0时,与left交换元素。

//第一种方法
class Solution {
    
    
public:
    void moveZeroes(vector<int>& nums) {
    
    
        int n = nums.size();
        int index = 0;
        for(int i=0;i<n;i++)
        {
    
    
            if(nums[i]!=0)
            {
    
    
                nums[index] = nums[i];
                index++;
            }
        }
        for(int j=index;j<n;j++)
        {
    
    
            nums[j] = 0;
        }
    }
};

//第二种方法
class Solution {
    
    
public:
    void moveZeroes(vector<int>& nums) {
    
    
        int n = nums.size();
        int left = 0 , right = 0;
        while(right < n)
        {
    
    
            if(nums[right])
            {
    
    
                swap(nums[left],nums[right]);
                left++;
            }
            right++;
        }
    }
};

1.8 【双指针】【哈希表】两数之和

https://leetcode.cn/problems/two-sum/

(1)双指针,首先对数组进行排序,然后使用双指针对数组分别从前后进行遍历,最后把找到的两个数字在原数组中找到对应的下标。
(2)哈希表,遍历数组的时候找一下对应的与target的差值是否在哈希表中,如果在的话就直接返回结果,如果不在的话就把这个元素加入哈希表中继续遍历,这里要注意差值是target-nums[i],要不然搞反了碰见负数就只能找另一个负数了。

//第一种方法---双指针
class Solution {
    
    
public:
    vector<int> twoSum(vector<int>& nums, int target) {
    
    
        vector<int> nums2 = nums;
        sort(nums2.begin(),nums2.end());
        int n = nums2.size();
        int anum[2];
        vector<int> ans;
        for(int i=0,j=n-1;;)
        {
    
    
            if(nums2[i] + nums2[j] == target)
            {
    
    
                anum[0] = nums2[i];
                anum[1] = nums2[j];
                break;
            }
            else if(nums2[i] + nums2[j] > target) j--;
            else i++;
        }
        for(int i=0;i<n;i++)
        {
    
    
            if(nums[i]==anum[0])
            {
    
    
                ans.push_back(i);
                break;
            }
        }
        for(int j=n-1;j>=0;j--)
        {
    
    
            if(nums[j]==anum[1])
            {
    
    
                ans.push_back(j);
                break;
            }
        }
        sort(ans.begin(),ans.end());
        return ans;
    }
};

//第二种方法---哈希表
class Solution {
    
    
public:
    vector<int> twoSum(vector<int>& nums, int target) {
    
    
        unordered_map<int,int> hash_table;
        vector<int> ans;
        for(int i=0;i<nums.size();i++)
        {
    
    
            int x = target - nums[i];
            if(hash_table.count(x))
            {
    
    
                ans.push_back(i);
                ans.push_back(hash_table[x]);
                return ans;
            }
            else hash_table[nums[i]] = i;
        }
        return ans;
    }
};

1.9 【双指针】有效的数独

https://leetcode.cn/problems/valid-sudoku/

分别针对每一行、每一列和每个小九宫格用哈希表来判断是否有重复元素,注意小九宫格的九个元素的下标关系。

class Solution {
    
    
public:
    bool isValidSudoku(vector<vector<char>>& board) {
    
    
        bool flag = true;
        unordered_map<int,int> hash_table;
        //判断每行是否true
        for(int i=0;i<9;i++)
        {
    
    
            for(int j=0;j<9;j++)
            {
    
    
                if(board[i][j] != '.')
                {
    
    
                    int x = board[i][j] - '0';
                    if(hash_table.count(x)) return !flag;
                    else hash_table[x] = j;
                }
            }
            hash_table.clear();
        }
        //判断每列是否true
        for(int i=0;i<9;i++)
        {
    
    
            for(int j=0;j<9;j++)
            {
    
    
                if(board[j][i] != '.')
                {
    
    
                    int x = board[j][i] - '0';
                    if(hash_table.count(x)) return !flag;
                    else hash_table[x] = j;
                }
            }
            hash_table.clear();
        }
        //判断每个3*3是否true
        for(int k=0;k<9;k++)
        {
    
    
            int i_size = (k/3)*3;
            int j_size = (k%3)*3;
            for(int i=0;i<3;i++)
            {
    
    
                for(int j=0;j<3;j++)
                {
    
    
                    if(board[i+i_size][j+j_size] != '.')
                    {
    
    
                        int x = board[i+i_size][j+j_size] - '0';
                        if(hash_table.count(x)) return !flag;
                        else hash_table[x] = (i+1)*(j+1);
                    }
                }
            }
            hash_table.clear();
        }
        return flag;
    }
};

PS:考虑一下如何只遍历一次即可解决问题。

1.10 【数学】旋转图像

https://leetcode.cn/problems/rotate-image/

先对矩阵进行上下翻转,再对角线进行翻转,主要是理解90度这个翻转规则

class Solution {
    
    
public:
    void rotate(vector<vector<int>>& matrix) {
    
    
        int n = matrix.size();
        //先上下翻转
        for(int i=0;i<n/2;i++)
        {
    
    
            vector<int> temp = matrix[i];
            matrix[i] = matrix[n-1-i];
            matrix[n-1-i] = temp;
        }
        //对角线翻转
        for(int i=0;i<n-1;i++)
        {
    
    
            for(int j=i+1;j<n;j++)
            {
    
    
                int temp = matrix[i][j];
                matrix[i][j] = matrix[j][i];
                matrix[j][i] = temp;
            }
        }
    }
};

2 字符串

2.1 【双指针】反转字符串

https://leetcode.cn/problems/reverse-string/

利用双指针,一前一后遍历字符串数组,然后交换。

class Solution {
    
    
public:
    void reverseString(vector<char>& s) {
    
    
        int n = s.size();
        char temp;
        for(int i=0,j=n-1;i<j;i++,j--)
        {
    
    
            temp = s[i];
            s[i] = s[j];
            s[j] = temp;
        }
    }
};

2.2 【数学】整数反转

https://leetcode.cn/problems/reverse-integer/

这道题目考察到了int的范围,题目中给出的范围限制刚好是int所表示的整数范围,所以一开始先定义一个long型遍历,按照取余之后计算反转后的整数,如果这个整数的类型转为int之后与原来不一致了,说明这个整数的范围超出了int,否则没有超出。

class Solution {
    
    
public:
    int reverse(int x) {
    
    
        long ans = 0;
        while(x!=0)
        {
    
    
            int temp = x % 10;
            ans = ans*10 + temp;
            x /= 10;
        }
        if(int(ans)==ans) return ans;
        else return 0;
    }
};

2.3 【哈希表】字符串中的第一个唯一字符

https://leetcode.cn/problems/first-unique-character-in-a-string/

利用哈希表,如果遇到重复的字符,就把这个字符在哈希表中的value变为-1,否则存储index,最后遍历哈希表,输出第一个不为-1的index,否则返回-1。

class Solution {
    
    
public:
    int firstUniqChar(string s) {
    
    
        unordered_map<char,int> hash_table;
        int n = s.size();
        for(int i=n-1;i>=0;i--)
        {
    
    
            char temp = s[i];
            if(hash_table.count(temp)) hash_table[temp] = -1;
            else hash_table[temp] = i;
        }
        int m = hash_table.size();
        for(auto x:hash_table)
        {
    
    
            if(x.second != -1) return x.second;
        }
        return -1;
    }
};

2.4 【哈希表】有效的字母异位词

https://leetcode.cn/problems/valid-anagram/

分别对两个字符串构造哈希表,最后比较每个位置字符数量是否相等。

class Solution {
    
    
public:
    bool isAnagram(string s, string t) {
    
    
        int s_num[26]={
    
    0}, t_num[26]={
    
    0};
        for(int i=0;i<s.size();i++)
        {
    
    
            s_num[s[i]-'a']++;
        }
        for(int j=0;j<t.size();j++)
        {
    
    
            t_num[t[j]-'a']++;
        }
        for(int k=0;k<26;k++)
        {
    
    
            if(s_num[k]!=t_num[k]) return false;
        }
        return true;
    }
};

2.5 【双指针】验证回文串

https://leetcode.cn/problems/valid-palindrome/

使用双指针进行解题,但是在遍历的时候要注意判断一个地方,不只是’a’-'A’的值是32,‘P’-'0’的值也是32,所以在判断是否是同一大小写的时候加入两者是否都是字母的判断。

class Solution {
    
    
public:
    bool isPalindrome(string s) {
    
    
        int n = s.size();
        if(n<=1) return true;
        for(int i=0,j=n-1;i<j;i++,j--)
        {
    
    
            while(!isalnum(s[i]) && i<j)
            {
    
    
                i++;
            }
            while(!isalnum(s[j]) && i<j)
            {
    
    
                j--;
            }
            if(s[i]!=s[j] && (s[i]<'A' || s[j]<'A' || abs(s[i]-s[j])!=32)) return false;
        }
        return true;
    }
};

2.6 【暴力】字符串转换整数(atoi)

https://leetcode.cn/problems/string-to-integer-atoi/

这道题目很恶心,让我感觉总是根据需求在不断的改代码。
首先遍历字符串时候,找出这个数是正是负,之后对于找到的位置从后开始输出完整数字,这里建议大家认真读题,不是说后面有数字都要输出,而是紧接着你找到的可以判断符合的这一个字符往后遍历,如果没有整数就直接输出0。
下面记录下我遇到的两个坑
“20000000000000000000"和” 0000000000012345678"
第一个让我加了判断是否超过int的位数的判断,第二个让我加入了要从第一个不为零的数字开始判断是否超过int的位数判断。

class Solution {
    
    
public:
    int myAtoi(string s) {
    
    
        int n = s.size();
        int i = 0;
        long ans = 0;
        int ans_size = 0;
        bool flag = true;
        while(i<n)
        {
    
    
            if(s[i]=='-')
            {
    
    
                flag = false;
                i++;
                break;
            }
            else if(s[i]=='+')
            {
    
    
                i++;
                break;
            }
            else if(s[i]!=' ')
            {
    
    
                break;
            }
            i++;
        }
        while(i<n)
        {
    
    
            if(s[i]=='0') i++;
            else break;
        }
        for(;i<n;i++)
        {
    
    
            if(s[i]<'0' || s[i]>'9') break;
            else
            {
    
    
                int x = s[i] - '0';
                ans = ans * 10 + x;
                ans_size++;
                if(ans_size > 10) break;
            }
        }
        if(!flag) ans = -ans;
        if(int(ans)==ans) return ans;
        else
        {
    
    
            if(flag) return pow(2,31)-1;
            else return -pow(2,31);
        }
    }
};

2.7 【双指针】 找出字符串中第一个匹配项的下标

https://leetcode.cn/problems/find-the-index-of-the-first-occurrence-in-a-string/

其实这道题目最好的解决办法是kmp,但是可以自己练习一下普通的双指针查找,我实现的是没有回溯的双指针解法,也就是如果碰到不匹配的情况haystack的index是向后移动一位,后续会改进一下看看kmp怎么实现。

class Solution {
    
    
public:
    int strStr(string haystack, string needle) {
    
    
        int h_size = haystack.size();
        int n_size = needle.size();
        int n_index = 0;
        int ans = -1;
        for(int i=0;i<h_size;i++)
        {
    
    
            if(haystack[i] == needle[n_index])
            {
    
    
                ans = i;
                for(int j=i;j<h_size,n_index<n_size;j++,n_index++)
                {
    
    
                    if(haystack[j]!=needle[n_index])
                    {
    
    
                        n_index = 0;
                        ans = -1;
                        break;
                    }
                }
            }
        }
        return ans;
    }
};

2.8 【暴力】外观数列

https://leetcode.cn/problems/count-and-say/

暴力解决,注意观察数列特征,尤其是字符串的最后一位判断是否和前一位相同,也决定了要插入的数字是单独一个,还是和前面几个相同的数字同时插入,比如最后结尾是“222”就要插入“32”,而最后结尾是“223”就要插入“2213”。

class Solution {
    
    
public:
    string countAndSay(int n) {
    
    
        string ans = "11";
        int x_num = 1;
        if(n==1) return "1";
        if(n==2) return "11";
        for(int i=3;i<=n;i++)
        {
    
    
            string n_str;
            for(int j=0;j<ans.size();j++)
            {
    
    
                if(j==ans.size()-1)
                {
    
    
                    n_str.push_back(x_num + '0');
                    n_str.push_back(ans[j]);
                    x_num = 1;
                }
                else
                {
    
    
                    if(ans[j]==ans[j+1]) x_num++;
                    else
                    {
    
    
                        n_str.push_back(x_num + '0');
                        n_str.push_back(ans[j]);
                        x_num = 1;
                    }
                }
            }
            ans = n_str;
        }
        return ans;
    }
};

2.8 【字典树】最长公共前缀

https://leetcode.cn/problems/longest-common-prefix/

这里因为是要找出最长公共前缀,所以对strs里面的每一个字符串采取纵向比较,按个比对,一旦出现纵向不一致,立即结束循环输出ans。

class Solution {
    
    
public:
    string longestCommonPrefix(vector<string>& strs) {
    
    
        string ans = "";
        for(int i=0;strs[0][i];i++)
        {
    
    
            for(int j=0;j<strs.size()-1;j++)
            {
    
    
                if(strs[j][i]!=strs[j+1][i]) return ans;
            }
            ans.push_back(strs[0][i]);
        }
        return ans;
    }
};

おすすめ

転載: blog.csdn.net/qq_44528283/article/details/130256344