像这样简单的算法前人工程师确定想到过,即便你是初学者,若是偶然发现了这个也并不稀罕,我也没有打算效仿郭小四窃他人成果自居,说到底这只是一篇系列扯淡文,它真正有价值的部分是由此延伸出来的问题。 html
在归并算法中,合并两个数列须要消耗m+n的空间,排序好了后还要将队列拷回。在个人版本的算法中,可一开始就申请一块等长内存,而后顺次的写入数据,而不须要写回,一遍写完后,交换两块内存,继续写。这样减小了一半的写开销,避免了重复申请空间的问题。 算法
交换次数决定了排序完成时正确的数据写入到了哪块内存中。 数组
当交换次数为偶数时,写入原内存。 spa
当交换次数为奇数时,写入临时内存。 code
若是交换次数为奇数,那么还要将数据拷贝至原内存。不过,在两两交换时,不须要额外的内存,由于比较数据只有一个,简单交换就好了,奇数次时先进行一次比较,接下来就仍然是偶数次的了。所以在算法的主要部分前面才有了这段代码。 htm
int temp = len,i = 0,size = 1; int l1p,l1end,l2p,l2end; int *templist,*listb; printarr(list,len); if(len<=1){ return; } i = 0; //calculate swaptimes while(temp){ temp = temp>>1; i++; } if(i%2){ for(i=0;(i+1)<len;i+=2){ if(list[i]>list[i+1]){ temp = list[i]; list[i] = list[i+1]; list[i+1] = temp; } } size = 2; printarr(list,len); }
这段代码虽然完成了任务,可是有处地方特么让人不爽。就是在算交换次数的地方,用了一个O(n)的循环来得到次数。感受有点不效率。 排序
事实上咱们只在意长度数量的最高1位在哪,由于那一位才决定交换次数。 队列
因而开始寻找有没有更快的方法。在英文中这个问题叫Find the log base 2 of an integer,这里有一个斯坦福的页面讲这个问题 http://www-graphics.stanford.edu/~seander/bithacks.html#IntegerLogObvious 内存
很遗憾的是,关于此问题,目前最快解也只有logN,基于二分查找法。可是把N降为logN,对于数位长度来讲,真有点小题大作,仍然仍是想找一个O(1)的方法才有意义。 get
之因此有这种想法,由于在位操做的世界里,有些算法非常销魂。
好比
x&(x-1)
可将X最右边起的连续的0填充为1
x&(-x)
可单独抽出最右边的1
等等。
因此老是以为,我们的问题,也是能用O(1)解决的。
可是这些简单好用的魔法彷佛都只和数的右侧沾边,由于操纵右边的位数,能够用确切知道的数,好比1 或FFFFFF,而想像操做右边位同样操做左边位就不行了,除非你知道该数的log base 2 interger是什么……若是咱们有办法填充最左边的0,就能得解,但这个看似很简单的问题,竟然是个至今都无解的题,除非新版cpu出了求专门问题对应的指令,要想在低于logN的步骤内解决这个彷佛不可能了。
不过对咱们的应用来讲,这还没到头,由于最终的目的是要知道该位是在奇数位上仍是在偶数位上。所以问题转换为:
把数组长度-1,若是最左边的1落在偶数位上,则须要偶数次交换,若是落在奇数位上则须要奇数次交换
这样一来问题彷佛很好解决了,这是改写后的代码
int temp = len-1,i = 0,size = 1; int l1p,l1end,l2p,l2end; int *templist,*listb; printarr(list,len); if(len<=1){ return; } //calculate swaptimes,only if highest bit in odd place if((temp&0xaaaaaaaa)<(temp&0x55555555)){ for(i=0;(i+1)<len;i+=2){ if(list[i]>list[i+1]){ temp = list[i]; list[i] = list[i+1]; list[i+1] = temp; } } size = 2; printarr(list,len); }