您的位置:首页 > 其它

用Scala瞎写的冒泡排序算法

2011-01-19 11:24 183 查看

冒泡排序的基本概念

冒泡排序的基本概念是:依次比较相邻的两个数,将小数放在前面,大数放在后面,如此继续直到最后一个数;然后再从头开始,一直到倒数第二个数;如此循环一直到排序完成。

稍微想一下

基本概念已经很清楚了,我稍微的想了一下。如果用程序来实现的话,比较直观的方法就是用for循环。一开始时可以设置一个变量index,让index=0,也就是每次都从0开始循环,比较index和index+1的值,如果index的值大于index+1的值,就将它们互换,然后让index++,一直比较到最后两个值;一轮完成后,再重新让index为0,重新从0开始循环,依旧上面的逻辑,只不过这次只循环到倒数第二个值就好了;以此类推。

有了上面的想法,我很直观的写出下面的代码:(这里没有Scala代码框,我就用Java的代替了)

for(i<-0 to arr.length-2){
if(arr(i)>arr(i+1)){
val temp=arr(i+1)
arr(i+1)=arr(i)
arr(i)=temp
}
}
这个代码可以用来做第一次循环,那么问题来了:之后的循环怎么办?包括我该怎么控制一轮的循环以及每一轮循环中for循环的上边界,毕竟每一轮for循环的上边界都是不同的。

去掉for循环

网上最推崇的方法是用嵌套for循环的方法,自己对for循环不是很喜欢,更何况for循环嵌套for循环的做法。所以我的想法是去掉for循环。就连上面代码中的那一层也不要。当然,去掉for用while来代替等于什么也没做,因此,我只能用递归来做了。

用递归的话,我可以把上面的代码改造一下,把用来控制循环的数组索引变量i作为方法参数,这样我可以在递归调用方法时,每次把索引i加1作为参数来代替for循环。

OK,改造完就是下面的代码:

def sort(index:Int, arr:Array[Int]):Unit={
if(index == arr.length-1) return
if(arr(index)>arr(index+1)){
val temp=arr(index+1)
arr(index+1)=arr(index)
arr(index)=temp
}
sort(index+1)
}


但是我还是没有解决上面的问题:之后的循环该怎么办?

还是先重构吧

虽然我还没想明白之后的循环怎么办,不过至少上面的这个代码做第一次循环已经够了。先重构一下,把那个index的判断换成case方式:

def sort(i:Int, arr:Array[Int]):Unit= {
i match {
case last if last == arr.length-1 =>
case index => {
if(arr(index)>arr(index+1)) swap(index, index+1)
sort(index+1,arr)
}
}
def swap(index:Int, nextIndex:Int)={
val temp=arr(nextIndex)
arr(nextIndex)=arr(index)
arr(index)=temp
}
}


至少我看着舒服了,继续。

回到冒泡排序的概念上

我现在的递归每次都要到数组的最后一个元素。按照冒泡排序的概念,从第二轮开始就不需要再看最后一个元素了。这也就是说,每一轮递归下来,需要排序的元素都会少一个。这样的话,我们最后一轮递归的就只是数组的头两个元素。

这么来看,上面的两个问题就好解决了。我可以再引入一个变量lastIndex,排序开始的时候让它等于数组的最后一个索引值,每递归完一轮把lastIndex减1,这样就可以用它来告诉程序这次应该递归到哪个元素;另外还可以用它来指示我们什么时候完成排序——就是当lastIndex等于我们数组第二个元素的索引时。

由此看来,这个lastIndex最好作为实例变量,独立于方法之外,这样可以在调用方法之前初始化它,并且在每一轮递归完成时将它的值减1。

需要一个单独的类了

从上面分析来看,我应该创建一个新的类出来了。另外我还发现一个问题,sort方法的参数会很让人费解。现在我自己编写没关系,但是如果给别人用呢?作为排序算法,我们最直观的用法是这样:

sort(array)
或者

array.sort
现在我的方法第一个参数是一个不伦不类、让人费解的Int类型,这个必须得改掉。

