行为树的设计与实现

原地址:http://blog.csdn.net/luyuncsd123/article/details/18351137

查阅了一些行为树资料,目前最主要是参考了这篇文章,看完后感觉行为树实乃强大,绝对是替代状态机的不二之选。但从理论看起来很简单的行为树,真正着手起来却发现很多细节无从下手。

总结起来,就是:

1、行为树只是单纯的一棵决策树,还是决策+控制树。为了防止不必要的麻烦,我目前设计成单纯的决策树。

2、什么时候执行行为树的问题,也就是行为树的Tick问题,是在条件变化的时候执行一次,还是只要对象激活,就在Update里面一直Tick。前者明显很节省开销,但那样设计的最终结果可能是最后陷入事件发送的泥潭中。那么一直Tick可能是最简单的办法,于是就引下面出新的问题。目前采用了一直Tick的办法。

3、基本上可以明显节点有 Composite
Node、Decorator Node、Condition Node、Action
Node,但具体细节就很头疼。比如组合节点里的Sequence
Node。这个节点是不是在每个Tick周期都从头迭代一次子节点,还是记录正在运行的子节点。每次都迭代子节点,就感觉开销有点大。记录运行节点就会出现条件冗余问题,具体后面再讨论。目前采用保存当前运行节点的办法。

4、条件节点(Condition
Node)的位置问题。看到很多设计都是条件节点在最后才进行判断,而实际上,如果把条件放在组合节点处,就可以有效短路判断,不再往下迭代。于是我就采用了这种方法。

设计开始

在Google
Code上看到的某个行为树框架,用的是抽象类做节点。考虑到C#不能多继承,抽象类可能会导致某些时候会很棘手,所以还是用接口。虽然目前还未发现接口的好处。

在进行抽象设计的时候,接口的纯粹性虽然看起来更加清晰,不过有时候遇到需要重复使用某些类函数的时候就挺麻烦,让人感觉有点不利于复用。

[csharp] view plaincopy

public enum RunStatus
{
Completed,
Failure,
Running,
}

public interface IBehaviourTreeNode
{
RunStatus status { get; set; }
string nodeName { get; set; }
bool Enter(object input);
bool Leave(object input);
bool Tick(object input, object output);
RenderableNode renderNode { get; set; }
IBehaviourTreeNode parent { get; set; }
IBehaviourTreeNode Clone();
}

/************************************************************************/
/* 组合结点 */
/************************************************************************/
public interface ICompositeNode : IBehaviourTreeNode
{
void AddNode(IBehaviourTreeNode node);
void RemoveNode(IBehaviourTreeNode node);
bool HasNode(IBehaviourTreeNode node);

void AddCondition(IConditionNode node);
void RemoveCondition(IConditionNode node);
bool HasCondition(IConditionNode node);

ArrayList nodeList { get; }
ArrayList conditionList { get; }
}

/************************************************************************/
/* 选择节点 */
/************************************************************************/
public interface ISelectorNode : ICompositeNode
{

}

/************************************************************************/
/*顺序节点 */
/************************************************************************/
public interface ISequenceNode : ICompositeNode
{

}

/************************************************************************/
/* 平行(并列)节点 */
/************************************************************************/
public interface IParallelNode : ICompositeNode
{

}

//////////////////////////////////////////////////////////////////////////

/************************************************************************/
/* 装饰结点 */
/************************************************************************/
public interface IDecoratorNode : IBehaviourTreeNode
{

}

/************************************************************************/
/* 条件节点 */
/************************************************************************/
public interface IConditionNode
{
string nodeName { get; set; }
bool ExternalCondition();
}

/************************************************************************/
/* 行为节点 */
/************************************************************************/
public interface IActionNode : IBehaviourTreeNode
{

}

public interface IBehaviourTree
{

}

很多节点的接口都是空的,目前唯一的作用就是用于类型判断,很可能在最后也没有什么实际的作用,搞不好就是所谓的过度设计。如果最终确定没有用再删掉吧。

接口里出现了一个渲染节点,目的是为了能够更方便的把这个节点和负责渲染的节点联系到一起,方便节点的可视化。

如果只有接口,每次实现接口都要重复做很多工作,为了利用面向对象的复用特性,就来实现一些父类

[csharp] view plaincopy

