Given a list of non negative integers, arrange them such that they form the largest number.
For example, given [3, 30, 34, 5, 9], the largest formed number is 9534330.
Note: The result may be very large, so you need to return a string instead of an integer.
s思路:
1. 正如我所理解。需要排序。例如:[3, 30, 34, 5, 9],先把所有数变成string,然后排序:从左边起逐位比较,如果相等,则都移动到右边一位比较,这都很平常。有趣的一点是,3和30比较,第0位同为3,相等,但是3只有一位不能再往右移动了,此时就不移动,而只移动30的指针到0,比较3和0。也就是说移动受到边界的限制,只是这里处理边界的方法是停留在边界处,并取边界值比较。再比如:3和34,那3就会和4比较,发现4>3,所以34比3大,因为34如果放在3前面组成343比34放在3后面组成的334大。(删除的部分不正确)
2. debug了好久,重新来说。有很多case,例如:3和34比较,可以认为3这个数停在这个位置上了,但是这么特殊的一个例子,3只有一位,怎么看的出来规律呢,所以问题是被简化了,显然自己没看到更general的case。例如,824和8247,当824的指针移动到4时,下一刻应该去向何处?应该把8247的7和824的8比较才对呀,我马上结论说那就是到了结尾后下一步就再取开头的数比较。再例如,323和3233比较,当走到323结尾的3时,下一刻如果回到开头的3,那么3233也是3,难道这两个数怎么放都可以吗?显然323+3233<3233+323,所以还是不对,不过结合上一次的教训,应该不用急着全盘否定这种做法,可能我们离正确的做法又近了!既然323遍历到最右边数之后还要回到最左边数继续比较,那么3233也可以啊,当我们3233到了最右边的3,下一步不是跳出循环,而是继续回到最左边数,所以:3233的第一个数3和323的第二个数2比较,这不,马上就可以看出大小!到这里,自然想到,如果是333和3333比较,结果肯定是相等,所以两个数循环多少次都不可能不相等,因此必须又一个stopping rule,那就是最长的数的长度的两倍(后来又进一步缩小范围,stopping条件是循环次数是两个数的长度之和)即:3333循环两次还是相等,那就真的相等,这时候就需要跳出while。
3. 这里思维的进步,需要单独提一下。昨天 Leetcode 165. Compare Version Numbers的时候,就把比较放在while内和while外到处都是,一点没打包严实,不光思路不清,debug也不易,最后把比较功能都想办法塞进while内,世界一下美好了!今天刚开始也出现过,没多想就把比较不等的放在while内,比较相等的又放在while外。不过昨天的记录,今天脑子马上就冒出一个声音说要想办法把两部分的代码都塞进while里面,果然就简单很多,而且最后beat 96%.
4. 补充一点,题做多了,和以前僵硬的思路比,现在思维更灵活了,思维的训练和肌肉的训练很相似,或者说这两者本就没什么差别,尤其是自己尝试每天去捕捉思维的形状。越是去观察思维的样子,思维反倒容易放松,也就越容易reshape,而那些批评自我羡慕别人并不能让自己思维变化,反而徒增烦恼。就拿这道题说,刚开始有很大一部分是连蒙带猜的,想到两个指针到结尾时仍相等怎么办时,就思维几乎停在这个问题,没有自然而然或努力尝试去深入思考这个问题,而是找了两个特殊的简单的例子观察得出一个简化的方法,没有搞清楚为什么。整个思维过程就是这样子,后来debug把复杂的case拿出来一研究才发现两个指针应该循环往复,问题一下就有趣了些。
5. 这道题反映的思维的短板在哪儿呢?还要说两个指针到尾巴后如何处理的事,自己给自己的选项就是试图简化答案,比如就停在尾巴,或回到开头停下。试图让指针停在某一个具体的位置,本身这个答案就没有新意,停在一个地方,而不动起来,说明思维或者自己潜意识在图省事,怕麻烦,都不愿意让指针动起来。指针不动,那脑子当然也动不了,这就expose思维僵化的一瞬间。当然,最后能打破思维的静态和惯性,让指针继续循环动起来,说明脑子里本来就有动的不僵化的一面,只是被这个强大的僵化的思维给覆盖了!
6. 看了网上答案,这个题还有一个代码和理解起来都很容易的做法:如何判断a和b谁应该放在前面的问题?比较a+b和b+a看谁更大,这个比较直接用string的比较就可以了,因此最后还是一个简洁的数学题,不过这个方法每次都要concatenate两个string,比较费时间!
7. 再思考一下,这个数学的方法是怎么想到的?自己想到的方法是深入细节,挑战各路extreme case,而且最终也在各路细节中摸爬滚打出一条正确的方式—循环指针,一言以蔽之,自己的方法是bottom-up,希望从底层往上解决问题;而利用数学的方法,思考就不是从细节入手了,而从系统出发,从顶层设计入手,是一种top-down的方法。两种方法各有优势,不必厚此薄彼。数学的方法,是一眼能看到10里的人容易想出来的;从底层入手的,对抗各路limit case的适合一眼能看到1里的人容易想到了,属于不同世界的人看世界的角度。也不是说世界不同,是处在世界的位置不同,一眼能看10里,那是因为别人站在数学的高度看了,所以眼里就是公式,如何用公式把世界给完美的模拟;一眼看1里,没有数学这栋高楼,就只能泥地里打滚,combat艰难的各路case。多从数学的角度出发,尤其是发现目前脑袋里冒出来的方法要面临很多琐碎的extreme case时!
//方法1:循环比较大小
class Solution {
public:
string largestNumber(vector<int>& nums) {
vector<string> ss(nums.size());//bug:全零就不用转换了,直接返回0.
string res;
int zero=0;
for(int i=0;i<nums.size();i++){
zero+=(nums[i]==0);
ss[i]=to_string(nums[i]);
}
if(zero==nums.size()) return "0";
sort(ss.begin(),ss.end(),[](string&a,string&b){
int n1=a.size(),n2=b.size();
int i=0;
/*while(i<max(n1,n2)){//不正确的编码
char c1,c2;
if(i<n1) c1=a[i];
//else c1=a[n1-1];//bug1
//else c1=a[0];//bug2
else c1=max(a[0],a[n1-1]);
if(i<n2) c2=b[i];
else c2=max(b[0],b[n2-1]);
if(c1>c2) return true;
if(c1<c2) return false;
i++;
}*/
int n=n1+n2;
while(i<n){
char c1,c2;
c1=a[i%n1];
c2=b[i%n2];;
if(c1>c2) return true;
if(c1<c2) return false;
i++;
}
return true;
});
for(string str:ss){
res+=str;
}
return res;
}
};
//方法2:简洁明了,用数学之美成就代码之美!
class Solution {
public:
string largestNumber(vector<int>& nums) {
vector<string> ss(nums.size());//bug:全零就不用转换了,直接返回0.
string res;
for(int i=0;i<nums.size();i++){
ss[i]=to_string(nums[i]);
}
sort(ss.begin(),ss.end(),[](string&a,string&b){
return a+b>b+a;
});
for(string str:ss){
res+=str;
}
return res[0]=='0'?"0":res;
}
};