在scala中插入排序实现

时间:2012-05-02 00:09:04

标签: scala insertion-sort

我正在尝试使用Scala,我想看看如何在scala中实现插入排序,并满足以下要求:

  1. 嵌套for循环
  2. 输入数组[Int]
  3. 如果可能的话,通过引用方式修改调用中函数内容的方法,否则返回一个数组[Int]
  4. 如果这不是实现插入排序的Scala方法,您仍然可以提供上述代码并解释该方法的问题。 编辑: 这是尝试使用while循环(doest work)而不是它不是作业问题,为什么会有敌意?

    def insert_sort(a:Array[Int]):Array[Int]={
    for(i <- 0 until a.length)
    {
      var j=i+1
    
      while(j>1&&a(j)<a(j-1)&&j<a.length)
      {
          var c=a(j)
          a(j)=a(j-1)
          a(j-1)=c
          j-=1
      }
    }
    return a
    }
    

12 个答案:

答案 0 :(得分:18)

这就是我提出的,它似乎是功能性的,通用的,尾递归的(foldLeft是尾递归的)......:

def insertionSort[A](la: List[A])(implicit ord: Ordering[A]): List[A] = {
  def insert(la: List[A], a: A) = {
    val (h, t) = la.span(ord.lt(_, a))
    h ::: (a :: t)
  }

  la.foldLeft(List[A]()) {(acc, a) => insert(acc, a)}
}

答案 1 :(得分:4)

我的插入排序程序版本如下。它由两个函数isortinsert组成,它们具有以下签名:

  • isort(xs: List[int]) : List[int]:获取整数输入列表并输出整数列表。
  • insert(x: Int, xs: List[int]) : List[int]获取已排序的输入列表和整数x,并将x添加到列表中的正确位置,以维护列表已排序的不变量。示例:insert(3, [1, 2]) = [1, 2, 3]

首先让我们编写insert函数的代码。

  1. 如果列表为空,则返回要添加的整数列表。
  2. 否则,有两种可能的情况。列表的头部大于或等于给定的输入整数,在这种情况下,我们只需将输入整数附加到列表的开头并返回它。另一种情况是输入整数大于输入列表的头部。在这种情况下,我们递归地将输入整数插入到列表的尾部。

    def insert(x : Int, xs : List[Int]) : List[Int] = {
    
    xs match{
        case Nil => List(x)
        case y :: xs1 =>
            if(y >= x) x :: xs
            else y :: insert(x,  xs1)
     }
    
    }
    
  3. 第二个函数是isort,它接受​​一个输入列表并对其进行排序。算法如下:

    1. 如果输入列表等于Nil,请返回Nil
    2. 否则,递归排序列表的尾部,并将头部添加到(已排序)尾部的正确位置(使用insert过程)

       def isort(xs : List[Int]) : List[Int] = {
        xs match{
          case Nil => Nil
          case x :: xs1 => insert(x, isort(xs1))
      
         }
      
       } 
      
    3. 我们可以将其测试为isort(List(1, 6, 4, -2, 5, 12)),并获得正确的预期输出。

答案 2 :(得分:4)

到目前为止我见过的最优雅的插入排序算法实现:

val rand = List(5,3,1,2)

def isort(xs: List[Int]): List[Int] =
  if (xs.isEmpty) Nil
  else insert(xs.head, isort(xs.tail))

def insert(x: Int, xs: List[Int]): List[Int] =
  if (xs.isEmpty || x <= xs.head) x :: xs
  else xs.head :: insert(x, xs.tail)

isort(rand) // Produces List(1,2,3,5)

算法实现取自这本伟大的书:https://www.artima.com/shop/programming_in_scala_3ed

答案 3 :(得分:3)

嵌套for循环可能不是Scala中的答案,无论问题是什么。它们在for循环代表“重复直到条件,改变每次迭代”的语言中是有意义的,这是循环或Scala中的理解是什么(理解是for循环产生的每次迭代的东西。)

在Scala中,for循环和for comprehension是对集合元素的迭代(或非集合monad的更奇怪的东西),但是对于插入排序,你想找到一个位置,要么交换放置到该位置,或将元素插入该位置。不应该使用for循环或理解来查找内容。

此外,Scala中没有pass-by-reference。而且,事实上,Array甚至不是Scala集合,而是具有JVM授予它的独特特性的Java,它不能用类重现,所以它不能被替换。

答案 4 :(得分:3)

这是插入排序的另一个通用版本:

  def less[T <: Comparable[T]](i: T, j: T) = i.compareTo(j) < 0

  def swap[T](xs: Array[T], i: Int, j: Int) { val tmp = xs(i); xs(i) = xs(j); xs(j) = tmp }

  def insertSort[T <: Comparable[T]](xs: Array[T]) {
    for { i <- 1 to xs.size
          j <- List.range(1, i).reverse
          if less(xs(j),xs(j - 1)) } swap(xs, j, j -1)
  }

答案 5 :(得分:2)

你需要什么?
如果你只想要一个排序数组我更喜欢:def sortArray(a: Array[Int]): Array[Int] = a.sortWith(_ < _)

答案 6 :(得分:1)

一开始不要忘记添加:

import scala.util.control.Breaks._

这是我的解决方案,使用列表作为输入