public class BaseNode
{
public BaseNode() { nodeName_ = this.GetType().Name + "\n"; }

protected RunStatus status_ = RunStatus.Completed;
protected string nodeName_;
protected RenderableNode renderNode_;
protected IBehaviourTreeNode parent_;

public virtual RunStatus status { get { return status_; } set { status_ = value; } }
public virtual string nodeName { get { return nodeName_; } set { nodeName_ = value; } }
public virtual RenderableNode renderNode { get { return renderNode_; } set { renderNode_ = value; } }
public virtual IBehaviourTreeNode parent { get { return parent_; } set { parent_ = value; } }
public virtual IBehaviourTreeNode Clone() {
var clone = new BaseNode();
clone.status_ = status_;
clone.nodeName_ = nodeName_;
clone.renderNode_ = renderNode_;
clone.parent_ = parent_;
return clone as IBehaviourTreeNode;
}
}

public class BaseActionNode : IActionNode
{
public BaseActionNode() { nodeName_ = this.GetType().Name + "\n"; }
protected RunStatus status_ = RunStatus.Completed;
protected string nodeName_;
protected RenderableNode renderNode_;
protected IBehaviourTreeNode parent_;
public virtual RunStatus status { get { return status_; } set { status_ = value; } }
public virtual string nodeName { get { return nodeName_; } set { nodeName_ = value; } }
public virtual RenderableNode renderNode { get { return renderNode_; } set { renderNode_ = value; } }
public virtual IBehaviourTreeNode parent { get { return parent_; } set { parent_ = value; } }
public virtual IBehaviourTreeNode Clone()
{
var clone = new BaseActionNode();
clone.status_ = status_;
clone.nodeName_ = nodeName_;
clone.renderNode_ = renderNode_;
clone.parent_ = parent_;
return clone as IBehaviourTreeNode;
}

public virtual bool Enter(object input)
{
status_ = RunStatus.Running;
return true;
}

public virtual bool Leave(object input)
{
status_ = RunStatus.Completed;
return true;
}

public virtual bool Tick(object input, object output)
{
return true;
}
}
public class BaseCondictionNode {
protected string nodeName_;
public virtual string nodeName { get { return nodeName_; } set { nodeName_ = value; } }
public BaseCondictionNode() { nodeName_ = this.GetType().Name+"\n"; }
public delegate bool ExternalFunc();
protected ExternalFunc externalFunc;
public static ExternalFunc GetExternalFunc(BaseCondictionNode node) {
return node.externalFunc;
}
}

public class Precondition : BaseCondictionNode, IConditionNode{
public Precondition(ExternalFunc func) { externalFunc = func; }
public Precondition(BaseCondictionNode pre) { externalFunc = BaseCondictionNode.GetExternalFunc(pre); }
public bool ExternalCondition()
{
if (externalFunc != null) return externalFunc();
else return false;
}
}

public class PreconditionNOT : BaseCondictionNode, IConditionNode
{
public PreconditionNOT(ExternalFunc func) { externalFunc = func; }
public PreconditionNOT(BaseCondictionNode pre) { externalFunc = BaseCondictionNode.GetExternalFunc(pre); }
public bool ExternalCondition()
{
if (externalFunc != null) return !externalFunc();
else return false;
}
}

public class BaseCompositeNode : BaseNode{
protected ArrayList nodeList_ = new ArrayList();
protected ArrayList conditionList_ = new ArrayList();
protected int runningNodeIndex = 0;
protected bool CheckNodeAndCondition() {
if (nodeList_.Count == 0)
{
status_ = RunStatus.Failure;
Debug.Log("SequenceNode has no node!");
return false;
}
return CheckCondition();
}
protected bool CheckCondition() {
foreach (var node in conditionList_)
{
var condiction = node as IConditionNode;
if (!condiction.ExternalCondition())
return false;
}
return true;
}
public virtual void AddNode(IBehaviourTreeNode node) { node.parent = (IBehaviourTreeNode)this; nodeList_.Add(node); }
public virtual void RemoveNode(IBehaviourTreeNode node) { nodeList_.Remove(node); }
public virtual bool HasNode(IBehaviourTreeNode node) { return nodeList_.Contains(node); }

public virtual void AddCondition(IConditionNode node) { conditionList_.Add(node); }
public virtual void RemoveCondition(IConditionNode node) { conditionList_.Remove(node); }
public virtual bool HasCondition(IConditionNode node) { return conditionList_.Contains(node); }

public virtual ArrayList nodeList { get { return nodeList_; } }
public virtual ArrayList conditionList { get { return conditionList_; } }

public override IBehaviourTreeNode Clone()
{
var clone = base.Clone() as BaseCompositeNode;
clone.nodeList_.AddRange(nodeList_);
clone.conditionList_.AddRange(conditionList_);
clone.runningNodeIndex = runningNodeIndex;
return clone as IBehaviourTreeNode;
}
}


