最近开始刷题学习,下面整理一些题目,源自力扣,并附上了完整的程序,直接使用就可以验证,用于今后学习随时随地参考
剑指 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
unordered_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
sort(nums.begin(), nums.end());//#include
//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
//遍历数组,将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
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
#include
#include
#include
#include
using namespace std;
#define MAX 10
#define AMIN 1
#ifdef MAX
class Solution {
public:
int findRepeatNumber(vector
#if 0
sort(nums.begin(), nums.end());
cout<<__LINE__<<" WWWWWWWWWWWWWWWWWWWWWW "< // for (int i = 0; i < nums.size(); i++) //{ //cout <<"nums["<
// } 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 "< cout<< nums[i]< } } else if((i== 0 )&& (nums[i] == nums[i+1])) { //cout<<__LINE__<<" HHHHHHHHHHHHHHHHHHHHHHHHHH "< cout<< nums[i]< } else { } //} } #endif // 0 unordered_set for (int i = 0; i < nums.size(); i++) { cout <<"nums["<
} for (int i:nums) { if (hash_set.count(i) == 1) { cout<<__LINE__<<" WWWWWWWWWWWWWWWWWWWWWW "< return nums[i]; } else { cout<<__LINE__<<" HHHHHHHHHHHHHHHHH "< hash_set.insert(i); } } // return 0; return 0; } int lengthOfLongestSubstring(string s) { // 哈希集合,记录每个字符是否出现过 unordered_set int n = s.size(); cout< // 右指针,初始值为 -1,相当于我们在字符串的左边界的左侧,还没有开始移动 int rk = -1, ans = 0; // 枚举左指针的位置,初始值隐性地表示为 -1 for (int i = 0; i < n; ++i) { if (i != 0) { // 左指针向右移动一格,移除一个字符 occ.erase(s[i - 1]); cout< } while (rk + 1 < n && !occ.count(s[rk + 1])) { // 不断地移动右指针 occ.insert(s[rk + 1]); cout< ++rk; } // 第 i 到 rk 个字符是一个极长的无重复字符子串 cout<<" ans "< ans = max(ans, rk - i + 1); } return ans; } }; #else ifdef MIN class Solution { public: int lengthOfLongestSubstring(string s) { // 哈希集合,记录每个字符是否出现过 unordered_set 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 #if 0 int result=0; for (int temp = 0; cin >> temp;) { cout<<__LINE__<<" "< arry.push_back(temp);//push_back 在数组的最后添加一个数据 if (cin.get() == '\n') break; } cout << "输入元素个数为:" << arry.size()< for (int i = 0; i < arry.size(); i++) { cout <<"arry["<
} // for (int i = 0; i < arry.size(); i++) //{ result= s.findRepeatNumber(arry); cout< // } #endif // 0 string str; getline(cin,str); cout< int result=0; result=s.lengthOfLongestSubstring(str); cout<<"result "< cout<<"you"< return 0; } #endif // 0