力扣刷题学习笔记(1)

最近开始刷题学习,下面整理一些题目,源自力扣,并附上了完整的程序,直接使用就可以验证,用于今后学习随时随地参考

剑指 Offer 03. 数组中重复的数字

难度简单92收藏分享切换为英文关注反馈

找出数组中重复的数字。

 

在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。

示例 1:

输入:
[2, 3, 1, 0, 2, 5, 3]
输出:2 或 3

 

限制:

2 <= n <= 100000

 

 

 

C++题解

本题的条件限制没有很多,没有之名是否可以使用额外空间,是否可以修改原始数组,因此需要根据不同的需求进行不同的考虑,这也符合我们在实际运用中根据不同需求采取不同策略。

 

1.哈希set

当我们允许使用额外空间时,set是一个不错的选择。

我们可以遍历数组来将nums中的元素放入set;

如果nums[i] 已经存在于集合,则返回该值;

如果不存在,将该值添加到集合中。

 

C++

 

class Solution {

public:

    int findRepeatNumber(vector<int>& nums) {

        unordered_set <int> hash_set;

        for (int i:nums)

        {

            if (hash_set.count(i) == 1)

                return i;

            else

                hash_set.insert(i);

        }

        return 0;

    }

};

复杂度分析

时间复杂度:O(N)。在最坏条件下,我们会遍历整个数组。

空间复杂度:O(N)。使用了哈希set作为辅助空间。

2.排序

排序是比较容易想到的方式。

排序后,相同的数会挨在一起,会改变原始数组,但不需要额外的储存空间。

 

C++

 

class Solution {

public:

    int findRepeatNumber(vector<int>& nums) {

        sort(nums.begin(), nums.end());//#include<algorithm> 必须有这个头文件调用,没有第三个参数默认从小到大排列,

//4.begin 得到数组头的指针

//5.end 得到数组的最后一个单元+1的指针

        for (int i = 0; i < nums.size() - 1; ++i)

        {

            if (nums[i] == nums[i+1])

                return nums[i];

        }

        return 0;

    }

};

复杂度分析

时间复杂度:O(NlogN),sort排序。

空间复杂度:O(1)。

3.原地交换

本题条件,长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内,

因此我们可以将数组中的元素nums[i]与下标i进行匹配,

当出现相同数字,但下标不同的时候,说明出现的了重复

这种方法也对数组进行了修改

 

C++

 

class Solution {

public:

    int findRepeatNumber(vector<int>& nums) {

        //遍历数组,将i与nums[i]对应起来

        for(int i = 0; i < nums.size(); ++i) {

            //如果当前值的下标不等于当前值,如 1 != nums[1]:2

            while(i != nums[i]) {

                //如果nums[1]:2 == nums[2],找到相同数字,但下标不同,则说明出现重复数字

                if(nums[i] == nums[nums[i]])

                    //返回该数字

                    return nums[i];

                //若不相等

                else

                    //则交换nums[i]和nums[nums[i]],一直循环交换

                    swap(nums[i], nums[nums[i]]);

            }

        }

        return 0;

    }

};

复杂度分析

时间复杂度:O(N),只对数组进行了一次变量。

空间复杂度:O(1)。

 

 

 

 

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

 

示例 1:

 

输入: "abcabcbb"

输出: 3

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

示例 2:

 

输入: "bbbbb"

输出: 1

解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。

示例 3:

 

输入: "pwwkew"

输出: 3

解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。

     请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。

 

 

方法一:滑动窗口

思路和算法

 

我们先用一个例子来想一想如何在较优的时间复杂度内通过本题。

 

我们不妨以示例一中的字符串 \texttt{abcabcbb}abcabcbb 为例,找出 从每一个字符开始的,不包含重复字符的最长子串,那么其中最长的那个字符串即为答案。对于示例一中的字符串,我们列举出这些结果,其中括号中表示选中的字符以及最长的字符串:

 

以 \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)。

发现了什么?如果我们依次递增地枚举子串的起始位置,那么子串的结束位置也是递增的!这里的原因在于,假设我们选择字符串中的第 kk 个字符作为起始位置,并且得到了不包含重复字符的最长子串的结束位置为 r_kr

k

​        

 。那么当我们选择第 k+1k+1 个字符作为起始位置时,首先从 k+1k+1 到 r_kr

k

​        

  的字符显然是不重复的,并且由于少了原本的第 kk 个字符,我们可以尝试继续增大 r_kr

k

​        

 ,直到右侧出现了重复字符为止。

 

这样以来,我们就可以使用「滑动窗口」来解决这个问题了:

 

我们使用两个指针表示字符串中的某个子串(的左右边界)。其中左指针代表着上文中「枚举子串的起始位置」,而右指针即为上文中的 r_kr

k

​        

 ;

 

在每一步的操作中,我们会将左指针向右移动一格,表示 我们开始枚举下一个字符作为起始位置,然后我们可以不断地向右移动右指针,但需要保证这两个指针对应的子串中没有重复的字符。在移动结束后,这个子串就对应着 以左指针开始的,不包含重复字符的最长子串。我们记录下这个子串的长度;

 

在枚举结束后,我们找到的最长的子串的长度即为答案。

 

判断重复字符

 