View
Code

然后实现具体的节点,先是序列节点

[csharp] view plaincopy


public class SequenceNode : BaseCompositeNode, ISequenceNode
{
public SequenceNode(bool canContinue_ = false) { canContinue = canContinue_; }
public bool canContinue = false;

public bool Enter(object input)
{
var checkOk = CheckNodeAndCondition();
if (!checkOk) return false;
var runningNode = nodeList_[runningNodeIndex] as IBehaviourTreeNode;
checkOk = runningNode.Enter(input);
if (!checkOk) return false;
status_ = RunStatus.Running;
return true;
}

public bool Leave(object input)
{
if (nodeList_.Count == 0)
{
status_ = RunStatus.Failure;
Debug.Log("SequenceNode has no node!");
return false;
}
var runningNode = nodeList_[runningNodeIndex] as IBehaviourTreeNode;
runningNode.Leave(input);
if (canContinue)
{
runningNodeIndex++;
runningNodeIndex %= nodeList_.Count;
}
status_ = RunStatus.Completed;
return true;
}

public bool Tick(object input, object output)
{
if (status_ == RunStatus.Failure) return false;
if (status_ == RunStatus.Completed) return true;

var runningNode = nodeList_[runningNodeIndex] as IBehaviourTreeNode;
var checkOk = CheckCondition();
if (!checkOk)
{
return false;
}

switch (runningNode.status)
{
case RunStatus.Running:
if (!runningNode.Tick(input, output))
{
runningNode.Leave(input);
return false;
}

break;
default:
runningNode.Leave(input);
runningNodeIndex++;
if(runningNodeIndex >= nodeList_.Count)break;
var nextNode = nodeList_[runningNodeIndex] as IBehaviourTreeNode;
var check = nextNode.Enter(input);
if (!check) return false;
break;
}
return true;
}

public override IBehaviourTreeNode Clone()
{
var clone = base.Clone() as SequenceNode;
clone.canContinue = canContinue;
return clone;
}
}

这就是序列节点的设计,但是明显看起来很不爽,里面还出现了一个别扭的变量canContinue
。为什么会出现这个?因为序列节点的特点就是遇到一个子节点FALSE,就会停止并返回FALSE,但是这里我想用序列节点来做根节点,如果是根节点遇到这种情况,那么就不会执行下一个节点,而我看了很多种对于几大节点的描述,似乎都没提到这个。很多都用序列节点做根节点,有些就直接说是根节点。那么要么根节点另外实现,要么改一下序列节点。因为如果序列节点是非根节点的情况下,如果不是每次都从头开始,似乎又会引来新的问题,虽然目前还没想到会出什么问题。不过最后实现执行起来之后发现,用选择节点其实是一样的。所以目前这样的设计,可能是有根本上的问题。希望哪位大神可以指点一下。

然后是选择节点,根据了所有FALSE才返回FALSE的特点设计了

[csharp] view plaincopy

public class SelectorNode : BaseCompositeNode, ISelectorNode
{
public bool Enter(object input)
{
var checkOk = CheckNodeAndCondition();
if (!checkOk) return false;

do
{
var runningNode = nodeList_[runningNodeIndex] as IBehaviourTreeNode;
checkOk = runningNode.Enter(input);
if (checkOk) break;
runningNodeIndex++;
if (runningNodeIndex >= nodeList_.Count) return false;
} while (!checkOk);

status_ = RunStatus.Running;
return true;
}

public bool Leave(object input)
{
var runningNode = nodeList_[runningNodeIndex] as IBehaviourTreeNode;
runningNode.Leave(input);
runningNodeIndex = 0;
status_ = RunStatus.Completed;
return true;
}

public bool Tick(object input, object output)
{
if (status_ == RunStatus.Failure) return false;
if (status_ == RunStatus.Completed) return true;
var checkOk1 = CheckCondition();
if (!checkOk1) return false;
var runningNode = nodeList_[runningNodeIndex] as IBehaviourTreeNode;
switch (runningNode.status)
{
case RunStatus.Running:
if (!runningNode.Tick(input, output))
{
runningNode.Leave(input);
return false;
}

break;
default:
runningNode.Leave(input);
runningNodeIndex++;
if (runningNodeIndex >= nodeList_.Count) return false;

bool checkOk = false;
do
{
var nextNode = nodeList_[runningNodeIndex] as IBehaviourTreeNode;
checkOk = nextNode.Enter(input);
if (checkOk) break;
runningNodeIndex++;
if (runningNodeIndex >= nodeList_.Count) return false;
} while (!checkOk);
break;
}
return true;
}
}