def insertionSortLoop(list :List[Int]) : List[Int] = {
      val intArray: Array[Int] = list.toArray
      for ( j <- 1 until intArray.length ) {
          breakable {
            for ( i <- (1 to j).reverse ) {
              if (intArray(i-1) < intArray(i)) {
                break
              } else {
                  val temp = intArray(i)
                  intArray(i) = intArray(i-1)
                  intArray(i-1) = temp
              }
            }
        }
      }
      intArray.toList
    }

我在github处有实施。

答案 7 :(得分:1)

这是使用foldLeft编写的代码示例。

| ORD_ID | MODEL_NAME | GENERATION | BEGIN_YEAR | END_YEAR |
|--------|------------|------------|------------|----------|
|     11 |   CORVETTE |          7 |       2014 |     2016 |
|     12 |   CORVETTE |          7 |       2014 |     2016 |
|     13 |   CORVETTE |          7 |       2014 |     2016 |
|     14 |   CORVETTE |          7 |       2014 |     2016 |
|     15 |    MUSTANG |          1 |       1964 |     1973 |

此示例可在此github回购邮件中找到。

答案 8 :(得分:0)

wikipedia article中给出的实施符合您的要求。在这里,它被复制,粘贴并转换为Scala语法:

def insertionSort(a: Array[Int]): Array[Int] = {
  for (i <- 1 until a.length) {
    // A[ i ] is added in the sorted sequence A[0, .. i-1]
    // save A[i] to make a hole at index iHole
    val item = a(i)
    var iHole = i
    // keep moving the hole to next smaller index until A[iHole - 1] is <= item
    while (iHole > 0 && a(iHole - 1) > item) {
      // move hole to next smaller index
      a(iHole) = a(iHole - 1)
      iHole = iHole - 1
    }
    // put item in the hole
    a(iHole) = item
  }
  a
}

答案 9 :(得分:0)

即使在编写Scala时,我习惯于更喜欢函数式编程风格(通过组合器或递归)而不是命令式风格(通过变量和迭代),这个时间,对于这个特定问题,旧学校命令式嵌套循环导致更简单的读者代码。我不认为回退到命令式样式是某些类问题的错误(例如排序算法通常会改变作为输入传递的项目的缓冲区而不是导致新的排序集合)

这是我的解决方案:

package bitspoke.algo

import scala.math.Ordered
import scala.collection.mutable.Buffer

abstract class Sorter[T <% Ordered[T]] {

  // algorithm provided by subclasses
  def sort(buffer : Buffer[T]) : Unit

  // check if the buffer is sorted
  def sorted(buffer : Buffer[T]) = buffer.isEmpty || buffer.view.zip(buffer.tail).forall { t => t._2 > t._1 }

  // swap elements in buffer
  def swap(buffer : Buffer[T], i:Int, j:Int) {
    val temp = buffer(i)
    buffer(i) = buffer(j)
    buffer(j) = temp
  }
}

class InsertionSorter[T <% Ordered[T]] extends Sorter[T] {
  def sort(buffer : Buffer[T]) : Unit = {
    for {
      i <-  1 until buffer.length
      j <-  i until 0 by -1
      if (buffer(j) < buffer(j - 1))
    }
    swap(buffer, j, j - 1)
  }
}

正如您所看到的,我不是使用java.lang.Comparable,而是首选scala.math.Ordered和Scala View Bounds而不是Upper Bounds。这肯定是有效的,这要归功于Rich Wrappers的许多原始类型的Scala隐式转换。

您可以按如下方式编写客户端程序:

import bitspoke.algo._
import scala.collection.mutable._

val sorter = new InsertionSorter[Int]
val buffer = ArrayBuffer(3, 0, 4, 2, 1)
sorter.sort(buffer)

assert(sorter.sorted(buffer))

答案 10 :(得分:0)

Github link to Scala implementation

伪代码:

Algorithm Insertion-Sort-Descending(A)
    for j <- 2 to length[A]
        key <- A[j]                 // element we wish to insert
        i <- j - 1                  // position to the left side
        while i > 0 and A[i] < key  // while has not found correct position <-- difference between Ascending and Descending
            A[i + i] = A[i]         // right shift element
            i = i - 1               // decrement i
        A[i + 1] = key              // found position, place key

答案 11 :(得分:0)

adijo@RB_ 的答案大部分是正确的,因为您想要插入排序的不可变实现。

然而,对于大于 50.000 个条目的列表,这些实现会因 StackOverflow 错误而炸毁堆栈。这是因为那里使用的递归方法不是@tailrec。

一种可能的解决方案是使用 CPS 使方法tailrec,如下:

object Sort {
    def apply[A: Ordering](list: List[A]): List[A] = {
    
        @tailrec
        // In order to make this code tailrec, we are using CPS (continuation passing style)
        def insert(entry: A, list: List[A])(k: List[A] => List[A]): List[A] =
          list match {
            case Nil                       => k(List(entry))
            case head :: _ if entry < head => k(entry :: list)
            case head :: tail              => insert(entry, tail)(r => k(head :: r))
          }

        @tailrec
        def inner(sorted: List[A], rest: List[A]): List[A] =
          rest match {
            case Nil          => sorted
            case head :: tail => inner(insert(head, sorted)(identity), tail)
          }

        inner(Nil, list)
    }
}

值得注意的是,不可变实现总是比可变实现慢。

从我制作的 benchmark results 来看,使用可变实现,插入排序的执行速度大约快 2.5 倍。