gpt4 book ai didi

algorithm - 使用 O(n) 存储和 O(log n) 查询时间的什么数据结构应该用于范围最小查询?

转载 作者:塔克拉玛干 更新时间:2023-11-03 02:17:09 25 4
gpt4 key购买 nike

我被算法课的以下家庭作业问题难住了:

Suppose that we are given a sequence of n values x1, x2 ... xn, and seek to quickly answer repeated queries of the form: given i and j, find the smallest value in xi... xj

Design a data structure that uses O(n) space and answers queries in O(log n) time.

首先,我不确定 sequence 是指排序集还是未排序集 - 但由于它没有另外说明,我会假设 sequence表示未排序。

所以,我意识到如果我们谈论 O(log N) 查找时间,这显然必须涉及二叉树。所以基本上,我想,你有一个集合 S,你将 S 中的每个元素插入到一个二叉树中。问题是,这个问题基本上是希望我想出一种方法来回答查询,在该查询中我得到了一个未排序集合中的索引范围 - 然后确定 O 中该范围内的最低值(log N) 时间。这怎么可能?即使集合中的每个数字都被插入到树中,我能做的最好的事情就是在 O(log N) 时间内查找任何特定数字。这不允许我在 S 中的未排序数字范围内找到最小值。

有什么建议吗?

最佳答案

如果集合已排序,则不需要树。 [i,j] 范围内的最小元素的索引为 i。

因此,假设您的序列中的元素是按照它们在树叶中的索引顺序存储的。您能否在每个内部节点存储任何附加信息(啊哈,也许是某种最小值和最大值)以方便您的查询?

如果是这样,那么如果树是平衡的,并且如果您可以仅通过查看从根到 {i,j} 处的两个元素的两条路径来回答您的查询,那么您将实现 O(log N ) 查找成本。由于具有 N 个叶子的平衡二叉树包含 (2N-1) 个节点,因此您也将满足 O(N) 存储限制。


更多细节:考虑计算范围 [i,j] 中的最小值。

在树的每个内部节点 A 处,保持其下方所有叶子的最小值。这可以在首次构建树时自下而上计算。

现在从叶子 i 开始。沿着树往上走,将 i 处的值或任何已知位于 i 右侧和 j 左侧的值作为候选最小值。在 i 和 j 的共同祖先下停止一个节点。

从叶子 j 重新开始。沿着树向上走,再次将 j 处的值或任何已知位于 j 左侧和 i 右侧的值作为候选最小值。

[i,j] 的最小值就是您计算的两个值中的最小值。计算最大值是类似的。总存储要求是每个内部节点 2 个值加上每个内部节点两个指针加上每个叶子一个值,对于一棵完整的树来说是 N + 4(N-1)。

从叶子 i 开始向上树的路径与搜索叶子 i 时向下树的路径相同。


用于搜索的 C# 代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace RangeSearch
{
public class RangeSearch
{
int[] tree;
int N;

int LeafLocation(int leafNumber) { return leafNumber + N - 1; }
int LeafValue(int leafNumber) { return tree[ LeafLocation(leafNumber)]; }
int LeftChild(int x) { return 2*x + 1; }
int RightChild(int x) { return 2*x + 2; }
int Parent(int x) { return (x-1)/2; }
bool IsPowerOf2(int x) { while (x > 0) { if (x == 1) return true; if ((x & 1) == 1 ) return false; x = x >> 1; } return false; }
bool IsAncestorOf( int x, int y ) { if( x>y ) return false; return x==y || IsAncestorOf(LeftChild(x), y) || IsAncestorOf(RightChild(x),y); } // note: violating time bound for legibility, can fix by storing min/max descendant index at each node

public RangeSearch(params int[] vals)
{
if (!IsPowerOf2(vals.Length))
throw new ArgumentException("this implementation restricted to N being power of 2");
N = vals.Length;
tree = new int[2 * N - 1];
// the right half of the array contains the leaves
vals.CopyTo(tree, N - 1);
// the left half of the array contains the interior nodes, each of which holds the minimum of all its children
for (int i = N - 2; i >= 0; i--)
tree[i] = Math.Min(tree[LeftChild(i)], tree[RightChild(i)]);
}

public int FindMin(int a, int b)
{
if( a>b )
throw new ArgumentException( "FindMin expects a range [a,b] with a<=b" );
int x = Walk( a, true, b);
int y = Walk( b, false, a);
return Math.Min(x, y);
}

int Walk( int leafNumber, bool leftSide, int otherLeafNumber )
{
int minSoFar = LeafValue(leafNumber);
int leafLocation = LeafLocation(leafNumber);
int otherLeafLocation = LeafLocation(otherLeafNumber);
int parent = Parent(leafLocation);
bool cameFromLeft = (leafLocation == LeftChild(parent));
return Walk2(minSoFar, parent, cameFromLeft, leftSide, otherLeafLocation);
}
int Walk2(int minSoFar, int node, bool cameFromLeft, bool leftSide, int otherLeafLocation)
{
if (IsAncestorOf(node, otherLeafLocation))
return minSoFar;
if (leftSide)
minSoFar = !cameFromLeft ? minSoFar : Math.Min(minSoFar, tree[RightChild(node)]);
else
minSoFar = cameFromLeft ? minSoFar : Math.Min(minSoFar, tree[LeftChild(node)]);
return Walk2(minSoFar, Parent(node), node == LeftChild(Parent(node)), leftSide, otherLeafLocation);
}
}
}

用于测试它的 C# 代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace RangeSearch
{
class Program
{
static void Main(string[] args)
{
RangeSearch rngA = new RangeSearch(9, 3, 7, 1);
System.Diagnostics.Trace.Assert(3 == rngA.FindMin(0, 2) );
System.Diagnostics.Trace.Assert(1 == rngA.FindMin(0, 3));
System.Diagnostics.Trace.Assert(1 == rngA.FindMin(1, 3));

RangeSearch rngB = new RangeSearch(1, 7, 3, 9);
System.Diagnostics.Trace.Assert(3 == rngB.FindMin(1, 3));
System.Diagnostics.Trace.Assert(1 == rngB.FindMin(0, 3));
System.Diagnostics.Trace.Assert(1 == rngB.FindMin(0, 2));

RangeSearch rngC = new RangeSearch(17, 21, 77, 70, 58, 79, 79, 89);
System.Diagnostics.Trace.Assert(21 == rngC.FindMin(1, 7));

RangeSearch rngD = new RangeSearch(94, 78, 88, 72, 95, 97, 89, 83);
System.Diagnostics.Trace.Assert(72 == rngD.FindMin(1, 6));

RangeSearch rngE = new RangeSearch(0, 66, 6, 43, 34, 34, 63, 49);
System.Diagnostics.Trace.Assert(34 == rngE.FindMin(3, 4));

Random rnd = new Random();
for (int i = 0; i < 1000000; i++)
{
int[] tmp = new int[64];
for (int j = 0; j < tmp.Length; j++)
tmp[j] = rnd.Next(0, 100);
int a = rnd.Next(0, tmp.Length);
int b = rnd.Next(a, tmp.Length);
RangeSearch rng = new RangeSearch(tmp);
System.Diagnostics.Trace.Assert(Min(tmp, a, b) == rng.FindMin(a, b));
}
}

static int Min(int[] ar, int a, int b)
{
int x = ar[a];
for (int i = a + 1; i <= b; i++)
x = Math.Min(x, ar[i]);
return x;
}

}
}

关于algorithm - 使用 O(n) 存储和 O(log n) 查询时间的什么数据结构应该用于范围最小查询?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2315396/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com