目前对于我的简单DEMO,组合节点只需要这两个就够了,实际上只需要选择节点、条件节点、动作节点就够了。所以说设计是不完全的,虽然能够实现目标需求,但是实际工作量仍挺大,具体接下来会说明。

行为节点

先放一些渲染节点的代码。实际上我基本上是第一次接触自己去渲染一种数据结构,看完网上的大牛们随随便便就能写出个数据结构的示意图,不得不佩服。我一时半会没想出怎么渲染出树状结构,于是就简单的把树按层分组,一层一层渲染,缺点就是不能很好的表现树的样子,父子关系不能很好的表示。这里放出来希望能抛砖引玉。我以后可能会去完事它,但是现在首先是要搞清楚行为树。实现这个完全是为了看看节点是否正确放置,以方便调试。

[csharp] view plaincopy


public class RenderableNode
{
public RenderableNode parent;
public IBehaviourTreeNode targetNode;
public Rect posRect = new Rect();
public string name;
public int layer;
public RunStatus staus;
public override string ToString()
{
return name + "\n" + staus.ToString();
}
public virtual void Render()
{
bool running = staus == RunStatus.Running;
var rect = posRect;
rect.y -= (posRect.height / 2);

var oldColor = GUI.color;
if (running)
{
GUI.color = Color.green;
}
GUI.Box(rect, ToString());
GUI.color = oldColor;

if (parent == null && targetNode != null && targetNode.parent!=null)
{
parent = targetNode.parent.renderNode;
}
if (parent != null)
{
Vector2 parentPos = new Vector2();
parentPos.x = parent.posRect.x + parent.posRect.width;
parentPos.y = parent.posRect.y;
GUIHelper.DrawLine(new Vector2(rect.x, rect.y + rect.height / 2), parentPos, running?Color.green:Color.yellow);
}

}
}

public class RenderableCondictionNode : RenderableNode
{
public IConditionNode targetCondictionNode;
public override string ToString() { parent = null; return name; }
public override void Render()
{
var rect = posRect;
rect.y -= (posRect.height / 2);

var oldColor = GUI.color;
if (targetCondictionNode.ExternalCondition())
GUI.color = Color.green;
else
GUI.color = Color.blue;
GUI.Box(rect, ToString());
GUI.color = oldColor;
}
}

public class EmptyNode : RenderableNode { public override void Render() { } }

public class NodeBox
{
public Rect posRect = new Rect();
public List<RenderableNode> nodeList = new List<RenderableNode>();
public void AddNode(RenderableNode node)
{
nodeList.Add(node);
}
public void Render()
{
posRect.y = Screen.height / 2;
Rect rect = new Rect();

foreach (var node in nodeList)
{
var n = node;
rect.height += (n.posRect.height + 1);
rect.width = n.posRect.width + 10;
}
rect.height += 10;
rect.x = posRect.x - rect.width / 2;
rect.y = posRect.y - rect.height / 2;
//GUI.Box(rect, "");
posRect.width = rect.width;
posRect.height = rect.height;
float height = 0;
for (var i = 0; i < nodeList.Count; i++)
{
var n = nodeList[i];
n.posRect.y = rect.y + height + n.posRect.height / 2 + 5;
n.posRect.x = rect.x + 5;
n.Render();
height += n.posRect.height + 1;
}
}
}

放一张渲染出来的效果

虽然每一组都只是简单的居中,不过效果看起来还可以接受

然后从图中就可以看到问题了。所有正条件,都会有一个反条件,不这么做就无法在条件改变时,让当前节点返回FALSE,从而让行为树去寻找其他节点。而如果用状态机来做的话,条件肯定只用判断一次,比如

