算法笔记_12.16-12.22

这周继续刷数组类的算法题~

11.验证回文串

给定一个字符串,验证它是否是回文串,只考虑字母和数字字符,可以忽略字母的大小写。

示例 1:

输入: "A man, a plan, a canal: Panama"
输出: true

说明:本题中,我们将空字符串定义为有效的回文串。

解法

  • 双指针法
    分别使用两个指针从前后向中间扫描,遇到不是字母/数字的跳过,对每对符合要求字符进行比较是否相同
class Solution {
public:
    bool isPalindrome(string s) {
        int i1 = 0;
        int i2 = s.size()-1;

        while(i1 < i2){
            while(!is_char(s[i1]) && (i1 < i2)) ++i1;
            while(!is_char(s[i2]) && (i1 < i2)) --i2;
            if(!equal_character(s[i1], s[i2])) return false;
            ++i1;
            --i2;
        }

        return true;    
    }

    bool is_char(char c){
        if( ((c >= '0') && (c <= '9')) ||
            ((c >= 'a') && (c <='z')) || 
            ((c >= 'A') && (c <= 'Z'))) return true;
        else return false;
    }

    bool equal_character(char c1, char c2){
        int char_dis = 32;
        if((c1 >= 'a') && (c1 <= 'z')){
            c1 -= char_dis;
        }

        if((c2 >= 'a') && (c2 <= 'z')){
            c2 -= char_dis;
        }
        if(c1 == c2) return true;
        else return false;
    }
};

复杂度分析

时间复杂度:O(n),只扫描一遍。

空间复杂度:O(1),无需额外空间。


12. 两数之和 II - 输入有序数组

给定一个已按照升序排列 的有序数组,找到两个数使得它们相加之和等于目标数。

函数应该返回这两个下标值 index1 和 index2,其中 index1 必须小于 index2。

说明:

返回的下标值(index1 和 index2)是从1开始计数的。

你可以假设每个输入只对应唯一的答案,而且你不可以重复使用相同的元素。

示例 1:

输入: numbers = [2, 7, 11, 15], target = 9

输出: [1,2]

解释: 2 与 7 之和等于目标数 9 。因此 index1 = 1, index2 = 2 。

解法

  • 双指针法(对撞指针)
class Solution {
public:
    vector<int> twoSum(vector<int>& numbers, int target) {
        int i = 0, j = numbers.size()-1;   //对撞指针法
        //利用数组有序的性质,通过头和尾两个指针逐渐向中间靠拢(头尾两数之和小于目标时增加头指针,反之减少尾指针)来筛除不符合条件的组合,从而得到符合条件的组合
        while(i<j){
            if((numbers[i] + numbers[j]) == target){
                return {i+1, j+1};
            }
            if((numbers[i] + numbers[j]) < target){
                ++i;
            }
            
            if((numbers[i] + numbers[j]) > target){
                --j;
            }
        }
        return {0, 0};
    }
};

复杂度分析

时间复杂度:O(n),只需扫描一遍数组。

空间复杂度:O(1),无需额外空间。


13. 反转字符串

编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 char[] 的形式给出。

不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。

你可以假设数组中的所有字符都是 ASCII 码表中的可打印字符。

示例 1:
输入:["h","e","l","l","o"]
输出:["o","l","l","e","h"]

解法

  • 双指针法
class Solution {
public:
    void reverseString(vector<char>& s) {
        for(int i = 0; i < s.size()/2; ++i){
            swap(s[i],s[s.size()-1-i]);
        }
    }
};

复杂度分析

空间复杂度:O(1),无需额外空间。
时间复杂度:O(n),只需扫描一遍数组。


14. 反转字符串中的元音字母

编写一个函数,以字符串作为输入,反转该字符串中的元音字母。

示例 1:
输入: "hello"
输出: "holle"

解法

  • 双指针法

跳过字符串中非元音字符

class Solution {
public:
    string reverseVowels(string s) {
        int front_ptr = 0;
        int end_ptr = s.size()-1;
        while(front_ptr < end_ptr){
            while(!is_vowel(s[front_ptr]) && (front_ptr < end_ptr)) ++front_ptr;
            while(!is_vowel(s[end_ptr]) && (front_ptr < end_ptr)) --end_ptr;
            swap(s[front_ptr],s[end_ptr]);
            ++front_ptr;
            --end_ptr;
        }
        return s;    
    }
    bool is_vowel(char ch){
        if((ch == 'a') || (ch == 'e') 
        || (ch == 'i') || (ch == 'o') 
        || (ch == 'u') || (ch == 'A') || (ch == 'E') 
        || (ch == 'I') || (ch == 'O') 
        || (ch == 'U')) {return true;}
        else return false;
    }
    };


复杂度分析

空间复杂度:O(1),无需额外空间。
时间复杂度:O(n),只需扫描一遍字符串。


