这周继续刷数组类的算法题~
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:
输入: [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),只需扫描一遍字符串。