[csharp] view plaincopy

if(run){
Run();
}
else{
Walk();
}

那么可能就回到最初的组合节点的设计了,组合节点就不得不每次都扫描条件。其实本质上我是在担心开销问题,因为变成节点后,就不在是if
else那么简单,而是变成了函数调用的开销。简单的AI还好,如果大量复杂的AI,每次对整棵树进行扫描估计够呛。但是目前的设计,条件节点就会非常多,条件不完备就会出现BUG,似乎也不是非常好的情况。

最后放出一些细节

[csharp] view plaincopy


class PatrolAction : BaseActionNode {

public PatrolAction() { nodeName_ += "巡逻行为"; }

public override bool Tick(object input_, object output_)
{
// var input = input_ as WarriorInputData;
var output = output_ as WarriorOutPutData;
output.action = WarriorActon.ePatrol;
return true;
}
}

class RunAwayAction : BaseActionNode {
public RunAwayAction() { nodeName_ += "逃跑行为"; }

public override bool Tick(object input_, object output_)
{
// var input = input_ as WarriorInputData;
var output = output_ as WarriorOutPutData;
output.action = WarriorActon.eRunAway;
return true;
}
}

class AttackAction : BaseActionNode {
public AttackAction() { nodeName_ += "攻击行为"; }

public override bool Tick(object input_, object output_)
{
// var input = input_ as WarriorInputData;
var output = output_ as WarriorOutPutData;
output.action = WarriorActon.eAttack;
return true;
}
}

class CrazyAttackAction : BaseActionNode {
public CrazyAttackAction() { nodeName_ += "疯狂攻击行为"; }

public override bool Tick(object input_, object output_)
{
// var input = input_ as WarriorInputData;
var output = output_ as WarriorOutPutData;
output.action = WarriorActon.eCrazyAttack;
return true;
}
}

class AlertAction : BaseActionNode
{
public AlertAction() { nodeName_ += "警戒行为"; }
public override bool Tick(object input_, object output_)
{
// var input = input_ as WarriorInputData;
var output = output_ as WarriorOutPutData;
output.action = WarriorActon.eAlert;
return true;
}
}

[csharp] view plaincopy

private ICompositeNode rootNode = new SelectorNode();
private WarriorInputData inputData = new WarriorInputData();
private WarriorOutPutData outputData = new WarriorOutPutData();
// Use this for initialization
public void Start()
{
inputData.attribute = GetComponent<CharacterAttribute>();

rootNode.nodeName += "根";

//条件
var hasNoTarget = new PreconditionNOT(() => { return inputData.attribute.hasTarget; });
hasNoTarget.nodeName = "无目标";
var hasTarget = new Precondition(hasNoTarget);
hasTarget.nodeName = "发现目标";
var isAnger = new Precondition(() => { return inputData.attribute.isAnger; });
isAnger.nodeName = "愤怒状态";
var isNotAnger = new PreconditionNOT(isAnger);
isNotAnger.nodeName = "非愤怒状态";
var HPLessThan500 = new Precondition(() => { return inputData.attribute.health < 500; });
HPLessThan500.nodeName = "血少于500";
var HPMoreThan500 = new PreconditionNOT(HPLessThan500);
HPMoreThan500.nodeName = "血大于500";
var isAlert = new Precondition(() => { return inputData.attribute.isAlert; });
isAlert.nodeName = "警戒";
var isNotAlert = new PreconditionNOT(isAlert);
isNotAlert.nodeName = "非警戒";

var patrolNode = new SequenceNode();
patrolNode.nodeName += "巡逻";
patrolNode.AddCondition(hasNoTarget);
patrolNode.AddCondition(isNotAlert);
patrolNode.AddNode(new PatrolAction());

var alert = new SequenceNode();
alert.nodeName += "警戒";
alert.AddCondition(hasNoTarget);
alert.AddCondition(isAlert);
alert.AddNode(new AlertAction());

var runaway = new SequenceNode();
runaway.nodeName += "逃跑";
runaway.AddCondition(hasTarget);
runaway.AddCondition(HPLessThan500);
runaway.AddNode(new RunAwayAction());

var attack = new SelectorNode();
attack.nodeName += "攻击";
attack.AddCondition(hasTarget);
attack.AddCondition(HPMoreThan500);

var attackCrazy = new SequenceNode();
attackCrazy.nodeName += "疯狂攻击";
attackCrazy.AddCondition(isAnger);
attackCrazy.AddNode(new CrazyAttackAction());
attack.AddNode(attackCrazy);

var attackNormal = new SequenceNode();
attackNormal.nodeName += "普通攻击";
attackNormal.AddCondition(isNotAnger);
attackNormal.AddNode(new AttackAction());
attack.AddNode(attackNormal);

rootNode.AddNode(patrolNode);
rootNode.AddNode(alert);
rootNode.AddNode(runaway);
rootNode.AddNode(attack);
var ret = rootNode.Enter(inputData);
if (!ret)
{
Debug.Log("无可执行节点!");
}
}

