LeetCode编程题-----数组|121、169、217题解及优化

这篇博客详细讲解了LeetCode三道数组题目——121.买卖股票的最佳时机、169.求众数、217.存在重复元素的解决方案。对于121题,博主提出了一种O(n)复杂度的“边走边看”思想;169题中,博主探讨了使用HashMap和排序的方法;217题则讨论了如何利用HashSet的特性降低复杂度。每道题都包含了不止一种解法,并强调了思维方式的转变和算法的优雅实现。
摘要由CSDN通过智能技术生成

121.买卖股票的最佳时机

在这里插入图片描述

1.这道题看了的第一想法就是暴力求解…

  • 定义一个变量来存放当前最大差值,遍历数组中每个数,与它之后的每个大于它的数进行求差
  • 遍历完后变量中存放的就是最大差值了,但这样的想法一上来就应该避免,因为复杂度太高O(n^2),但是想了很久也没有更简单的做法(还是做题少)
//击败9%
 public int maxProfit(int[] prices) {
 	 int sum = 0;
 	 for (int i = 0; i < prices.length - 1; i++) {
  		 for (int j = i + 1; j < prices.length; j++) {
   			 if (prices[i] < prices[j]) {
    				 int sum1 = prices[j] - prices[i];
    				 if (sum < sum1) {
      					sum = sum1;
    				 }
   			 }
  		 }
 	 }
	  return sum;
 }

2.寻求思路的过程中,发現有个思路比较简便,而且做法清晰

  • 寻求当前最小峰值当前的最大差值:用两个变量,一个用来存放当前最小,一个用来存放差值
  • 在一次遍历的过程中通过不断移动指针来比较得出当前最小值,如果不是最小值就计算与其差值,差值再与存放差值的变量比较,得出最大差值
  • 复杂度为O(n),用的是我认为“边走边看”的思想。我觉得精髓还是在于变量存储的是什么,以及在一次遍历过程中完成的任务。
//击败99.9%
 public int maxProfit(int[] prices) {
  	int min = 0;
 	if (prices.length > 0)
  		 min = prices[0];
  	int sum = 0;
 	for (int i = 0; i < prices.length; i++) {
  		 if (prices[i] < min) {
    			min = prices[i];
  		 } else {
   			 if (prices[i] - min > sum) {
    				 sum = prices[i] - min;
   			 }
  		 }
 	 }
 	 return sum;
 }

169.求众数

在这里插入图片描述

1.练习用Hashset存放

  • 最近练习的时候,发现了自己的思维局限,总是依赖于暴力求解for套for,所以有意识的用之前借鉴的别人的方法和思路来看待问题,希望能够在刷题过程中养成新的思维习惯,能够从更多角度看待问题
    在我之前的一道题中,有人的方法提到了HashSet,于是我就去复习了相关的关于容器的知识。所以这道题
  • 分析完了之后,发现可以用key- value来记录某些数出现的次数,使用Hashmap
    当然前提是有序数组,所以先排序,排完后用一个变量n来记录当前的值的第一个位置,用i++去遍历它的次数
  • 如果第i个数不是第n个元素对应的值,那么就说明这个值的次数已经得到了,将它存入map中,最后遍历map的value值找到最大,返回其key值即可(当然还要考虑最后一个元素时的特殊情况,如果与前面相等就将次数加一,否则就put新值)。
  • 虽然这样做可能有些麻烦,但是也算是练习了hashmap,更加了解了hashmap的使用场景,改变我的固定思维
// 35%
 public void majorityElement(int[] nums) {
  	Arrays.sort(nums);
  	Map<Integer, Integer> map = new HashMap<Integer, Integer>();
  	int n = 0;
  	for (int i = 0; i < nums.length; i++) {
  		if (nums[i] != nums[n] || i == nums.length - 1) {
   			if (i == nums.length - 1 && nums[i] == nums[n]) {
    				i++;
    			} else {
    				 map.put(nums[i], 1);
    			}
    			map.put(nums[n], i - n);
    			n = i;
  		 }
  	}
  	int nn = 0;
  	int max = 0;
  	for (Integer key : map.keySet()) {
  		 if (map.get(key) > max) {
    			nn = key;
    			max = map.get(key);
  		 }
 	 }
 	 System.out.println(nn);
 }

2.还有一种方法也叫“蠕虫思想”

  • 其实跟上面2的方法类似,不过是设置变量存放当前最符合的值而忽略其余
//15%
 public int majorityElement(int[] nums) {
  	Arrays.sort(nums);
  	System.out.println(Arrays.toString(nums));
  	int n = 1;
  	if (nums.length == 1) {
   		return nums[0];
  	}
  	for (int i = 0; i < nums.length - 1; i++) {
   		if (nums[i] == nums[i + 1]) {
   			 n++;
   	 		if (n > nums.length / 2) {
    			 	return nums[i];
   			 }
 		} else {
    			n = 1;
   		}
 	 }
 	 return -1;
 }

3.借鉴解题区的优质方法(最简单易懂):

  • 直接排序输出中间数(由于这里众数的定义为超过数组1/2,同时题中给出的数字总有众数,故排序后中间的数绝对为中位数)。
  • 没想到代码可以写的这么优雅,这可能就是算法的魅力吧
 //96%
 public int majorityElement(int[] nums) {
  	Arrays.sort(nums);
 	return nums[nums.length/2];
 }

217.存在重复元素

在这里插入图片描述
1.分析之后,不难想出

  • 将其中元素逐个遍历,对比他与剩下的元素是否有相同的,相同就返回true,否则返回false

  • 可这样的复杂度是O(n^2),有没有更好的办法呢

//5%
 public boolean containsDuplicate(int[] nums) {
  	for(int i = 0;i<nums.length-1;i++) {
  		 for(int j = i+1;j<nums.length;j++) {
    			if(nums[i] == nums[j]) {
    				 return true;
   			 }
  		 }
 	 }
  	return false;
 }

2.再来读题

  • 如果这是个有序的数组,那么,判断是否有重复元素只需比较当前元素和它的下一个元素,再将指针后移,遍历整个数组
  • 如果相同,就立即返回true,否则就返回false,这样复杂度就直接降到了O(n)
//89%
 public boolean containsDuplicate(int[] nums) {
 	 Arrays.sort(nums);
	 for(int i = 0;i<nums.length-1;i++) {
  	 	if(nums[i] == nums[i+1]) {
   	  		return true;
   		 }
	 }
 	 return false;
 }

3.在做完后,查看了其他人的题解,想要借鉴更好的思路,发现一个比较有意思的做法:

  • 由于HashSet具有不能存放重复元素的特性,故将数组中的元素一一存入
  • 存入失败,则说明元素在HashSet中已经存在,则返回true,否则false
  • 这样的复杂度同样是O(n),但是却巧妙的利用了容器的特性,容器既然学了它的用法,就要学会学以致用,熟悉它的各种方法,用的多了自然会熟练
public boolean containsDuplicate(int[] nums) {
  	Set<Integer> set = new HashSet<Integer>();
   	for(int i:nums) {
    		if(!set.add(i)) 
    			return true;
 	  }
   	return false;

  }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值