leetcode

一、无重复字符的最长子串(第三题)

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;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值