datawhale组队训练——LeetCode分类练习——查找1

题目链接:
第一题: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的用法和区别

2.C++ STL之unordered_map和unordered_set的使用

3.C++中map的详细用法

4.Python 字典(Dictionary)

5.python-----删除列表中某个元素的3种方法

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值