目录
3. 无重复字符的最长子串
题目描述
给定一个字符串 s
,请你找出其中不含有重复字符的 最长子串的长度。
示例 1:
输入: s = "abcabcbb" 输出: 3 解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
示例 2:
输入: s = "bbbbb" 输出: 1 解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
示例 3:
输入: s = "pwwkew" 输出: 3 解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。
提示:
0 <= s.length <= 5 * 104
s
由英文字母、数字、符号和空格组成
解题思路
使用滑动窗口的算法,并结合哈希表来快速判断某个字符是否已被访问过。
1.初始化:
获取字符串s的长度len。如果字符串为空(len == 0),则最长无重复字符子串的长度显然为0,直接返回0。
定义左指针left和右指针right,都初始化为0。这两个指针用于定义当前考虑的子串范围。定义maxLen用于记录当前找到的最长无重复字符子串的长度,初始化为0。
定义哈希表hash,大小为256,并全部初始化为0。哈希表的索引对应字符的ASCII值,值对应字符的访问状态(0表示未访问,1表示已访问)。
2.遍历字符串
使用while循环遍历字符串,直到右指针right达到字符串的末尾。
3.检查字符状态
在每次循环中,检查当前右指针right指向的字符s[right]的访问状态:
如果hash[s[right]]为0,表示该字符在当前考虑的子串中还未出现过:将hash[s[right]]设置为1,标记该字符为已访问。更新maxLen为当前子串长度(right - left + 1)和maxLen中的较大值。右指针right右移一位,继续考虑下一个字符。
如果hash[s[right]]为1,表示该字符在当前考虑的子串中已经出现过:为了找到最长无重复字符子串,需要移动左指针left,并重置左指针及其左侧所有字符的访问状态(在hash中对应位置设置为0)。左指针left右移一位。
4.返回结果
当遍历完整个字符串后,maxLen中保存的就是最长无重复字符子串的长度,将其作为结果返回。
代码实现
int lengthOfLongestSubstring(char* s) {
int len = strlen(s);
if (len == 0)
return 0;
int left = 0, right = 0;
int maxLen = 0;
int hash[256] = {0};
while (right < len) {
// 如果当前字符没有被访问过
if (hash[s[right]] == 0) {
hash[s[right]] = 1; // 标记当前字符为已访问
maxLen = (right - left + 1) > maxLen ? (right - left + 1): maxLen; // 更新最大长度
right++; // 右指针右移
} else {
// 如果当前字符已经被访问过,则移动左指针,并重置左指针及其左侧所有字符的访问状态
hash[s[left]] = 0;
left++;
}
}
return maxLen;
}
11. 盛最多水的容器
题目描述
给定一个长度为 n
的整数数组 height
。有 n
条垂线,第 i
条线的两个端点是 (i, 0)
和 (i, height[i])
。
找出其中的两条线,使得它们与 x
轴共同构成的容器可以容纳最多的水。
返回容器可以储存的最大水量。
说明:你不能倾斜容器。
示例 1:
输入:[1,8,6,2,5,4,8,3,7] 输出:49 解释:图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。
示例 2:
输入:height = [1,1] 输出:1
提示:
n == height.length
2 <= n <= 105
0 <= height[i] <= 104
解题思路
这个问题是关于找出两条垂线,使得它们与x轴共同构成的容器可以容纳最多的水。一个直观的想法是遍历所有可能的线对,并计算它们之间形成的容器的面积,然后找出面积最大的那个。但是,这种方法的时间复杂度是O(n^2),对于较大的n,效率较低。
为了优化这个问题,可以使用双指针法。其基本思想是,初始化两个指针分别指向数组的开始和结束,然后逐步向中间移动,同时更新最大面积。
具体步骤如下:
1.初始化两个指针,left指向数组的第一个元素,right指向数组的最后一个元素。同时初始化最大面积max为0。
2.进入循环,当left < right时执行以下操作:
计算当前left和right所指向的两条线构成的容器的面积s,面积的计算公式为s = min(height[left], height[right]) * (right - left)。如果s大于max,则更新max为s。
3.移动指针:比较height[left]和height[right],如果height[left]较小,则left右移一位;如果height[right]较小,则right左移一位。
4.返回结果:循环结束后,max中保存的就是最大的容器面积,返回max即可。
为什么这种方法是有效的呢?因为容器的面积取决于两个因素:两条线的高度和它们之间的距离。在移动指针的过程中,始终保持着一条高度较高的线不动,而移动高度较低的线。这样做的目的是尽可能地保留高度较高的线,因为移动高度较高的线可能导致面积变小。同时,通过缩小搜索范围,减少了计算量,从而提高了效率。
这种双指针法的时间复杂度是O(n),因为每个指针最多移动n次。因此,对于较大的n,这种方法比遍历所有线对的方法更加高效。
代码实现
int maxArea(int* height, int heightSize) {
int s = 0, max = 0, left = 0, right = heightSize - 1;
while (left < right) {
if (height[left] > height[right]) {
s = height[right] * (right - left);
if (s > max)
max = s;
right--;
} else {
s = height[left] * (right - left);
if (s > max)
max = s;
left++;
}
}
return max;
}