// Update is called once per frame
void Update () {
var ret = rootNode.Tick(inputData, outputData);

if (!ret)
rootNode.Leave(inputData);

if (rootNode.status == RunStatus.Completed)
{
ret = rootNode.Enter(inputData);
if (!ret)
rootNode.Leave(inputData);
}
else if (rootNode.status == RunStatus.Failure)
{
Debug.Log("BT Failed");
enabled = false;
}

if (outputData.action != inputData.action)
{
OnActionChange(outputData.action, inputData.action);
inputData.action = outputData.action;
}
}

void OnActionChange(WarriorActon action, WarriorActon lastAction) {
// print("OnActionChange "+action+" last:"+lastAction);
switch (lastAction)
{
case WarriorActon.ePatrol:
GetComponent<WarriorPatrol>().enabled = false;
break;
case WarriorActon.eAttack:
case WarriorActon.eCrazyAttack:
GetComponent<WarriorAttack>().enabled = false;
break;
case WarriorActon.eRunAway:
GetComponent<WarriorRunAway>().enabled = false;
break;
case WarriorActon.eAlert:
GetComponent<WarriorAlert>().enabled = false;
break;
}

switch (action) {
case WarriorActon.ePatrol:
GetComponent<WarriorPatrol>().enabled = true;
break;
case WarriorActon.eAttack:
var attack = GetComponent<WarriorAttack>();
attack.revenge = false;
attack.enabled = true;
break;
case WarriorActon.eCrazyAttack:
var crazyAttack = GetComponent<WarriorAttack>();
crazyAttack.revenge = true;
crazyAttack.enabled = true;
break;
case WarriorActon.eRunAway:
GetComponent<WarriorRunAway>().enabled = true;
break;
case WarriorActon.eAlert:
GetComponent<WarriorAlert>().enabled = true;
break;
case WarriorActon.eIdle:
GetComponent<WarriorPatrol>().enabled = false;
GetComponent<WarriorAttack>().enabled = false;
GetComponent<WarriorRunAway>().enabled = false;
break;
}
}

行为树的设计与实现

时间: 2024-07-28 13:27:26

行为树的设计与实现的相关文章

java数据结构与算法之平衡二叉树(AVL树)的设计与实现

[版权申明]未经博主同意,不允许转载!(请尊重原创,博主保留追究权) http://blog.csdn.net/javazejian/article/details/53892797 出自[zejian的博客] 关联文章: java数据结构与算法之顺序表与链表设计与实现分析 java数据结构与算法之双链表设计与实现 java数据结构与算法之改良顺序表与双链表类似ArrayList和LinkedList(带Iterator迭代器与fast-fail机制) java数据结构与算法之栈(Stack)设

分类树测试用例设计工具:CTE XL

1.1工具安装 CTE XL是一款免费的分类树测试用例设计工具,安装过程很简单,下载好安装程序后,运行安装程序:如图点击下一步:点击下一步:是否创建桌面按钮:安装:  安装完成后,需要注册才能获取免费的License,打开CTE XL,会弹出一个注册的对话框,输入好Firstname,Famliyname,Organization,Email后系统将会发送一封带License的邮件到填写的email地址上,获取后,复制到license key中就完成注册了 1.2 CTE XL的使用 在了解CT

[BZOJ] 4552: [Tjoi2016&amp;Heoi2016]排序 #二分+线段树+算法设计策略