上面的两个用法,第一个适合写一个帮助类,不过这个让我想到Java里的一大堆static方法,舍弃不要。

采用第二种方式,需要我创建一个新的类型,里面包装我的sort方法,这个简单:

case class BubbleSort(val array:Array[Int]){
private[this] var lastIndex = array.length-1
private[this] def sortFromIndex(index:Int):Unit= index match {
case last if last==lastIndex => if(lastIndex>1){
lastIndex=lastIndex-1
sortFromIndex(0)
}
case otherIndex => {
if(array(otherIndex)>array(otherIndex+1)) swap(otherIndex, otherIndex+1)
sortFromIndex(otherIndex+1)
}
}
private[this] def swap(index:Int, nextIndex:Int) = {
val temp = array(nextIndex)
array(nextIndex)=array(index)
array(index)=temp
}
def bubbleSort=sortFromIndex(0)
}
这上面做了一些改动,当程序递归到lastIndex的位置时,判断一下此时lastIndex是否在第二个元素索引上,如果在的话我们就排序完了,如果不在,就需要把lastIndex减1,并且启动新一轮的递归。

这下看着明了多了,对于使用者只要调用bubbleSort就可以。

不行,还有杂念

首先,这个类只能用来排序Int类型的数组,那么其它的类型呢?比如String数组。

另外,如果我想用这个类排序,每次还需要new一个对象,太麻烦了,我只是想排序而已。

对于第一个问题,使用泛型就好了。我们可以把类定义变成这样:

case class BubbleSort[T <% Ordered[T]](val array:Array[T])
这里使用了Scala的View Bound,这样可以让Scala的编译器把你的T看成是可以变为Ordered[T]的类型。为什么这么做呢,首先我在程序里需要比较两个T类型,而Ordered[T]提供了我需要的>方法。其次,我能够想到的需要排序的类型如Int,Long,Double等都有对应的Ordered类型下的子类型,Int对应RichInt,Long对应RichLong等。所以我在这里选择Ordered类型。

好了,第一个问题解决了,第二个问题也不复杂,只要我提供一个implicit转换就可以:

object BubbleSort{
implicit def arrToBubbleSort[T <% Ordered[T]](arr:Array[T]):BubbleSort[T]=BubbleSort(arr)
}
OK,这样就全部解决了。

杂念,玩儿蛋去吧

最后还有一个问题。比如有一个比较大的数组,程序进行一轮又一轮的递归的时候,如果在途中就排序完成了,程序就不需要等到lastIndex一直减到数组第二个元素才停,只要在这一轮的递归中没有发现调用swap方法,就说明没有乱序的排列了,此时程序就应该停止继续递归下去——因为排序完成了。

按照这个分析的话,我可以加入一个notFinish变量,一旦在这一轮递归中调用到swap方法了,notFinish就是true,否则就是false。

下面就是修改后的完整代码:

case class BubbleSort[T<%Ordered[T]](val array:Array[T]){
private[this] var lastIndex = array.length-1
private[this] var notFinish = false
private[this] def sortFromIndex(index:Int):Unit= index match {
case last if last==lastIndex => if(lastIndex>1 && notFinish){
lastIndex=lastIndex-1
notFinish=false
sortFromIndex(0)
}
case otherIndex => {
if(array(otherIndex)>array(otherIndex+1)) swap(otherIndex, otherIndex+1)
sortFromIndex(otherIndex+1)
}
}
private[this] def swap(index:Int, nextIndex:Int) = {
val temp = array(nextIndex)
array(nextIndex)=array(index)
array(index)=temp
}
def bubbleSort=sortFromIndex(0)
}
object BubbleSort{ implicit def arrToBubbleSort[T <% Ordered[T]](arr:Array[T]):BubbleSort[T]=BubbleSort(arr) }
下面是测试代码:

import src.BubbleSort._
object BubbleSortMain {
def main(args: Array[String]): Unit = {
val s = Array(2,4,5,7,9,8,6,3,1)
s.bubbleSort
s foreach println
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: