Ближайшие ключи в SortedMap
Учитывая ключ k
в SortedMap, как я могу эффективно найти самый большой ключ m
, который меньше или равен k
, а также самый маленький ключ n
, который больше или равен k
. Спасибо.
Ответы
Ответ 1
Глядя на исходный код для 2.9.0, следующий код кажется лучшим, что вы можете сделать
def getLessOrEqual[A,B](sm: SortedMap[A,B], bound: A): B = {
val key = sm.to(x).lastKey
sm(key)
}
Я не знаю точно, как работает расщепление дерева RedBlack, но я предполагаю, что это похоже на обход дерева (log n) дерева/конструкции новых элементов, а затем балансировку, предположительно также O (log n). Затем вам нужно снова спуститься по новому дереву, чтобы получить последний ключ. К сожалению, вы не можете получить значение за один проход. Поэтому вам нужно снова спуститься, чтобы получить значение.
Кроме того, lastKey
может генерировать исключение и не существует аналогичного метода, который возвращает Option
.
Я жду исправлений.
Редактировать и личный комментарий
Область SortedMap в std lib, по-видимому, немного игнорируется. Я также пропускаю измененную SortedMap. И, просматривая источники, я заметил, что некоторые важные методы отсутствуют (например, тот, который запрашивает OP, или те, которые указаны в моем ответе), а также некоторые из них имеют плохую реализацию, например "last", которая определяется TraversableLike и идет через полное дерево от первого до последнего, чтобы получить последний элемент.
Изменить 2
Теперь вопрос переформулирован, мой ответ уже недействителен (ну это было не раньше). Я думаю, вы должны сделать то, что я описываю дважды, для lessOrEqual и moreOrEqual. Ну, вы можете взять ярлык, если найдете равный элемент.
Ответ 2
Scala SortedSet
trait не имеет метода, который даст вам ближайший элемент к другому элементу.
В настоящее время он реализован с помощью TreeSet
, который основан на RedBlack
. Дерево RedBlack
не видно с помощью методов на TreeSet
, но защищенный метод tree
защищен. К сожалению, это в основном бесполезно. Вам придется переопределить методы, возвращающие TreeSet
, чтобы вернуть ваш подкласс, но большинство из них основано на newSet
, который является приватным.
Итак, в конце концов, вам придется дублировать большую часть TreeSet
. С другой стороны, это не так много кода.
Как только у вас будет доступ к RedBlack
, вам нужно будет реализовать нечто похожее на RedBlack.Tree
lookup
, поэтому у вас будет производительность O(logn)
. Это на самом деле такая же сложность range
, хотя она, безусловно, будет работать меньше.
В качестве альтернативы вы создадите застежку-молнию для дерева, чтобы вы могли фактически перемещаться по множеству в постоянное время. Конечно, было бы намного больше работать.
Ответ 3
Похоже, я должен подать билет, чтобы добавить методы fromIterator и toIterator к признаку 'Sorted'.
Ответ 4
Ну, один вариант, безусловно, использует java.util.TreeMap
.
У него есть методы lowerKey
и higherKey
, которые действительно делают то, что вы хотите.
Ответ 5
К сожалению, библиотека Scala позволяет эффективно выполнять этот тип запроса:
а также наименьший ключ n
, который больше или равен k
.
val n = TreeMap(...).keyIteratorFrom(k).next
Вы можете взломать это, сохранив две структуры: одну с обычными клавишами и одну с отрицательными клавишами. Затем вы можете использовать другую структуру для создания второго типа запроса.
val n = - TreeMap(...).keyIteratorFrom(-k).next
Ответ 6
Используя Scala 2.11.7, следующее даст то, что вы хотите:
scala> val set = SortedSet('a', 'f', 'j', 'z')
set: scala.collection.SortedSet[Char] = TreeSet(a, f, j, z)
scala> val beforeH = set.to('h').last
beforeH: Char = f
scala> val afterH = set.from('h').head
afterH: Char = j
Как правило, вы должны использовать lastOption
и headOption
, поскольку указанные элементы могут не существовать. Если вы хотите сжать немного больше эффективности, вы можете попробовать заменить from(...).head
на keysIteratorFrom(...).head
Ответ 7
У меня была аналогичная проблема: я хотел найти ближайший элемент к заданному ключу в SortedMap. Я помню ответ на этот вопрос: "Вы должны взломать TreeSet", поэтому, когда мне пришлось реализовать его для проекта, я нашел способ обернуть TreeSet, не входя в его внутренности.
Я не видел ответ jazmit, который более точно отвечает на исходный вопрос с минимальным шумом (два вызова метода). Тем не менее, эти вызовы методов делают больше работы, чем необходимо для этого приложения (несколько обходов дерева), и мое решение обеспечивает множество перехватов, когда другие пользователи могут модифицировать его в соответствии с их собственными потребностями.
Вот он:
import scala.collection.immutable.TreeSet
import scala.collection.SortedMap
// generalize the idea of an Ordering to metric sets
trait MetricOrdering[T] extends Ordering[T] {
def distance(x: T, y: T): Double
def compare(x: T, y: T) = {
val d = distance(x, y)
if (d > 0.0) 1
else if (d < 0.0) -1
else 0
}
}
class MetricSortedMap[A, B]
(elems: (A, B)*)
(implicit val ordering: MetricOrdering[A])
extends SortedMap[A, B] {
// while TreeSet searches for an element, keep track of the best it finds
// with *thread-safe* mutable state, of course
private val best = new java.lang.ThreadLocal[(Double, A, B)]
best.set((-1.0, null.asInstanceOf[A], null.asInstanceOf[B]))
private val ord = new MetricOrdering[(A, B)] {
def distance(x: (A, B), y: (A, B)) = {
val diff = ordering.distance(x._1, y._1)
val absdiff = Math.abs(diff)
// the "to" position is a key-null pair; the object of interest
// is the other one
if (absdiff < best.get._1)
(x, y) match {
// in practice, TreeSet always picks this first case, but that's
// insider knowledge
case ((to, null), (pos, obj)) =>
best.set((absdiff, pos, obj))
case ((pos, obj), (to, null)) =>
best.set((absdiff, pos, obj))
case _ =>
}
diff
}
}
// use a TreeSet as a backing (not TreeMap because we need to get
// the whole pair back when we query it)
private val treeSet = TreeSet[(A, B)](elems: _*)(ord)
// find the closest key and return:
// (distance to key, the key, its associated value)
def closest(to: A): (Double, A, B) = {
treeSet.headOption match {
case Some((pos, obj)) =>
best.set((ordering.distance(to, pos), pos, obj))
case None =>
throw new java.util.NoSuchElementException(
"SortedMap has no elements, and hence no closest element")
}
treeSet((to, null.asInstanceOf[B])) // called for side effects
best.get
}
// satisfy the contract (or throw UnsupportedOperationException)
def +[B1 >: B](kv: (A, B1)): SortedMap[A, B1] =
new MetricSortedMap[A, B](
elems :+ (kv._1, kv._2.asInstanceOf[B]): _*)
def -(key: A): SortedMap[A, B] =
new MetricSortedMap[A, B](elems.filter(_._1 != key): _*)
def get(key: A): Option[B] = treeSet.find(_._1 == key).map(_._2)
def iterator: Iterator[(A, B)] = treeSet.iterator
def rangeImpl(from: Option[A], until: Option[A]): SortedMap[A, B] =
new MetricSortedMap[A, B](treeSet.rangeImpl(
from.map((_, null.asInstanceOf[B])),
until.map((_, null.asInstanceOf[B]))).toSeq: _*)
}
// test it with A = Double
implicit val doubleOrdering =
new MetricOrdering[Double] {
def distance(x: Double, y: Double) = x - y
}
// and B = String
val stuff = new MetricSortedMap[Double, String](
3.3 -> "three",
1.1 -> "one",
5.5 -> "five",
4.4 -> "four",
2.2 -> "two")
println(stuff.iterator.toList)
println(stuff.closest(1.5))
println(stuff.closest(1000))
println(stuff.closest(-1000))
println(stuff.closest(3.3))
println(stuff.closest(3.4))
println(stuff.closest(3.2))
Ответ 8
Я делаю:
val m = SortedMap(myMap.toSeq:_*)
val offsetMap = (m.toSeq zip m.keys.toSeq.drop(1)).map {
case ( (k,v),newKey) => (newKey,v)
}.toMap
Когда я хочу, чтобы результаты моей карты были отключены одним ключом. Я также ищу лучший способ, желательно, не сохраняя дополнительную карту.