LeetCode---ugly number I and II

好久不来这里写文章了呢!前几天本来是要做Super Ugly Number 这道题的,因为想接着之前做的题目复习一下数据结构中堆的知识。就在heap分类中找了一道medium,通过率看着挺高的题目,很久不来LeetCode写题目了,肯定是要挑一道较简单得练练手,练练脑~不过,以我自己的实际经历来说,不能以为AC率高的题就是简单的题。委屈被虐了,然后看到它的姊妹题目,ugly number I和ugly number II。心想,先把Super  ugly number这道题的前序题目做出来,那这道题肯定也不在话下了。现实又一次把我虐哭了,最后把ugly number I 和II都AC了,也没弄明白super ugly number...最后的最后,晚上跟小狐狸讨论了一下。第二天他说这道题对我来说有点难,就先放过它吧。。。

先看Ugly Number I,这道题目还是很简单的。判断一个数是否是丑陋数,也就是判断一个正数的质因数是否只包含2,3,5。在这道题中,1也是丑陋数。

分析:也就是说,如果一个数循环除以2,3,5之后余数是0,那么这个数就是丑陋数;如果余数不为0,说明这个数的质因数不只包含2,3,5还有其他数,也就不是丑陋数。有了思路后,代码实现就so easy了。

public boolean isUgly(int num) {
        if(num<=0)
	<span style="white-space:pre">	</span>return false;
        if(num%2==0){
        	while(num%2==0){
        		num/=2;
        	}
        }
        if(num%3==0){
        	while(num%3==0){
        		num/=3;
        	}
        }
        if(num%5==0){
        	while(num%5==0){
        		num/=5;
        	}
        }
        if(num!=1)
        	return false;
        return true;
    }
把这道简单的题AC了,是不是也给自己增加了些许信心呢?接下来,让我们再来挑战自己吧~

ugly number II  这道题让我们找到第n个丑陋数,而不是只判断一个数是否是丑陋数辣么简单了。分析:一个大的丑陋数必然是一个比它小的丑陋数乘以2,3,5得到的。1是丑陋数,那么第二个丑陋数一定是第一个丑陋数分别乘以2,3,5后最小的一个,那么第二个丑陋数是2;同理,第三个丑陋数是2*2,1*3 和1*5中最小的一个,依次类推。可以把这个过程想象为三条不断延伸的数字链,每次都是有最小丑陋数的那一条或者几条链向下延伸(每一次比较数字链上可能会出现相同的最小丑陋数)。不知道这个过程你是不是想象出来了,把我画的一个简易图贴出来吧(手画的,跟博客大神们的图没法比)



从图中可以看到,每次都是持有最小丑陋数的数字链向下更新。

思想分析完了,剩下的就是如何实现它了。我最初的想法是,开辟三个队列,每个队列就是一个不断变化的数字链,取三个队列头元素进行比较,每次都是持有最小丑陋数的队列移出头元素,把新生成的最小丑陋数分别加入三个队列尾。

public int nthUglyNumber(int n) {
        if(n==1)
			return 1;
		Queue<Integer> q1,q2,q3;
		q1=new LinkedList<Integer>();
		q2=new LinkedList<Integer>();
		q3=new LinkedList<Integer>();
		q1.add(1);
		q2.add(1);
		q3.add(1);
		
        int nth_number=0;
        int min=-1;
        for(int i=1;i<n;i++){
        	int l1=q1.peek()*2;
        	int l2=q2.peek()*3;
        	int l3=q3.peek()*5;
//        	System.out.println(l1+","+l2+","+l3);
        	if(l1<=l2){
        		if(l1<=l3){
        			min=l1;
        			q1.remove();
        			if(l1==l2)
        				q2.remove();
        			if(l1==l3)
        				q3.remove();
        		}
        		else if(l3<l1){
        			min=l3;
        			q3.remove();
        		}
        	}
        	else if(l2<=l1){
        		if(l2<=l3){
        			min=l2;
        			q2.remove();
        			if(l2==l1)
        				q1.remove();
        			if(l2==l3)
        				q3.remove();
        		}
        		else if(l3<l2){
        			min=l3;
        			q3.remove();
        		}
        	}
//        	System.out.println("min is "+min);
        	q1.add(min);
        	q2.add(min);
        	q3.add(min);
//        	System.out.println(q1);
//        	System.out.println(q2);
//        	System.out.println(q3);
        }
        nth_number=min;
        return nth_number;
    }

最初的方法虽然也可以实现我们的算法思想,但是它开辟了三个队列,使得算法的空间复杂度较高。我使用队列其实只是想要实现三条数字链的延伸操作,那么可以在最初想法之前改进方法吗?当然是可以的啦!

改进一:仔细观察上图可以发现,其实这三条链可以用一条链代替,移出队列头的这种操作可以用指针的移动代替。因此,只需要一个数组来存储丑陋数,并设立三个指针分别指示三条数字链的当前位置。

public int nthUglyNumber(int n) {//改进一算法
        int result[]=new int[n];
        int p1=0,p2=0,p3=0;
        result[0]=1;
        for(int i=1;i<n;i++){
        	int min=Math.min(result[p1]*2, Math.min(result[p2]*3, result[p3]*5));
        	result[i]=min;
        	if(min==result[p1]*2)
        		p1++;
        	if(min==result[p2]*3)
        		p2++;
        	if(min==result[p3]*5)
        		p3++;
        }
        return result[n-1];
    }

改进二:在这道题的discuss讨论区看到有小伙伴使用了PriorQueue,这种方法也是可以的哦,因为优先队列其实就是一个优先堆,队头就相当于堆顶,默认放着最小元素。有了这个数据结构我们就不用费劲吧啦的维护丑陋数的顺序了。向队列插入元素的过程就是一个调整堆的过程,所以堆顶永远都是我们想要的当前最小丑陋数。这里就不给出具体代码实现了,有兴趣的小伙伴可以尝试一下哦!


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值