我刚刚学习Scala,我正在尝试实现一些算法和数据结构。
我写了一些代码,旨在转换线性二进制堆中的Vector。例如:
Vector(8,3,4,6,2,5,7,9)
已转换为Vector(9,8,7,6,2,4,5,3)
通过这种方式,给定索引i
,其父级位于:(i-1)/2
或(i-2)/2
,具体取决于i
是奇数还是对。
我将代码留在这里,我正在寻找的是关于如何改进我的实现的一些建议。或者甚至在另一个完全不同的方向尝试。
您可以使用以下内容:new Heap(Vector(8,3,4,6,2,5,7,9))
class Heap(vs: Vector[Int]) {
val heap = build()
private def build():Vector[Int] = {
((1 until vs.length) foldLeft Vector[Int](vs.head)) ( (accu, idx) =>
fixFrom(accu :+ vs(idx), idx) )
}
private def fixFrom(heapToFix:Vector[Int], idx: Int): Vector[Int] = {
val parentIndex = parent(idx)
if(parentIndex == -1 || heapToFix(idx) <= heapToFix(parentIndex)) heapToFix
else {
val nextToFix = (heapToFix.updated(parentIndex, heapToFix(idx))) take idx
val fixed = fixFrom(nextToFix, parentIndex)
val swap = heapToFix.updated(idx, heapToFix(parentIndex))
fixed ++ (swap drop idx)
}
}
def children(parentIndex: Int) =
(valid(2*parentIndex + 1), valid(2*parentIndex + 2))
def parent(childIndex: Int) =
if(childIndex % 2 == 0) valid((childIndex-2)/2)
else valid((childIndex-1)/2)
def valid(idx:Int) =
if(idx >= 0 && idx < vs.length) idx else -1
override def toString = heap mkString " "
}
更新1:根据以下建议,我做了一些更改:
import math.Ordering.Implicits._
class Heap[T: Ordering](vs: Vector[T]) {
val heap = build()
private def build():Vector[T] =
((0 until vs.length) foldLeft Vector.empty[T]) ( (accu, idx) =>
fixUp(accu :+ vs(idx), idx) )
@annotation.tailrec
private def fixUp(h:Vector[T], idx: Int): Vector[T] = {
val parentIdx = parent(idx)
if(parentIdx < 0 || h(idx) <= h(parentIdx)) h
else fixUp(h.updated(parentIdx, h(idx)).updated(idx, h(parentIdx)), parentIdx)
}
def parent(idx: Int) = (idx-1) >> 1
override def toString = heap mkString " "
}
答案 0 :(得分:4)
import scala.math.Ordering.Implicits._
def insert[T : Ordering](heap: Vector[T], newItem: T) = {
@annotation.tailrec
def siftUp(h: Vector[T], idx: Int):Vector[T] = {
val parentIdx = (idx - 1) >> 1
if(parentIdx < 0 || h(parentIdx) > h(idx)) h
else siftUp(h.updated(parentIdx, h(idx)).updated(idx, h(parentIdx)), parentIdx)
}
siftUp(heap :+ newItem, heap.length)
}
def heapify[T: Ordering](vs: Vector[T]) = vs.foldLeft(Vector.empty[T])(insert)
assert(heapify(Vector(8, 3, 4, 6, 2, 5, 7, 9)) == Vector(9, 8, 7, 6, 2, 4, 5, 3))
答案 1 :(得分:1)
矢量不平坦。它本身就是一个链表。它有一棵1:32树,即每个节点有32个孩子。并按顺序填写它们。
由于您正在实现二进制堆,我们知道它是一个平衡树。而且我们也知道当你实现插入和删除时,树会发生一些变化。树也会增加和减少。
考虑到上述事实,我建议使用一个可变数组对象作为主数据类型:
var arr = Array[Int]()
通过将其声明为var,它将更适合于大小增加和减少时。
但是,如果你的想法只是实现一个不可变的二进制堆,也许你不需要声明它'var',但我仍然建议使用Array [Int]而不是Vector [Int],因为你没有在实现堆时需要链表。
UPDATE(2012年12月1日):想想我会尝试使用数组实现这种方式,几个小时后,我就开始工作了。花了很长时间,并有一大堆Scala概念:
<T extends Comparable<? super T>>
Option
,None
和Some
代替null
值可能还有更多。它仍然有一些改进的余地,如:
compare
函数但我想我会在这里完成它,除非有人想要添加更多内容:
package com.test
import Ordering.Implicits._
/**
* Pass 'a' to sort ascending or 'd' to sort descending
*/
class BinaryHeap[T <% Ordered[T]: Manifest](sortingOrder: Char) {
def this() = this('d')//Default will be descending
var arr: Array[Option[T]] = Array.empty[Option[T]]
var num: Int = 0
def doCompare = {
if (sortingOrder == 'a')
(idx1: Int, idx2: Int) => arr(idx1).get > arr(idx2).get
else
(idx1: Int, idx2: Int) => arr(idx1).get < arr(idx2).get
}
def size: Int = num
def add(t: T): Unit = {
resizeIfRequired
arr(num) = Some(t)
swim(num)
num = num + 1
}
def remove: T = {
if (num > 0) {
val ret = arr(0)
num = num - 1
swap(0, num)
arr(num) = None: Option[T]
sink(0)
ret.get
} else {
throw new Exception("Tried to remove from an empty heap")
}
}
private def resizeIfRequired: Unit = {
if (arr.length == 0)
arr = Array.fill(1)(None: Option[T])
else if (num == arr.length) {
doResize(num * 2)
} else if (num == arr.length / 2 - 1) {
doResize(arr.length / 2)
}
}
private def doResize(newSize: Int): Unit = {
var newArr = Array.fill(newSize)(None: Option[T])
Array.copy(arr, 0, newArr, 0, num)
arr = newArr
}
private def swim(idx: Int): Unit = {
val parentIdx = getParent(idx)
if (doCompare(parentIdx, idx)) {
swap(parentIdx, idx)
swim(parentIdx)
}
}
private def swap(idx1: Int, idx2: Int) = {
val temp = arr(idx1)
arr(idx1) = arr(idx2)
arr(idx2) = temp
}
private def sink(idx: Int): Unit = {
val leftChildIdx = getLeftChild(idx)
val rightChildIdx = getRightChild(idx)
if ((isValid(leftChildIdx)) && (doCompare(leftChildIdx, idx))) {
swap(leftChildIdx, idx)
sink(leftChildIdx)
} else if ((isValid(rightChildIdx)) && (doCompare(rightChildIdx, idx))) {
swap(rightChildIdx, idx)
sink(rightChildIdx)
}
}
private def isValid(idx: Int): Boolean = {
idx < num
}
private def getParent(idx: Int): Int = {
idx / 2
}
private def getLeftChild(idx: Int): Int = {
2 * idx + 1
}
private def getRightChild(idx: Int): Int = {
2 * idx + 2
}
def printOrdered: Unit = {
if (num == 0) {
println("Heap is empty")
} else {
(0 until num) map (x => println(arr(x).get))
}
}
}