15. 盛最多水的容器

给定 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。
图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。

示例 1:
输入: [1,8,6,2,5,4,8,3,7] 
输出: 49

解法

  • 双指针法(对撞指针)

利用容器容量由短板决定,每次将短板向中间移动

class Solution {
public:
    int maxArea(vector<int>& height) {
        int front_ptr = 0, end_ptr = height.size() - 1;
        int max_water = 0;
        if(height[front_ptr] > height[end_ptr]){
            max_water = height[end_ptr] * (end_ptr - front_ptr);
        }
        else max_water = height[front_ptr] * (end_ptr - front_ptr); 

        while(front_ptr < end_ptr){//对撞指针法,利用容器容量由短板决定,每次将短板向中间移动
            if(height[front_ptr] > height[end_ptr]){
                while(height[front_ptr] > height[end_ptr] && (front_ptr < end_ptr)){
                    if(height[end_ptr] * (end_ptr-front_ptr) > max_water){
                        max_water = height[end_ptr] * (end_ptr-front_ptr);
                    }
                    --end_ptr;
                }
            }
            else{
                while(height[front_ptr] <= height[end_ptr] && (front_ptr < end_ptr)){
                    if(height[front_ptr] * (end_ptr - front_ptr) > max_water){
                        max_water = height[front_ptr] * (end_ptr - front_ptr);
                    }
                    ++front_ptr;
                }
            }      
       }
    return max_water;
    }
};

复杂度分析

空间复杂度:O(1),无需额外空间。

时间复杂度:O(n),只需扫描一遍字符串。


16. 长度最小的子数组

给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的连续子数组。如果不存在符合条件的连续子数组,返回 0。

示例 1:
输入: s = 7, nums = [2,3,1,2,4,3] 
输出: 2

解释: 子数组 [4,3] 是该条件下的长度最小的连续子数组。

解法

  • 双指针法(滑动窗口)
    利用双索引法中的滑动窗口法,窗口数组和大于s时向右滑动左窗口(减小窗口),窗口数组和小于s时向右滑动右窗口(增大窗口)
class Solution {
public:
    int minSubArrayLen(int s, vector<int>& nums) {
        if(nums.size() == 0) return 0;
        int first_ptr = 0, second_ptr = 0;//利用双索引法中的滑动窗口法,窗口数组和大于s时向右滑动左窗口(减小窗口),窗口数组和小于s时向右滑动右窗口(增大窗口)
        int sum_int=nums[first_ptr];
        int min_len = 0; 

        while(second_ptr < nums.size() || (sum_int >= s)){
            if(sum_int < s && (second_ptr < nums.size())){
                if(second_ptr < nums.size() - 1){
                    ++second_ptr;
                    sum_int += nums[second_ptr];
                }
                else ++second_ptr;          
            }
            if(sum_int >= s && (first_ptr < nums.size())){
                if((min_len == 0) || ((second_ptr-first_ptr+1) < min_len)){
                    min_len = second_ptr - first_ptr +1;
                }
                sum_int -= nums[first_ptr];
                ++first_ptr;               
            }
        }
        return min_len;      
    }
};

复杂度分析

空间复杂度:O(1),无需额外空间。

时间复杂度:O(n),只需扫描一遍字符串。


17. 无重复字符的最长子串

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

示例 1:
输入: "abcabcbb" 
输出: 3 

解释: 因为无重复字符的最长子串是
“abc”,所以其长度为 3

解法

  • 双指针法(滑动窗口)

利用双索引法中的滑动窗口法,窗口内出现重复字符时向右滑动左窗口(减小窗口),窗口中无重复字符时向右滑动右窗口(增大窗口)

当我们知道该字符集比较小的时侯,我们可以用一个整数数组作为直接访问表来替换 Map。

常用的表如下所示:
int [26] 用于字母 ‘a’ -‘z’ 或 ‘A’ - ‘Z’
int [128] 用于ASCII码
int [256] 用于扩展ASCII码

class Solution {
 public:
  int lengthOfLongestSubstring(string s) {
    int char_freq[256] = {0};//利用256长度的数组储存字串内出现过的字符信息
    int first = 0, second = 0;//滑动窗口法s[first,second]内为最长子串
    char_freq[s[second]] = 1;
    int sub_length = 1;
    char new_char;
 
    if(s.size() == 0) return 0;
    while (first < s.size()) {
      while (second < (s.size() - 1)) {
        new_char = s[++second];
        if (char_freq[new_char] == 1)
          break;
        else {
          char_freq[new_char] = 1;
          sub_length = max(sub_length, second - first + 1);
        }
      }
      while (s[first] != new_char) {
        char_freq[s[first++]] = 0;
      }
      ++first;
    }
    return sub_length;
  }
};

复杂度分析

空间复杂度:O(1),无需额外空间。

时间复杂度:O(n),只需扫描一遍字符串。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值