题目链接:
第一题:35. 搜索插入位置
第二题:202. 快乐数
第三题:205. 同构字符串
第四题:242. 有效的字母异位词
第五题:290. 单词规律
第六题:349. 两个数组的交集
第七题:350. 两个数组的交集 II
第八题:410. 分割数组的最大值
第九题:451. 根据字符出现频率排序
第十题:540. 有序数组中的单一元素
目录
ps:Python真的太简短了,有的库函数用起来真的很方便!
35. 搜索插入位置
题目描述
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
你可以假设数组中无重复元素。
示例 1:
输入: [1,3,5,6], 5
输出: 2
示例 2:
输入: [1,3,5,6], 2
输出: 1
示例 3:
输入: [1,3,5,6], 7
输出: 4
示例 4:
输入: [1,3,5,6], 0
输出: 0
思路分析
分析:在有序数组中查找插入元素的位置,显然可以使用二分查找。这篇题解提供的思路是「排除法」,思路是:在循环的过程中,不断排除不需要的解,最后剩下的那个元素的位置就一定是插入元素的位置。
具体来说:
首先,插入位置有可能在数组的末尾(题目中的示例 3),需要单独判断,此时返回数组的长度;
否则,根据示例和暴力解法的分析,插入的位置是大于等于 target 的第 1 个元素的位置。
因此,严格小于 target 的元素一定不是解,在循环体中将左右边界 left 和 right 逐渐向中间靠拢,最后 left 和 right 相遇,则找到了插入元素的位置。根据这个思路,可以写出如下代码。
python版本是利用O(N)的循环算法来进行的,从第0个开始循环,直到找到为止。
程序代码
c++版
class Solution {
public:
int searchInsert(vector<int>& nums, int target) {
int n = nums.size();
int left = 0, right = n - 1, ans = n;
while (left <= right) {
int mid = ((right - left) >> 1) + left;
//确定中间位置的指针
if (target <= nums[mid]) {
//如果要找的目标数小于当前数组中的数值,右端点往中间靠
ans = mid;
right = mid - 1;
} else {
//反之,左端点往中间靠
left = mid + 1;
}
}
return ans;
}
};
python版
class Solution:
def searchInsert(self, nums: List[int], target: int) -> int:
if target < nums[0]or (len(nums) == 1 and target == nums[0]): return 0
if target > nums[-1]: return len(nums)
for i in range(len(nums) - 1):
if nums[i] == target:
return i
elif nums[i] < target <= nums[i + 1]:
return i + 1
202. 快乐数
题目描述
编写一个算法来判断一个数 n 是不是快乐数。
「快乐数」定义为:对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和,然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。如果 可以变为 1,那么这个数就是快乐数。
如果 n 是快乐数就返回 True ;不是,则返回 False 。
示例:
输入:19
输出:true
解释:
12 + 92 = 82
82 + 22 = 68
62 + 82 = 100
12 + 02 + 02 = 1
思路分析
题意很容易看懂,按照他这样操作的去弄就好了。但是这种题做多了我们会发现,其实他说无限操作下去是不可能的,肯定在某一个地方又会回到原点,或者之前出现过的数字。
方法一:
所以我们可以用map、set之类的记录之前出现过的数字,如果在之后的某次操作中又出现了这个数字,就说明会一直循环下去,得不到1,这时候退出就好了。
时间复杂度,O(玄学)
方法二:
一般这种能够形成循环的我们可以考虑快慢指针法,一个指针跑在前面,一个指针跑在后面。如果有循环的话,那么快指针肯定会和慢指针相遇。
程序代码
c++哈希版
class Solution {
int getsum(int x) {
int ans=0;
while(x) {
ans+=(x%10)*(x%10);
x=x/10;
}
return ans;
}
public:
bool isHappy(int n) {
set<int> set1;
while(1) {
int sum=getsum(n);
if(sum==1)
return true;
else {
if(set1.find(sum)!=set1.end()) return false;
else {
set1.insert(sum);
}
n=sum;
}
}
}
};
python 哈希版
class Solution:
def isHappy(self, n: int) -> bool:
num=0
l1=[n]
#l1算是建立的一个列表,将出现过的数字放入列表中
n2=n
while n2!=1:
n=n2
n2=0
a=len(str(n))
for i in range(a):
n2 += int(str(n)[i:i+1])**2
#进行题目中所要求的数字变换
if n2 in l1:
return False
#如果已经出现过,就返回False
l1.append(n2)
return True
#最终退出循环之后再返回True,说明经过变换之后得到了1
205. 同构字符串
题目描述
给定两个字符串 s 和 t,判断它们是否是同构的。
如果 s 中的字符可以被替换得到 t ,那么这两个字符串是同构的。
所有出现的字符都必须用另一个字符替换,同时保留字符的顺序。两个字符不能映射到同一个字符上,但字符可以映射自己本身。
示例 1:
输入: s = “egg”, t = “add”
输出: true
示例 2:
输入: s = “foo”, t = “bar”
输出: false
示例 3:
输入: s = “paper”, t = “title”
输出: true
说明:
你可以假设 s 和 t 具有相同的长度。
思路分析
利用数组的映射关系,当第一次在s字符串出现的字符,我们在ss中标记一下,并且储存相应的t[i]值,当再次出现的时候,再看是不是符合的,不符合就返回false,直到结束还没有返回false的话就再返回true。
程序代码
c++版
class Solution {
public:
bool isIsomorphic(string s, string t) {
int n,m;
n=s.size();m=t.size();
int ss[150],tt[150];
memset(ss,0,sizeof(ss));
memset(tt,0,sizeof(tt));
for(int i=0;i<n;++i) {
if(ss[s[i]]==0) ss[s[i]]=t[i];
//如果是第一次出现,就标记一下
else if(ss[s[i]]!=t[i]) return false;
//如果不是第一次出现,那就看是否ss[s[i]]==t[i],不等于就返回false,说明不是一一对应
if(tt[t[i]]==0) tt[t[i]]=s[i];
else if(tt[t[i]]!=s[i]) return false;
//同理
}
return true;
//都没有返回false,就返回true,说明符合题目要求
}
};
Python版
class Solution:
def isIsomorphic(self, s: str, t: str) -> bool:
for i in range(len(s)):
if s.index(s[i])!=t.index(t[i]):
#直接用Python里面列表的索引来查询,贼方便
return False
return True
242. 有效的字母异位词
题目描述
给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。
示例 1:
输入: s = “anagram”, t = “nagaram”
输出: true
示例 2:
输入: s = “rat”, t = “car”
输出: false
说明:
你可以假设字符串只包含小写字母。
进阶:
如果输入字符串包含 unicode 字符怎么办?你能否调整你的解法来应对这种情况?
思路分析
方法一:先用一边循环将s字符数组里每个字符出现一次就加一,看单个字符共出现了几次,然后再循环一遍t字符串,把相应出现的字符次数减一,如果出现了不等于0或小于0的情况,就可以返回false。如果都是等于0,最终就返回true。
方法二:排序法
利用Python自带的排序,直接用就是很方便,几行代码搞定。
排序之后如果两个字符串相等,就说明是异位字符串,不相等就说明不是的。
程序代码
c++版
class Solution {
public:
bool isAnagram(string s, string t) {
int n=s.size(),m=t.size();
int ss[150];
memset(ss,0,sizeof(ss));
if(n!=m) return false;
for(int i=0;i<n;++i)
ss[s[i]]++;
for(int i=0;i<m;++i) {
ss[t[i]]--;
if(ss[t[i]]<0) return false;
}
return true;
}
};
Python计数版
class Solution:
def isAnagram(self, s: str, t: str) -> bool:
if len(s)!=len(t): return False
set1 = set(s)
for i in set1:
if s.count(i)!= t.count(i):
return False
return True
python版排序法
class Solution:
def isAnagram(self, s: str, t: str) -> bool:
if(sorted(s)==sorted(t)):
#如果排序后相等就返回true,不等就返回false
return True
else: return False
290. 单词规律
题目描述
给定一种规律 pattern 和一个字符串 str ,判断 str 是否遵循相同的规律。
这里的 遵循 指完全匹配,例如, pattern 里的每个字母和字符串 str 中的每个非空单词之间存在着双向连接的对应规律。
示例1:
输入: pattern = “abba”, str = “dog cat cat dog”
输出: true
示例 2:
输入:pattern = “abba”, str = “dog cat cat fish”
输出: false
示例 3:
输入: pattern = “aaaa”, str = “dog cat cat dog”
输出: false
示例 4:
输入: pattern = “abba”, str = “dog dog dog dog”
输出: false
说明:
你可以假设 pattern 只包含小写字母, str 包含了由单个空格分隔的小写字母。
思路分析
这道题难点就在于把str中的字符串分开,幸好Python中有个split的函数,一下就可以分好了。
分好后,我们利用Python里面的dict(字典)结构,利用键值对,将一开始出现在pattern中的字母和str中的单词对应起来,等再次出现的时候直接查询就好了。
程序代码
Python版
class Solution:
def wordPattern(self, pattern: str, str: str) -> bool:
t = str.split()
if len(pattern) != len(t):
return False
dct = {}
for i in range(len(pattern)):
if pattern[i] not in dct:
#如果pattern[i]不在dct中,但是t[i]在dct中就返回false
if t[i] in dct.values():
return False
dct[pattern[i]] = t[i]
#将t[i]放入dct中
elif dct[pattern[i]] !=t[i]:
return False
return True
ps:关于dict结构的拓展,欢迎查看文章末尾的拓展资料。
349. 两个数组的交集
题目描述
给定两个数组,编写一个函数来计算它们的交集。
示例 1:
输入:nums1 = [1,2,2,1], nums2 = [2,2]
输出:[2]
示例 2:
输入:nums1 = [4,9,5], nums2 = [9,4,9,8,4]
输出:[9,4]
说明:
输出结果中的每个元素一定是唯一的。
我们可以不考虑输出结果的顺序。
思路分析
利用hash的特点:
我们可以对nums2数组中的元素,在nums1中搜索是否存在,如果存在的话我们就把当前这个元素放在tmp中,如果不存在就继续查找下一个。
但是这样是不能去重,因为可能nums2中有多个元素是重复的,所以tmp定义的类型也是unordered的类型。
程序代码
c++版
class Solution {
public:
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
int n=nums1.size(),m=nums2.size();
unordered_set<int> set(nums1.begin(), nums1.end());
unordered_set<int> tmp; //用来去重 nums2可能有重复
for(int i=0;i<nums2.size();++i) {
if(set.find(nums2.at(i))!=set.end()) tmp.insert(nums2.at(i));
}
return vector <int> (tmp.begin(),tmp.end());
}
};
Python版
class Solution:
def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
ans = []
set1=set(nums1)
set2=set(nums2)
#先对nums1和nums2分别建立一个set,用于去重
#剩下的就跟两个数组的交集||一样了
for i in set2:
if (i in set1) and (len(set1) > 0):
set1.remove(i)
ans.append(i)
return ans
350. 两个数组的交集 II
题目描述
给定两个数组,编写一个函数来计算它们的交集。
示例 1:
输入:nums1 = [1,2,2,1], nums2 = [2,2]
输出:[2,2]
示例 2:
输入:nums1 = [4,9,5], nums2 = [9,4,9,8,4]
输出:[4,9]
说明:
输出结果中每个元素出现的次数,应与元素在两个数组中出现次数的最小值一致。
我们可以不考虑输出结果的顺序。
进阶:
如果给定的数组已经排好序呢?你将如何优化你的算法?
如果 nums1 的大小比 nums2 小很多,哪种方法更优?
如果 nums2 的元素存储在磁盘上,内存是有限的,并且你不能一次加载所有的元素到内存中,你该怎么办?
思路分析
具体见程序代码中的注释
程序代码
c++版
class Solution {
public:
vector<int> intersect(vector<int>& nums1, vector<int>& nums2) {
sort(nums1.begin(), nums1.end());
sort(nums2.begin(), nums2.end());
//先排序
int length1 = nums1.size(), length2 = nums2.size();
vector<int> intersection;
int index1 = 0, index2 = 0;
//利用双指针,如果相等就加入,不相等就继续下一个
while (index1 < length1 && index2 < length2) {
if (nums1[index1] < nums2[index2]) {
index1++;
} else if (nums1[index1] > nums2[index2]) {
index2++;
} else {
intersection.push_back(nums1[index1]);
index1++;
index2++;
}
}
return intersection;
}
};
Python版(真的简洁)
class Solution:
def intersect(self, nums1: List[int], nums2: List[int]) -> List[int]:
ans = []
for i in nums2:
if (i in nums1) and (len(nums1) > 0):
nums1.remove(i)
#如果是存在于nums1中,我们就把他删掉
ans.append(i)
#并且加入到答案中
return ans
ps:关于Python中list的元素删除的拓展在文章后面的拓展里
410. 分割数组的最大值
题目描述
给定一个非负整数数组和一个整数 m,你需要将这个数组分成 m 个非空的连续子数组。设计一个算法使得这 m 个子数组各自和的最大值最小。
注意:
数组长度 n 满足以下条件:
1 ≤ n ≤ 1000
1 ≤ m ≤ min(50, n)
示例:
输入:
nums = [7,2,5,10,8]
m = 2
输出:
18
解释:
一共有四种方法将nums分割为2个子数组。
其中最好的方式是将其分为[7,2,5] 和 [10,8],
因为此时这两个子数组各自的和的最大值为18,在所有情况中最小。
思路分析
二分答案法
「使……最大值尽可能小」是二分搜索题目常见的问法。
本题中,我们注意到:当我们选定一个值 xx,我们可以线性地验证是否存在一种分割方案,满足其最大分割子数组和不超过 xx。策略如下:
贪心地模拟分割的过程,从前到后遍历数组,用 sum 表示当前分割子数组的和,cnt 表示已经分割出的子数组的数量(包括当前子数组),那么每当sum 加上当前值超过了 xx,我们就把当前取的值作为新的一段分割子数组的开头,并将 cnt 加 11。遍历结束后验证是否cnt 不超过 mm。
这样我们可以用二分查找来解决。二分的上界为数组 nums 中所有元素的和,下界为数组nums 中所有元素的最大值。通过二分查找,我们可以得到最小的最大分割子数组和,这样就可以得到最终的答案了。
程序代码
class Solution {
public:
int check(int x,vector<int>& nums,int n,int m) {
int mm=0,summ=0,i=0;bool flag=true;
while(i<n) {
summ+=nums[i];
if(summ>x) {
mm++; summ=0;
if(mm>m) { break; }
continue;
}
else if(summ==x){
mm++; summ=0;i++;
if(mm>m) { break; }
continue;
}
++i;
}
if(summ!=0) mm++;
return mm<=m;
}
int splitArray(vector<int>& nums, int m) {
int sum=0,ans=0,n=nums.size();
for(int i=0;i<n;++i) sum+=nums[i];
int l=0,r=sum;
while(l<r) {//注意边界情况的处理
int midd=(l+r)>>1;ans=midd;
if(!check(midd,nums,n,m)) {
l=midd+1;
}
else {r=midd;}
}
return l;
}
};
451. 根据字符出现频率排序
题目描述
给定一个字符串,请将字符串里的字符按照出现的频率降序排列。
示例 1:
输入:
“tree”
输出:
“eert”
解释:
'e’出现两次,'r’和’t’都只出现一次。
因此’e’必须出现在’r’和’t’之前。此外,"eetr"也是一个有效的答案。
示例 2:
输入:
“cccaaa”
输出:
“cccaaa”
解释:
'c’和’a’都出现三次。此外,"aaaccc"也是有效的答案。
注意"cacaca"是不正确的,因为相同的字母必须放在一起。
示例 3:
输入:
“Aabb”
输出:
“bbAa”
解释:
此外,"bbaA"也是一个有效的答案,但"Aabb"是不正确的。
注意’A’和’a’被认为是两种不同的字符。
思路分析
利用c++中的关键字排序,先把各个字母出现的个数算出来,建立一个pair结构,并将字母出现的个数存储在first中,字母本身存放在second中,然后进行排序。
程序代码
class Solution {
public:
string frequencySort(string s) {
vector<pair<int, char>> v(125);
for(const auto& str : s){
v[str].first++;
v[str].second = str;
}
sort(v.rbegin(), v.rend());
string res;
for(auto cur : v){
for(int i = 0; i < cur.first; i++){
res += cur.second;
}
}
return res;
}
};
540. 有序数组中的单一元素
题目描述
给定一个只包含整数的有序数组,每个元素都会出现两次,唯有一个数只会出现一次,找出这个数。
示例 1:
输入: [1,1,2,3,3,4,4,8,8]
输出: 2
示例 2
输入: [3,3,7,7,10,11,11]
输出: 10
注意: 您的方案应该在 O(log n)时间复杂度和 O(1)空间复杂度中运行。
思路分析
方法一:对nums数组循环一遍,如果nums[i]!=nums[i+1],说明是nums[i]只有一个,循环节为2,也就是i+=2。
方法二:异或
利用异或的特殊性质,两个相同的数异或得出来的是0,0和任何一个数 i 异或等于 i 本身。
程序代码
c++版(方法一)
class Solution {
public:
int singleNonDuplicate(vector<int>& nums) {
for (int i = 0; i < nums.size() - 1; i += 2) {
if (nums[i] != nums[i + 1]) {
return nums[i];
}
}
return nums.back();
}
};
Python版(方法二)
class Solution:
def singleNonDuplicate(self, nums: List[int]) -> int:
ans=0
for i in nums :
ans=ans^i
return ans
拓展资料
1.【C++】【总结】unordered_map,unordered_set,map和set的用法和区别