4552: [Tjoi2016&Heoi2016]排序 Time Limit: 60 Sec  Memory Limit: 256 MBSubmit: 1451  Solved: 734[Submit][Status][Discuss] Description 在2016年,佳媛姐姐喜欢上了数字序列.因而他经常研究关于序列的一些奇奇怪怪的问题,现在他在研究一个难题 ,需要你来帮助他.这个难题是这样子的:给出一个1到n的全排列,现在对这个全排列序列进行m次局部排序,排 序分为两种:1:(0,l,r

LSM树由来、设计思想以及应用到HBase的索引

转自:http://www.cnblogs.com/yanghuahui/p/3483754.html 讲LSM树之前,需要提下三种基本的存储引擎,这样才能清楚LSM树的由来: 哈希存储引擎  是哈希表的持久化实现,支持增.删.改以及随机读取操作,但不支持顺序扫描,对应的存储系统为key-value存储系统.对于key-value的插入以及查询,哈希表的复杂度都是O(1),明显比树的操作O(n)快,如果不需要有序的遍历数据,哈希表就是your Mr.Right B树存储引擎是B树(关于B树的由来

主席树设计与实现

主席树设计与实现 一.主席树的一句话简介 1.主席树是可持久化线段树 2.可持久化技术用于将多棵树相同的部分复用.不同的部分分开构成一颗新树. 3.主席树的实际物理原型是多颗线段树. 二.区间第K大问题 1.设计上应当考虑,如果有了多颗线段树,应当怎么做这道题? 2.有了思路之后应当考虑,可持久化线段树的性质是什么样的,使得我们应当如何针对这种特殊的性质完成线段树的设计? 3.如何进行更新操作? ans: 1.如果有多颗线段树,则每次插入新的元素建立一颗的线段树:在查找的时候,可以知道对于任意元

Opencv研读笔记:haartraining程序之cvCreateCARTClassifier函数详解(CART树状弱分类器创建)~

cvCreateCARTClassifier函数在haartraining程序中用于创建CART树状弱分类器,但一般只采用单一节点的CART分类器,即桩分类器,一个多节点的CART分类器训练耗时很多.根据自己的测试,要等差不多10分钟(2000正样本.2000负样本)才能训练完一个3节点的弱分类器,当然,总体的树状弱分类器的数目可能也会减少1/2.之所以将此函数拿出来说说,主要是因为在网上找不到针对这个函数的详细说明,同时,CART的应用十分广泛,自己也趁这个机会好好学学,把自己的一点理解分享给

《数据结构与算法分析》第四章--树 (1)

4.1 预备知识 定义: 树的递归定义:一棵树是一些节点的集合,这个集合若为空集:否则由一个根结点以及该节点的0个或者若干个非空子树组成,这些子树都与该根节点有边连接. 树叶:没有子节点的节点. 兄弟(Siblings):有相同父亲节点的节点. 节点n1到nj的路径:一个节点 序列:n1,n2...,nj. 路径的长:路径上的边数. 节点ni的深度:根节点到ni的唯一路径的长.根节点深度为0. 节点ni的高度:该节点ni到叶子节点的最长路径.树的高:根节点的高度. 4.1.1 树的实现 左孩子右

算法第十八章 B树

一.高级数据结构 本章以后到第21章(并查集)隶属于高级数据结构的内容.前面还留了两章:贪心算法和摊还分析,打算后面再来补充.之前的章节讨论的支持动态数据集上的操作,如查找.插入.删除等都是基于简单的线性表.链表和树等结构,本章以后的部分在原来更高的层次上来讨论这些操作,更高的层次意味着更复杂的结构,但更低的时间复杂度(包括摊还时间). B树是为磁盘存储还专门设计的平衡查找树.因为磁盘操作的速度要远远慢于内存,所以度量B树的性能,不仅要考虑动态集合操作消耗了多少计算时间,还要考虑这些操作执行了多

LSM和B+树区别

B树存储引擎是B树(关于B树的由来,数据结构以及应用场景可以看之前一篇博文)的持久化实现,不仅支持单条记录的增.删.读.改操作,还支持顺序扫描(B+树的叶子节点之间的指针),对应的存储系统就是关系数据库(Mysql等). LSM树(Log-Structured Merge Tree)存储引擎和B树存储引擎一样,同样支持增.删.读.改.顺序扫描操作.而且通过批量存储技术规避磁盘随机写入问题.当然凡事有利有弊,LSM树和B+树相比,LSM树牺牲了部分读性能,用来大幅提高写性能. 通过以上的分析,应该