Task3:查找1
35. 搜索插入位置
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
你可以假设数组中无重复元素。
示例 1:
输入: [1,3,5,6], 5
输出: 2
示例 2::
输入: [1,3,5,6], 2
输出: 1
解析:
遍历数组,与目标值逐一比较,如果当前值小于或者等于目标值则返回当前值的下标,否则直接返回数组长度,即为应该插入的位置;
javascript代码如下:
var searchInsert = function(nums, target) {
for(let i=0;i<nums.length;i++){
if(target<nums[i]||target==nums[i]){
return i;
}
}
return nums.length;
};
法二:二分查找java代码
class Solution {
public int searchInsert(int[] nums, int target) {
int left=0,right=nums.length-1,mid;
while(left<=right){
mid = (left+right)/2;
if(target==nums[mid]){
return mid;
}else if(target>nums[mid]){
left = mid+1;
}else{
right=mid-1;
}
}
return left;
}
}
202. 快乐数
编写一个算法来判断一个数 n 是不是快乐数。
「快乐数」定义为:对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和,然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。如果 可以变为 1,那么这个数就是快乐数。
如果 n 是快乐数就返回 True ;不是,则返回 False 。
示例:
输入:19
输出:true
解释:
12 + 92 = 82
82 + 22 = 68
62 + 82 = 100
12 + 02 + 02 = 1
解析:
判断每位平方和是否等于1,若是,则返回true,否则,判断set里面是否包含该平方和,若包含,则进入死循环,返回false,若不是,则添加进set里,将该数替换成该平方和
java代码如下:
class Solution {
public boolean isHappy(int n) {
Set<Integer> set = new HashSet<>();
int m=0;
while(true){
//计算每位的平方和
while(n!=0){
m += Math.pow(n%10,2);
n = n/10;
}
if(m==1){
return true;
}
if(set.contains(m)){
return false;
}else{
set.add(m);
n = m;
m = 0;
}
}
}
}
205. 同构字符串
给定两个字符串 s 和 t,判断它们是否是同构的。
如果 s 中的字符可以被替换得到 t ,那么这两个字符串是同构的。
所有出现的字符都必须用另一个字符替换,同时保留字符的顺序。两个字符不能映射到同一个字符上,但字符可以映射自己本身。
示例 1:
输入: s = “egg”, t = “add”
输出: true
示例 2:
输入: s = “foo”, t = “bar”
输出: false
解析:
利用一个 map 来处理映射。对于 s 到 t 的映射,我们同时遍历 s 和 t ,假设当前遇到的字母分别是 c1 和 c2 。
如果 map[c1] 不存在,那么就将 c1 映射到 c2 ,即 map[c1] = c2。
如果 map[c1] 存在,那么就判断 map[c1] 是否等于 c2,也就是验证之前的映射和当前的字母是否相同。同样,也需要处理t到s的映射。
class Solution {
public boolean isIsomorphic(String s, String t) {
return isIsomorphicHelper(s,t)&&isIsomorphicHelper(t,s);
}
private boolean isIsomorphicHelper(String s,String t){
int n=s.length();
HashMap<Character,Character> map = new HashMap();
for (int i=0;i<n;i++){
char c1 = s.charAt(i);
char c2 = t.charAt(i);
if(map.containsKey(c1)){
if(map.get(c1) != c2){
return false;
}
}else{
map.put(c1,c2);
}
}
return true;
}
}
242. 有效的字母异位词
给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。
示例 1:
输入: s = “anagram”, t = “nagaram”
输出: true
示例 2:
输入: s = “rat”, t = “car”
输出: false
说明:
你可以假设字符串只包含小写字母。
解析:
利用哈希映射:首先判断两个字符串长度是否相等,不相等则直接返回 false;若相等,则初始化 26 个字母哈希表,遍历字符串 s 和 t;s 负责在对应位置增加,t 负责在对应位置减少;如果哈希表的值都为 0,则二者是字母异位词
class Solution {
public boolean isAnagram(String s, String t) {
if(s.length() != t.length())
return false;
int[] alpha = new int[26];
for(int i = 0; i< s.length(); i++) {
alpha[s.charAt(i) - 'a'] ++;
alpha[t.charAt(i) - 'a'] --;
}
for(int i=0;i<26;i++)
if(alpha[i] != 0)
return false;
return true;
}
}
290. 单词规律
给定一种规律 pattern 和一个字符串 str ,判断 str 是否遵循相同的规律。
这里的 遵循 指完全匹配,例如, pattern 里的每个字母和字符串 str 中的每个非空单词之间存在着双向连接的对应规律。
示例1:
输入: pattern = “abba”, str = “dog cat cat dog”
输出: true
示例 2:
输入:pattern = “abba”, str = “dog cat cat fish”
输出: false
解析:
利用 HashMap ,按照单词出现的顺序,把两个字符串都映射到另一个集合中。第一次出现的单词(字母)映射到 1 ,第二次出现的单词(字母)映射到 2… 依次类推,这样就会得到一个新的字符串了。两个字符串都通过这样方法去映射,然后判断新得到的字符串是否相等 。
pattern = “abba”, str = “dog cat cat dog”
对于 abba
a -> 1
b -> 2
最终的得到的字符串就是 1221
对于 dog cat cat dog
dog -> 1
cat -> 2
最终的得到的字符串就是 1221
最终两个字符串都映射到了 1221, 所以 str 符合 pattern
class Solution {
public boolean wordPattern(String pattern, String str) {
String[] array = str.split(" ");
if(array.length!=pattern.length()){
return false;
}
//判断映射后的结果是否相等
return wordPatternHelper(pattern.split("")).equals(wordPatternHelper(array));
}
private String wordPatternHelper(String[] array) {
HashMap<String, Integer> map = new HashMap<>();
int count = 1;
//保存映射后的结果
StringBuilder sb = new StringBuilder();
for (int i = 0; i < array.length; i++) {
//是否已经映射过
if (map.containsKey(array[i])) {
sb.append(map.get(array[i]));
} else {
sb.append(count);
map.put(array[i], count);
count++;
}
}
return sb.toString();
}
}
349. 两个数组的交集
给定两个数组,编写一个函数来计算它们的交集。
示例 1:
输入:nums1 = [1,2,2,1], nums2 = [2,2]
输出:[2]
示例 2:
输入:nums1 = [4,9,5], nums2 = [9,4,9,8,4]
输出:[9,4]
解析:
用两个set,一个set用来装nums1中所出现过的元素,然后去遍历另一个数组,第二个数组中的元素凡是不在第一个set中的都去除,这样第二个set中放的就是两个数组中公有的元素且不重复,然后用一个队列来装这些元素,最后用新的数组返回。
class Solution {
public int[] intersection(int[] nums1, int[] nums2) {
HashSet<Integer> set = new HashSet<>();
HashSet<Integer> set2 = new HashSet<>();
Queue<Integer> queue = new LinkedList<>();
for(int i = 0;i < nums1.length;i++) {
if(!set.contains(nums1[i])) {
set.add(nums1[i]);
}
}
for(int i = 0;i < nums2.length;i++) {
if(set.contains(nums2[i])) {
set2.add(nums2[i]);
}
}
Iterator<Integer> it = set2.iterator();
while(it.hasNext()) {
queue.offer(it.next());
}
int size = queue.size();
int[] arr = new int[size];
for(int i = 0;i < size;i++) {
arr[i] = queue.poll();
}
return arr;
}
}
解析:
由于同一个数字在两个数组中都可能出现多次,因此需要用哈希表存储每个数字出现的次数。对于一个数字,其在交集中出现的次数等于该数字在两个数组中出现次数的最小值。
首先遍历第一个数组,并在哈希表中记录第一个数组中的每个数字以及对应出现的次数,然后遍历第二个数组,对于第二个数组中的每个数字,如果在哈希表中存在这个数字,则将该数字添加到答案,并减少哈希表中该数字出现的次数。
为了降低空间复杂度,首先遍历较短的数组并在哈希表中记录每个数字以及对应出现的次数,然后遍历较长的数组得到交集。
class Solution {
public int[] intersect(int[] nums1, int[] nums2) {
if (nums1.length > nums2.length) {
return intersect(nums2, nums1);
}
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
for (int num : nums1) {
int count = map.getOrDefault(num, 0) + 1;
map.put(num, count);
}
int[] intersection = new int[nums1.length];
int index = 0;
for (int num : nums2) {
int count = map.getOrDefault(num, 0);
if (count > 0) {
intersection[index++] = num;
count--;
if (count > 0) {
map.put(num, count);
} else {
map.remove(num);
}
}
}
return Arrays.copyOfRange(intersection, 0, index);
}
}
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,在所有情况中最小。
解析:
「使……最大值尽可能小」是二分搜索题目常见的问法。二分的上界为数组 \textit{nums}nums 中所有元素的和,下界为数组 \textit{nums}nums 中所有元素的最大值。通过二分查找,我们可以得到最小的最大分割子数组和
class Solution {
public int splitArray(int[] nums, int m) {
int left = 0, right = 0;
for (int i = 0; i < nums.length; i++) {
right += nums[i];
if (left < nums[i]) {
left = nums[i];
}
}
while (left < right) {
int mid = (right - left) / 2 + left;
if (check(nums, mid, m)) {
right = mid;
} else {
left = mid + 1;
}
}
return left;
}
public boolean check(int[] nums, int x, int m) {
int sum = 0;
int cnt = 1;
for (int i = 0; i < nums.length; i++) {
if (sum + nums[i] > x) {
cnt++;
sum = nums[i];
} else {
sum += nums[i];
}
}
return cnt <= m;
}
}
451. 根据字符出现频率排序
给定一个字符串,请将字符串里的字符按照出现的频率降序排列。
示例 1:
输入:
“tree”
输出:
“eert”
解释:
'e’出现两次,'r’和’t’都只出现一次。
因此’e’必须出现在’r’和’t’之前。此外,"eetr"也是一个有效的答案。
解析:
通过 int 数组记录 每个字符出现的次数
通过 HashMap 将出现次数相同的字符进行拼接
将 出现次数 排序, 倒序取出所有字符进行拼接
代码:
class Solution {
public String frequencySort(String s) {
HashMap<Integer, String> map = new HashMap<>();
int[] freq = new int[256];
for (char c : s.toCharArray()) {
freq[c]++;
}
for (int i = 0; i < freq.length; i++) {
if (freq[i] != 0) {
char ch = (char) i;
String str = map.get(freq[i]);
// 字符出现次数相同, 进行拼接
if (str != null) {
String strNew = str.concat(build(ch, freq[i]));
map.put(freq[i], strNew);
}else {
map.put(freq[i], build(ch, freq[i]));
}
}
}
Integer[] keys = map.keySet().toArray(new Integer[]{});
Arrays.sort(keys);
StringBuilder sbl = new StringBuilder();
for (int i = keys.length - 1; i >= 0; i--) {
sbl.append(map.get(keys[i]));
}
return sbl.toString();
}
private String build(char ch, int times) {
String string = Character.toString(ch);
StringBuilder res = new StringBuilder(string);
int t = 1;
while (t < times) {
res.append(string);
t++;
}
return res.toString();
}
}
540. 有序数组中的单一元素
给定一个只包含整数的有序数组,每个元素都会出现两次,唯有一个数只会出现一次,找出这个数。
示例 1:
输入: [1,1,2,3,3,4,4,8,8]
输出: 2
示例 2:
输入: [3,3,7,7,10,11,11]
输出: 10
解析:
用二分法,取数组中间的数,当中间数的下标为奇数,说明前后元素的个数为奇数,偶数则剩余个数为偶数。奇数时:当nums[h]等于[h+1],唯一数处于前h-1,反之处于后h+1。偶数时:当nums[h]等于[h+1],唯一数处于后h+2,反之处于前h.(就是要保证剩余查找元素个数奇数)
class Solution {
public int singleNonDuplicate(int[] nums) {
int l=0,r=nums.length-1;
while(l<r){
int h=(r+l)/2;
if(h%2==1){
if(nums[h]==nums[h+1])
r=h-1;
else
l=h+1;
}else{
if(nums[h]==nums[h+1])
l=h+2;
else
r=h;
}
}
return nums[l];
}
}