文章目录
前言
需要开通vip的题目暂时跳过
笔记导航
点击链接可跳转到所有刷题笔记的导航链接
341.扁平化嵌套列表迭代器
给你一个嵌套的整型列表。请你设计一个迭代器,使其能够遍历这个整型列表中的所有整数。
列表中的每一项或者为一个整数,或者是另一个列表。其中列表的元素也可能是整数或是其他列表。
-
解答
public class NestedIterator implements Iterator<Integer> { private Stack<Integer> s1 = new Stack<>(); private Stack<Integer> s2 = new Stack<>(); public NestedIterator(List<NestedInteger> nestedList) { dfs(nestedList); while(!s1.empty()) { s2.push(s1.pop()); } } @Override public Integer next() { return s2.pop(); } @Override public boolean hasNext() { return !s2.isEmpty(); } private void dfs(List<NestedInteger> nestedList) { for (NestedInteger temp : nestedList) { if (temp.isInteger()) { s1.push(temp.getInteger()); } else { dfs(temp.getList()); } } } }
-
分析
- 深度搜索遍历给定的嵌套链表
- 遇到数字则直接压栈,否则递归
- 然后将栈中的元素出栈进另一个栈中。
- 之后调用next()方法只需要直接返回栈顶即可
- hasNext()就判断栈是否为空。
-
提交结果
342.4的幂
给定一个整数 (32 位有符号整数),请编写一个函数来判断它是否是 4 的幂次方。
进阶:
你能不使用循环或者递归来完成本题吗?
-
解答
//方法一 public static boolean isPowerOfFour(int num) { String s = Integer.toString(num, 4); if (s.charAt(0) != '1') return false; for (int i = 1; i < s.length(); i++) { if (s.charAt(i) != '0') { return false; } } return true; } //方法二 public boolean isPowerOfFour(int num) { return (num > 0) && ((num & (num - 1)) == 0) && ((num & 0xaaaaaaaa) == 0); } //方法三 public static boolean isPowerOfFour(int num) { return (num > 0) && (Math.log(num) / Math.log(2) % 2 == 0); }
-
分析
-
方法一,一开始的想到的就是将num转换成4进制。这样如果是4的幂次,那么只有第一位是1,其余位为0。
-
方法二,在方法1的基础上,他的2进制也是满足首位为1,其余位为0。并且可以发现4的幂次和二进制(101010…10)做与运算等于0。num & 0xaaaaaaaa) == 0光这个判断还不够,因为例如5也符合条件。所以需要加上(num & (num - 1)) == 0,这个条件成立,表示仅有最高位为1.
-
方法三,根据数学公式推导 ,得到a是个整数即可。
-
-
提交结果
方法一
方法二
方法三
343. 整数拆分
给定一个正整数 n,将其拆分为至少两个正整数的和,并使这些整数的乘积最大化。 返回你可以获得的最大乘积。
-
解答
// 方法一 int[] maxMut = new int[59]; public int integerBreak(int n) { calMax(); return maxMut[n]; } public void calMax(){ maxMut[1] = 1; maxMut[2] = 1; for(int i = 3;i<=58;i++){ for(int j = 1;j<=i/2;j++){ int remain = i-j; maxMut[i] = Math.max(maxMut[i],Math.max(Math.max(j * remain,maxMut[j] * remain),j * maxMut[remain])); } } } //方法二 public int integerBreak(int n) { int[] dp = new int[n+1]; dp[1] = 1; dp[2] = 1; for (int i = 3;i <= n;i++){ for(int j = 1;j <= i - j;j++){ dp[i] = Math.max(dp[i],j*(i-j)); dp[i] = Math.max(dp[i],j*dp[i-j]); } } return dp[n]; } //方法三 int[] maxMut = new int[59]; public int integerBreak(int n) { calMax(); return maxMut[n]; } public void calMax() { maxMut[1] = 1; maxMut[2] = 1; for (int i = 3; i <= 58; i++) { maxMut[i] = Math.max(Math.max(2 * (i - 2), 2 * maxMut[i - 2]), Math.max(3 * (i - 3), 3 * maxMut[i - 3])); } }
-
分析
-
方法一记忆集搜索
-
将每个数字可以得到的最大乘机缓存下来。
-
遍历3~58,每个数字进行拆分成j和remain,得到的结果最大值有2种情况
- j * remain 拆分后的乘积为最大
- j * matMut[remain] 剩余部分继续拆分后的乘积更大
-
方法二
-
参考方法一写成dp的形式。
-
方法三
-
在第一个方法上的改进,每次数字拆分仅要考虑拆成2和i-2 或3和i-3即可。
-
-
提交结果
方法一
方法二
方法三
344. 反转字符串
编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 char[] 的形式给出。
不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。
你可以假设数组中的所有字符都是 ASCII 码表中的可打印字符。
-
解答
// 方法一 public void reverseString(char[] s) { reverseString(s,0,s.length-1); } public void reverseString(char[] s,int left,int right){ if(right <= left)return; char temp = s[left]; s[left++] = s[right]; s[right--] = temp; reverseString(s,left,right); } //方法二 public void reverseString(char[] s) { int len = s.length; for(int i = 0;i < len/2;i++){ char temp = s[i]; s[i] = s[len-1-i]; s[len-1-i] = temp; } }
-
分析
- 方法一 递归版首尾交换,但是会用到栈空间
- 方法二 首尾指针。
-
提交结果
方法一
方法二
345.反转字符串中的元音字母
编写一个函数,以字符串作为输入,反转该字符串中的元音字母。
-
解答
public String reverseVowels(String s) { int len = s.length(); if (len <= 1) return s; char[] chars = new char[]{ 'a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U'}; HashSet<Character> hash = new HashSet<>(); for (int i = 0; i < chars.length; i++) { hash.add(chars[i]); } int l = 0, r = len - 1; char[] arr = s.toCharArray(); while (l <= r) { boolean jL = hash.contains(arr[l]); boolean jR = hash.contains(arr[r]); if (jL && jR) { char temp = arr[l]; arr[l++] = arr[r]; arr[r--] = temp; } else if (jL && !jR) { r--; } else if (!jL && jR) { l++; } else { r--; l++; } } return new String(arr); }
-
分析
- 双指针,头尾遍历,寻找到元音字母交换。
-
提交结果
347.前 K 个高频元素
给定一个非空的整数数组,返回其中出现频率前 *k* 高的元素。
提示:
你可以假设给定的 k 总是合理的,且 1 ≤ k ≤ 数组中不相同的元素的个数。
你的算法的时间复杂度必须优于 O(n log n) , n 是数组的大小。
题目数据保证答案唯一,换句话说,数组中前 k 个高频元素的集合是唯一的。
你可以按任意顺序返回答案。
-
解答
public int[] topKFrequent(int[] nums, int k) { HashMap<Integer, Integer> map = new HashMap<>(); PriorityQueue<Pair<Integer, Integer>> priorityQueue = new PriorityQueue<>(new Comparator<Pair>() { @Override public int compare(Pair o1, Pair o2) { return (int) o2.getValue() - (int) o1.getValue(); } }); for (int i = 0; i < nums.length; i++) { map.put(nums[i], map.getOrDefault(nums[i], 0) + 1); } for (Integer integer : map.keySet()) { priorityQueue.add(new Pair(integer, map.get(integer))); } int[] result = new int[k]; for (int i = 0; i < k; i++) { result[i] = priorityQueue.poll().getKey(); } return result; }
-
分析
- 第一个map用于记录数字和对应出现的次数。
- 优先级队列,当作大顶堆,这样从堆顶取出的元素就是里面最大的。这个堆根据map中数字的次数大小来构建。
- 第一个循环,遍历nums数组,统计数字出现的次数。
- 第二个循环,遍历map,将数字和对应的次数组成Pair对象,添加到优先级队列。
- 第三个循环,从优先级队列中取堆顶元素,取k次。即可得到答案。
-
提交结果
349.两个数组的交集
给定两个数组,编写一个函数来计算它们的交集。
-
解答
public int[] intersection(int[] nums1, int[] nums2) { HashSet<Integer> set = new HashSet<>(); HashSet<Integer> used = new HashSet<>(); for(int i = 0;i<nums1.length;i++){ set.add(nums1[i]); } ArrayList<Integer> list = new ArrayList<>(); for(int i = 0;i<nums2.length;i++){ if(set.contains(nums2[i]) && !used.contains(nums2[i])){ list.add(nums2[i]); used.add(nums2[i]); } } int[] res = new int[list.size()]; for(int i = 0;i< res.length;i++){ res[i] = list.get(i); } return res; }
-
分析
- 第一个set,用来存nums1中出现的数字。
- used用来存已经在答案中的数字,避免重复添加。
- list用来存答案。最后根据这个list的大小构建一个数字,然后将list里的数据放到数组中返回。
- 第一个for循环,将nums1中的数字加入到set集合中。
- 第二个for循环,遍历nums2,判断是否在set中出现并且还没有出现在答案中。若满足条件,则加入到list和used中。
- 第三个for循环,将list中的答案放到数组中
-
提交结果
350. 两个数组的交集 II
给定两个数组,编写一个函数来计算它们的交集。
说明:
- 输出结果中每个元素出现的次数,应与元素在两个数组中出现次数的最小值一致。
- 我们可以不考虑输出结果的顺序。
-
解答
//方法一 public int[] intersect(int[] nums1, int[] nums2) { HashMap<Integer,Integer> map1 = new HashMap<>(); HashMap<Integer,Integer> map2 = new HashMap<>(); for(int num:nums1){ map1.put(num,map1.getOrDefault(num,0)+1); } for(int num:nums2){ map2.put(num,map2.getOrDefault(num,0)+1); } ArrayList<Integer> list = new ArrayList<>(); for(Integer i :map2.keySet()){ if(map1.containsKey(i)){ int number = Math.min(map1.get(i),map2.get(i)); for(int j = 0;j<number;j++){ list.add(i); } } } int[] res = new int[list.size()]; for(int i = 0;i<res.length;i++){ res[i] = list.get(i); } return res; } // 方法二 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[] res = new int[nums1.length]; int index = 0; for (int num : nums2) { int count = map.getOrDefault(num, 0); if (count > 0) { res[index++] = num; count--; if (count > 0) { map.put(num, count); } else { map.remove(num); } } } return Arrays.copyOfRange(res, 0, index); } //方法三 public int[] intersect(int[] nums1, int[] nums2) { Arrays.sort(nums1); Arrays.sort(nums2); int length1 = nums1.length, length2 = nums2.length; int[] res = new int[Math.min(length1, length2)]; int index1 = 0, index2 = 0, index = 0; while (index1 < length1 && index2 < length2) { if (nums1[index1] < nums2[index2]) { index1++; } else if (nums1[index1] > nums2[index2]) { index2++; } else { res[index] = nums1[index1]; index1++; index2++; index++; } } return Arrays.copyOfRange(res, 0, index); }
-
分析
- 方法一
- map1和map2分别统计两个数组中数字出现的次数
- 前两个for循环就是做这件事情
- 然后遍历map2中的键值对。判断是否在map1中有出现一样的数字。
- 若出现了,则判断两个map中记录该数字出现的次数,选择小的那个,添加多次该数字到答案中
- 然后根据list构建一个数字,将list中的答案放入数组中,返回。
- 方法二
- 对方法一进行了改进,不需要先统计出两个数组中数字出现的次数,只需要先统计长度短的数组中数字出现的次数。
- 然后遍历长度长的那个数组
- 判断是否在map中出现了。若出现了,则添加到答案中,并使map中该数字对应的次数-1。若结果为0了,则将该数字从map中移除。因为决定答案结果是出现次数少的一个数组里该数字的次数。
- 方法三
- 先将两个数组排序。
- 使用双指针来同时遍历两个数组。
- 若两个指针指向的数字相同,则添加到答案中。然后两个指针后移
- 若不相同,则将指向数字小的一个指针后移。
- 当一个指针超过了数组的界限则停下。
-
提交结果
方法一
方法二
方法三