在上面的流程中,我们还需要使用一种数据结构来判断 是否有重复的字符,常用的数据结构为哈希集合(即 C++ 中的 std::unordered_set,Java 中的 HashSet,Python 中的 set, JavaScript 中的 Set)。在左指针向右移动的时候,我们从哈希集合中移除一个字符,在右指针向右移动的时候,我们往哈希集合中添加一个字符。

 

至此,我们就完美解决了本题。

 

C++JavaPython3JavaScriptGolang

 

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;

    }

};

复杂度分析

 

时间复杂度:O(N)O(N),其中 NN 是字符串的长度。左指针和右指针分别会遍历整个字符串一次。

 

空间复杂度:O(|\Sigma|)O(∣Σ∣),其中 \SigmaΣ 表示字符集(即字符串中可以出现的字符),|\Sigma|∣Σ∣ 表示字符集的大小。在本题中没有明确说明字符集,因此可以默认为所有 ASCII 码在 [0, 128)[0,128) 内的字符,即 |\Sigma| = 128∣Σ∣=128。我们需要用到哈希集合来存储出现过的字符,而字符最多有 |\Sigma|∣Σ∣ 个,因此空间复杂度为 O(|\Sigma|)O(∣Σ∣)。

#if 1

#include <iostream>

#include <vector>

#include <algorithm>

#include <unordered_set>

#include <string>

using namespace std;

 

#define MAX 10

#define AMIN 1

#ifdef MAX

class Solution {

public:

    int findRepeatNumber(vector<int>& nums) {

        #if 0

        sort(nums.begin(), nums.end());

        cout<<__LINE__<<"  WWWWWWWWWWWWWWWWWWWWWW   "<<endl;

       // for (int i = 0; i < nums.size(); i++)

        //{

            //cout <<"nums["<<i<<"] : "<< nums[i]<<endl;

       // }

 

        for (int i = 0; i < nums.size() - 1; ++i)

        {

            //if (nums[i] == nums[i+1])

           // {

                if(i > 0)

                {

                    if((nums[i-1] == nums[i])||(nums[i] == nums[i+1]))

                    {

                        //cout<<__LINE__<<"  WWWWWWWWWWWWWWWWWWWWWW   "<<nums[i]<<endl;

                       cout<< nums[i]<<endl;

                    }

                }

                else if((i== 0  )&& (nums[i] == nums[i+1]))

                {

                    //cout<<__LINE__<<"  HHHHHHHHHHHHHHHHHHHHHHHHHH   "<<nums[i]<<endl;

                     cout<< nums[i]<<endl;

                }

                else

                {

 

                }

 

            //}

        }

        #endif // 0

 

        unordered_set <int> hash_set;

 

         for (int i = 0; i < nums.size(); i++)

        {

            cout <<"nums["<<i<<"] : "<< nums[i]<<endl;

       }

        for (int i:nums)

        {

            if (hash_set.count(i) == 1)

            {

 

               cout<<__LINE__<<"  WWWWWWWWWWWWWWWWWWWWWW   "<<endl;

                return nums[i];

            }

            else

            {

 

              cout<<__LINE__<<"  HHHHHHHHHHHHHHHHH   "<<endl;

                hash_set.insert(i);

        }

        }

       // return 0;

 

 

        return 0;

    }

 

        int lengthOfLongestSubstring(string s) {

        // 哈希集合,记录每个字符是否出现过

        unordered_set<char> occ;

        int n = s.size();

 

        cout<<n<<endl;

        // 右指针,初始值为 -1,相当于我们在字符串的左边界的左侧,还没有开始移动

        int rk = -1, ans = 0;

        // 枚举左指针的位置,初始值隐性地表示为 -1

        for (int i = 0; i < n; ++i) {

            if (i != 0) {

                // 左指针向右移动一格,移除一个字符

                occ.erase(s[i - 1]);

                cout<<s[i-1]<<endl;

            }

            while  (rk + 1 < n && !occ.count(s[rk + 1])) {

                // 不断地移动右指针

                occ.insert(s[rk + 1]);

                cout<<rk+1<<" = rk+1 "<<"occ s[rk+1]== "<<s[rk+1]<<endl;

                ++rk;

            }

            // 第 i 到 rk 个字符是一个极长的无重复字符子串

            cout<<" ans  "<<ans<< "   rk - i+1"<<rk-i+1<<endl;

            ans = max(ans, rk - i + 1);

 

        }

        return ans;

    }

};

#else ifdef MIN

 

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;

    }

};

#endif // MAX

 

 

 

int main(void)

{

    Solution s;

    vector<int> arry;

#if 0

    int result=0;

 

    for (int temp = 0; cin >> temp;)

    {   cout<<__LINE__<<"     "<<temp<<endl;

        arry.push_back(temp);//push_back 在数组的最后添加一个数据

        if (cin.get() == '\n')

            break;

    }

 

    cout << "输入元素个数为:" << arry.size()<<endl;

 

    for (int i = 0; i < arry.size(); i++)

    {

 

        cout <<"arry["<<i<<"] : "<< arry[i]<<endl;

    }

 

   // for (int i = 0; i < arry.size(); i++)

    //{

        result= s.findRepeatNumber(arry);

        cout<<result<<endl;

   // }

#endif // 0

    string str;

    getline(cin,str);

    cout<<str<<endl;

    int result=0;

    result=s.lengthOfLongestSubstring(str);

    cout<<"result "<<result<<endl;

    cout<<"you"<<endl;

 

 

 

    return 0;

}

#endif // 0

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值