一、无重复字符的最长子串(第三题)
1、问题描述:给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。
输入: s = "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
2、解决思路:滑动窗口思路,判断是否有重复的字符:哈希集合( std::unordered_set)
(1)i固定左侧,右侧窗口为j=i+1,
(2)j++向右移动,并一直判断窗口内部没有重复元素(确保这个子串是无重复元素的)
(3)当有重复的时候,返回这个长度和最大长度比较
(4)然后左侧i向右移动进行下一个字串的判断:继续进行(1)(2)(3)(4)---->(1)(2)(3)(4)
以 \texttt{(a)bcabcbb}(a)bcabcbb 开始的最长字符串为 \texttt{(abc)abcbb}(abc)abcbb;
以 \texttt{a(b)cabcbb}a(b)cabcbb 开始的最长字符串为 \texttt{a(bca)bcbb}a(bca)bcbb;
以 \texttt{ab(c)abcbb}ab(c)abcbb 开始的最长字符串为 \texttt{ab(cab)cbb}ab(cab)cbb;
以 \texttt{abc(a)bcbb}abc(a)bcbb 开始的最长字符串为 \texttt{abc(abc)bb}abc(abc)bb;
以 \texttt{abca(b)cbb}abca(b)cbb 开始的最长字符串为 \texttt{abca(bc)bb}abca(bc)bb;
以 \texttt{abcab(c)bb}abcab(c)bb 开始的最长字符串为 \texttt{abcab(cb)b}abcab(cb)b;
以 \texttt{abcabc(b)b}abcabc(b)b 开始的最长字符串为 \texttt{abcabc(b)b}abcabc(b)b;
以 \texttt{abcabcb(b)}abcabcb(b) 开始的最长字符串为 \texttt{abcabcb(b)}abcabcb(b)。
3、官方代码
class Solution {
public:
int lengthOfLongestSubstring(string s) {
// 哈希集合,记录每个字符是否出现过
unordered_set<char> occ;
int n = s.size();
// 右指针,初始值为 -1,相当于我们在字符串的左边界的左侧,还没有开始移动
int rk = -1, ans = 0;
// 枚举左指针的位置,初始值隐性地表示为 -1
for (int i = 0; i < n; ++i) {
if (i != 0) {
// 左指针向右移动一格,移除一个字符
occ.erase(s[i - 1]);
}
while (rk + 1 < n && !occ.count(s[rk + 1])) {
// 不断地移动右指针
occ.insert(s[rk + 1]);
++rk;
}
// 第 i 到 rk 个字符是一个极长的无重复字符子串
ans = max(ans, rk - i + 1);
}
return ans;
}
};
4、哈希集 基本的函数
- unordered_set构造
std::unordered_setstd::string c:初始化容器;
std::unordered_setstd::string c{ “aaa”, “bbb”, “ccc” }:初始化容器,并将"aaa", “bbb”, "ccc"加入到容器中;
std::unordered_setstd::string c{ 16 }:初始化容器,并设置16个桶;
- 添加新的元素
c.insert(“dddd”):向容器添加元素”dddd";
a.insert({ “aaa”,“bbbb”,“cccc” }):向容器添加元素"aaa",“bbbb”,“cccc”;
a.insert(b.begin(), b.end()):b是一个存储着和a相同类型元素的向量,可将b中所有元素添加到a中。
- 查找元素
a.find(“eeee”):查找元素"eeee",返回结果为a.end()则表明没有找到,否则返回所对应元素;
a.count(“eeee”):查找元素"eeee"在a中有几个(由于unordered_set中没有相同的元素,所以结果通常为0或1)。
- 查找桶接口
a.bucket_count():返回数据结构中桶的数量;
a.bucket_size(i):返回桶i中的大小;
a.bucket(“eeee"):返回元素"eeee"在哪个桶里。
- 观察器
a.hash_function()(“aaa”):返回"aaa"所对应的hash值;
a.key_eq()(“aaa”,“aaaa”) :当元素相同时返回true,否则返回false。
- 清除元素
a.clear():清除a中所有元素;
a.erase(“aaa”):清除元素"aaa"。
- 统计函数
a.size():返回a中总的元素个数;
a.max_size():返回a中最大容纳元素;
a.empty():判断a中是否为空。
5、自己的代码
我的思路就是通过两层循环,第一层确定子串的起始位置,第二层从子串起始位置向后扫描,找到最大无重复子串(使用哈希集)
class Solution {
public:
int lengthOfLongestSubstring(string s) {
unordered_set<char> occ;
int maxlen=0;
for(int left=0;left<s.length();left++)
{
occ.insert(s[left]);
for(int right=left+1;right<s.length();right++)
{
if(!occ.count(s[right]))
{
occ.insert(s[right]);
}
else{
break;
}
}
if(occ.size()>maxlen)maxlen=occ.size();
occ.clear();
}
return maxlen;
};
};
二、寻找两个正序数组的中位数(第四题)
1、问题描述:给定两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的 中位数 。算法的时间复杂度应该为 O(log (m+n)) 。
输入:nums1 = [1,3], nums2 = [2]
输出:2.00000
解释:合并数组 = [1,2,3] ,中位数 2
2、解决思路
- 给定两个有序数组,要求找到两个有序数组的中位数,最直观的思路有以下两种:
- 使用归并的方式,合并两个有序数组,得到一个大的有序数组。大的有序数组的中间位置的元素,即为中位数。
- 不需要合并两个有序数组,只要找到中位数的位置即可。由于两个数组的长度已知,因此中位数对应的两个数组的下标之和也是已知的。维护两个指针,初始时分别指向两个数组的下标00 的位置,每次将指向较小值的指针后移一位(如果一个指针已经到达数组末尾,则只需要移动另一个数组的指针),直到到达中位数的位置。
- 假设两个有序数组的长度分别为 mm 和 nn,上述两种思路的复杂度如何?
- 第一种思路的时间复杂度是 O(m+n),空间复杂度是 O(m+n)。第二种思路虽然可以将空间复杂度降到
O(1)但是时间复杂度仍是 O(m+n)。 - 如何把时间复杂度降低到 O(log(m+n)) 呢?如果对时间复杂度的要求有log,通常都需要用到二分查找,这道题也可以通过二分查找实现
class Solution {
public:
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
float med=0;
int len1=nums1.size();
int len2=nums2.size();
int len3=len1+len2;
int f1=0,f2=0,f3=0;
vector<int> nums3(len3,0);
while(f1<len1&&f2<len2)
{
if(nums1[f1]<nums2[f2])
{
nums3[f3++]=nums1[f1++];
}
else
{
nums3[f3++]=nums2[f2++];
}
}
while(f1<len1)
{
nums3[f3++]=nums1[f1++];
}
while(f2<len2)
{
nums3[f3++]=nums2[f2++];
}
if(len3%2==0)
{
int a=len3/2;
med=(nums3[a-1]+nums3[a])/2.0;
}
else{
int a=len3/2;
med=nums3[a];
}
return med;
}
};
三、7. 整数反转
给你一个 32 位的有符号整数 x ,返回将 x 中的数字部分反转后的结果。
如果反转后整数超过 32 位的有符号整数的范围 [−231, 231 − 1] ,就返回 0。
假设环境不允许存储 64 位整数(有符号或无符号)。
输入:x = 123
输出:321
这里面的关键就是if(rev>INT_MAX/10||rev<INT_MIN/10)
以及翻转的常见思路
int dight=x%10;
x=x/10;
rev=rev*10+dight;
class Solution {
public:
int reverse(int x) {
int rev=0;
while(x!=0){
if(rev>INT_MAX/10||rev<INT_MIN/10){
return 0;
}
int dight=x%10;
x=x/10;
rev=rev*10+dight;
}
return rev;
}
};
四、8. 字符串转换整数 (atoi)
1、题目
请你来实现一个 myAtoi(string s) 函数,使其能将字符串转换成一个 32 位有符号整数(类似 C/C++ 中的 atoi 函数)。
函数 myAtoi(string s) 的算法如下:
读入字符串并丢弃无用的前导空格
检查下一个字符(假设还未到字符末尾)为正还是负号,读取该字符(如果有)。 确定最终结果是负数还是正数。 如果两者都不存在,则假定结果为正。
读入下一个字符,直到到达下一个非数字字符或到达输入的结尾。字符串的其余部分将被忽略。
将前面步骤读入的这些数字转换为整数(即,“123” -> 123, “0032” -> 32)。如果没有读入数字,则整数为 0 。必要时更改符号(从步骤 2 开始)。
如果整数数超过 32 位有符号整数范围 [−231, 231 − 1] ,需要截断这个整数,使其保持在这个范围内。具体来说,小于 −231 的整数应该被固定为 −231 ,大于 231 − 1 的整数应该被固定为 231 − 1 。
返回整数作为最终结果。
注意:
本题中的空白字符只包括空格字符 ’ ’ 。
除前导空格或数字后的其余字符串外,请勿忽略 任何其他字符。
2、实现
关键:
if(num>INT_MAX/10||num==INT_MAX/10&&n*sign>INT_MAX%10)边界判断
if(s[i]<=‘9’&&s[i]>=‘0’)判断是否为数字
int n=s[i]-‘0’;将数组中数字提取出来
class Solution {
public:
int myAtoi(string s) {
int i=0;
int len=s.size();
while(i<len&&s[i]==' '){
i++;
}
int start=i;
int sign=1;
int num=0;
for(;i<len;i++)
{
if(i==start&&s[i]=='+')
{
sign=1;
}else if(i==start&&s[i]=='-'){
sign=-1;
}
else if(s[i]<='9'&&s[i]>='0'){
// res > Integer.MAX_VALUE / 10 || (res == Integer.MAX_VALUE / 10 && num > Integer.MAX_VALUE % 10)
int n=s[i]-'0';
if(num>INT_MAX/10||num==INT_MAX/10&&n*sign>INT_MAX%10)return INT_MAX;
if(num<INT_MIN/10||num==INT_MIN/10&&n*sign<INT_MIN%10)return INT_MIN;
// int n=s[i]-'0';
num=num*10+n*sign;
}
else{
break;
}
}
return num;
}
};
public int myAtoi(String s) {
int i = 0;
int len = s.length();
int sign = 1;
int res = 0;
while (i < len && s.charAt(i) == ' ') { //如果字符串前导位置为空格,循环到有数据的那一个位置
i++;
}
int start = i; //记录一下当前之后所有数据开始的位置
for (; i < len; i++) {
int c = s.charAt(i);
if (i == start && c == '+') { //判断是否是+,并且+要在初始位置
sign = 1;
} else if (i == start && c == '-') { //判断是-
sign = -1;
} else if (Character.isDigit(c)) { //判断是数字
int num = c - '0';
//如果是数字,其他不用考虑,只需要考虑两种超限的情况,这里不细说,具体去"https://leetcode-cn.com/problems/reverse-integer/"看
if (res > Integer.MAX_VALUE / 10 || (res == Integer.MAX_VALUE / 10 && num > Integer.MAX_VALUE % 10)) {
return Integer.MAX_VALUE;
} else if (res < Integer.MIN_VALUE / 10 || (res ==Integer.MIN_VALUE / 10 && -num < Integer.MIN_VALUE % 10)) {
return Integer.MIN_VALUE;
}
res = res * 10 + sign * num;
} else { //如果有一次循环既不是数字,又不是'+'和'-',那么立即退出循环,并返回当前res中已经储存的值
break;
}
}
return res;
}