S
中的每个元素插入二叉树中。问题是,问题基本上要我想出一种方法,在我得到一个范围索引的未排序集合时来回答查询,然后在 O(log N) 时间内确定该范围内的最小值。怎么可能呢?即使将集合中的每个数字插入树中,我能做的最好的就是在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个值加2个指针加每个叶子1个值,对于完整的树为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;
}
}
}
9 3 7 1
,如果我将其简单地倒转为1 7 3 9
,它也会崩溃,因为现在你对左右的想法没有考虑到这一点。至少在我在纸上运行你的算法时是如此。 - Welbog好的,我认为我已经为您做了一个良好的开端,并在此过程中学到了新知识。
我建议您查看维基百科上关于笛卡尔树的条目。我不想告诉您更多的内容,以免替您完成作业,但是您似乎是个聪明的人,我相信您能够理解它。
顺便感谢您帮助我学习一种新的数据结构!
一些提示:
假设您以某种方式存储了长度为1、2、4、8等的所有对齐子数组的最小值? 您可以通过查看其中多少个最小值来返回正确答案? 如何存储它们以便您可以有效地检索它们?
(*例如,存储min(x0...3)和min(x4...x7),但不是min(x1...x4))