目录
基础快乐
力扣第一题
题目描述:两数之和(简单)
双倍快乐
力扣第三题
题目描述:无重复字符的最长子串(中等)
三倍快乐
力扣第四题
题目描述:寻找两个正序数的中位数(困难)
力扣第一题
题目描述:两数之和(简单)
给定一个整数数组nums和一个整数目标值target,请你在该数组中找出和为目标值target的那两个整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。
你可以按任意顺序返回答案。
示例:
输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9,返回 [0, 1]输入:nums = [3,2,4], target = 6
输出:[1,2]输入:nums = [3,3], target = 6
输出:[0,1]解题思路
题意很好理解,就是在一个数组中找出两个数和等于指定数 的这两个数的下标。
说来惭愧,好久没做这种题了,刚开始写的时候连怎样定义数组都忘了。。不过问题也不大,言归正传,首先我想到的是使用两个循环,第一个循环用于获取第一个数的下标,第二个循环用于判断数组中的两个值相加是否等于指定的数,相等的话就获取第二个数的下标,然后就获取到了两个下标的值,最后通过数组返回这两个值。
代码实现
//自己所写的最优解,时间复杂度:O(N^2) //用时:四十分钟 public static int[] twoSum(int[] nums, int target) { //定义索引变量 int index1 = 0; int index2 = 0; //定义新数组 int[] nums1 = new int[2]; //循环数组 for (int i = 0; i < nums.length; i++) { //获取第一个索引 index1 = i; for (int j = i + 1; j < nums.length; j++) { //判断当前数组数的后每一位有无相加等于target的值, if (nums[i] + nums[j] == target) { //如果满足条件,获取第二个索引 index2 = j; //设置i的值,用于结束外层循环 i = nums.length; break; } } } //新数组赋值 nums1[0] = index1; nums1[1] = index2; //返回数组 return nums1; }
官方解答
//官方给的最优解,时间复杂度:O(N) public static int[] twoSum1(int[] nums, int target) { Map
hashtable = new HashMap (); //循环数组 for (int i = 0; i < nums.length; ++i) { //判断集合是否包含指定的键,包含的话就返回该值,不包含就添加到集合 if (hashtable.containsKey(target - nums[i])) { //返回这两个索引的值 return new int[]{hashtable.get(target - nums[i]), i}; } //向集合中添加元素,数组元素为键,索引为值 hashtable.put(nums[i], i); } return new int[0]; } 总结
自己写的代码能够成功运行,但是相对于效率而言,官方的解答时间复杂度更低,执行效率更高。原本以为能够做出来就行了,才发现差别还是蛮大的,一个效率高的算法真的是太考究了,立志今后解题尽量将效率达到极致!
力扣第三题
题目描述:无重复字符的最长子串(中等)
给定一个字符串
s
,请你找出其中不含有重复字符的最长子串的长度。示例:
输入:s = "abcabcbb"
输出:3
解释:因为无重复字符的最长子串是 “abc”,所以其长度为 3。输入:s = "bbbbb"
输出:1
解释:因为无重复字符的最长子串是 “b”,所以其长度为 1。输入:s = "pwwkew"
输出:3
解释:因为无重复字符的最长子串是 “wke”,所以其长度为 3。输入:s = ""
输出:0解题思路
刚开始是想用数组存放字符,然后循环进行比较,判断字符是否存在,最后返回最长的子串,然后发现难以实现,有一个问题解决起来太过于麻烦。。然后就果断换一种方式,想起来使用去重的集合,定义一个HashSet集合,该集合可以自动去重,用于循环将字符依次存放到该集合,再用循环进行判断集合中是否有该字符 (此处的判断是写在另一个方法内的,这里调用该方法进行判断),然后再记录集合的长度,重置集合,最后获取最大的长度并返回。
代码实现
//自己做的最优解,时间复杂度:O(N^2) //用时:一个小时。。 public static int lengthOfLongestSubstring2(String s) { //计长度 int k = 1; //最大的长度 int max = 0; //设置Set集合 HashSet
hashSet = new HashSet (); //判断字符串是否为空 if (s.length() != 0) { //循环字符串 for (int i = 0; i < s.length(); i++) { //赋值给集合 hashSet.add(s.charAt(i)); //循环字符的后一位 for (int j = i + 1; j < s.length(); j++) { //判断集合中是否存在字符 if (!ifTrue(hashSet, s.charAt(j))) { //不存在就添加到集合 hashSet.add(s.charAt(j)); } else break; //记录集合长度 k = hashSet.size(); } //集合重置 hashSet.clear(); //判断并赋值 if (k > max) { max = k; } } } return max; } //判断集合中是否有字符c public static boolean ifTrue(HashSet hashSet, char c) { boolean b = false; for (Character s : hashSet) { if (s == c) { return true; } } return b; } 波仔的解答
//波仔的解答,时间复杂度:O(N) public static int lengthOfLongestSubstring1(String s) { int n = s.length(), ans = 0; //定义一个hash表,字符串和长度 Map
map = new HashMap<>(); for (int end = 0, start = 0; end < n; end++) { //获取单个字符 char alpha = s.charAt(end); //如果查找到有数据 if (map.containsKey(alpha)) { //更新start start = Math.max(map.get(alpha), start); } //不管存进来没更新一下ans,就是我们得结果 //end-start+1就是我们统计出来字符串得长度 ans = Math.max(ans, end - start + 1); //存入数据,有重复得在上面已经查找过了,所以一定能找出来 map.put(s.charAt(end), end + 1); } return ans; } 评论区大哥解答
//评论区大哥的解答,时间复杂度:O(N) public static int lengthOfLongestSubstring(String s) { //记录字符上一次出现的位置 int[] last = new int[128]; for (int i = 0; i < 128; i++) { last[i] = -1; } int n = s.length(); int res = 0; int start = 0; //窗口开始位置 for (int i = 0; i < n; i++) { //获取当前字符的ASCII码 int index = s.charAt(i); //a --97 //获取字符出现的最大下标值 start = Math.max(start, last[index] + 1); //获取最大长度的子串 res = Math.max(res, i - start + 1); //下标值存放到数组 last[index] = i; } return res; }
总结
刚开始的解题思路不对,绕了很久也花了很多时间也没做出来,后面用集合才勉强做出来,先把思路确定好了再行动,写代码的效率应该会高些。后面看了小波波和评论区大哥的解答,才发现是我肤浅了,是我没想到的方法,逻辑性比较强,下次再遇到这种类型的题,我必定将它拿捏!
力扣第四题
题目描述:寻找两个正序数的中位数(困难)
给定两个大小分别为
m
和n
的正序 (从小到大)数组nums1
和nums2
。请你找出并返回这两个正序数组的中位数。示例:
输入:nums1 = [1,3], nums2 = [2]
输出:2.00000
解释:合并数组 = [1,2,3],中位数 2。输入:nums1 = [1,2], nums2 = [3,4]
输出:2.50000
解释:合并数组 = [1,2,3,4] ,中位数 (2 + 3) / 2 = 2.5输入:nums1 = [0,0], nums2 = [0,0]
输出:0.00000输入:nums1 = [], nums2 = [1]
输出:1.00000解题思路
刚看到这题目就有一个疑问,这么简单的题咋会定义为困难哦,也没管那么多,先实现了再说,想到的就是把两个数组合并到一个新数组,然后用arrays中的sort方法排序,最后判断数组的长度,算出中位数并返回。
代码实现
//自己所写的最优解,时间复杂度:O(N) //用时:十五分钟 public static double findMedianSortedArrays(int[] nums1, int[] nums2) { //定义变量 double num = 0; //创建一个新数组,用于存放给出的两个数组值 int[] nums3 = new int[nums1.length + nums2.length]; //循环将第一个数组值赋值给新数组 for (int i = 0; i < nums1.length; i++) { nums3[i] = nums1[i]; } //循环将第二个数组继续赋值给新数组 for (int i = 0, j = nums1.length; i < nums2.length; i++, j++) { nums3[j] = nums2[i]; } //使用Arrays的方法排序 Arrays.sort(nums3); //判断数组长度是奇数还是偶数 if (nums3.length % 2 != 0) { //奇数,直接取出中位数 num = nums3[nums3.length / 2]; } else { //偶数,取出中间那个下标的值,再加上它前面那个值,除以二就是中位数 num = nums3[nums3.length / 2] + nums3[nums3.length / 2 - 1]; num /= 2; } return num; }
官方解答
//官方解答,时间复杂度:O(log (m+n)),m和n分别是数组nums1和nums2的长度 public static double findMedianSortedArrays1(int[] nums1, int[] nums2) { //比较数组长度,数组长度较短的作为第一个参数 if (nums1.length > nums2.length) { return findMedianSortedArrays(nums2, nums1); } int m = nums1.length; int n = nums2.length; int left = 0, right = m; // median1:前一部分的最大值 // median2:后一部分的最小值 int median1 = 0, median2 = 0; while (left <= right) { // 前一部分包含 nums1[0 .. i-1] 和 nums2[0 .. j-1] // 后一部分包含 nums1[i .. m-1] 和 nums2[j .. n-1] int i = (left + right) / 2; int j = (m + n + 1) / 2 - i; // nums_im1, nums_i, nums_jm1, nums_j 分别表示 nums1[i-1], nums1[i], nums2[j-1], nums2[j] int nums_im1 = (i == 0 ? Integer.MIN_VALUE : nums1[i - 1]); int nums_i = (i == m ? Integer.MAX_VALUE : nums1[i]); int nums_jm1 = (j == 0 ? Integer.MIN_VALUE : nums2[j - 1]); int nums_j = (j == n ? Integer.MAX_VALUE : nums2[j]); if (nums_im1 <= nums_j) { median1 = Math.max(nums_im1, nums_jm1); median2 = Math.min(nums_i, nums_j); left = i + 1; } else { right = i - 1; } } //返回中位数 return (m + n) % 2 == 0 ? (median1 + median2) / 2.0 : median1; }
总结
看了后面的解答才知道原来还有一个进阶,就是要求时间复杂度为O(log(m+n)),这就有点不好办了。说实话如果题目加上这种硬性的要求的话,我都不知道怎么写了,定义为困难级别还是有原因的。参考了官方的解答,然后再层层解析,还是能够领悟其中的奥秘,眼高手低这种事情,不会再有下次了!