代码改变世界

2-3-4树(2)实现

2010-10-11 15:49  Clingingboy  阅读(655)  评论(0编辑  收藏  举报

 

为了简单起见,内部将不采用数组,而采用.net的List列表,此部分与用到的算法无关,所以可以忽略

Node的数据项操作

private static int MaxItems = 3;

private List<int> itemArray = new List<int>(MaxItems);

public bool IsFull
{
    get{ return (itemArray.Count == MaxItems) ? true : false; }
}

public int NumItems
{
    get { return itemArray.Count; }
}

public int FindItemIndex(int key)
{
    return itemArray.IndexOf(key);
}

public int GetItem(int index)
{
    return itemArray[index];
}

public int InsertItem(int key)
{
    itemArray.Add(key);
    itemArray.Sort();
    return -1;
}

public int RemoveItem()
{
    var item = itemArray[itemArray.Count - 1];
    itemArray.RemoveAt(itemArray.Count - 1);
    return item;
}

在没有数组的影子下是如此的easy

Node的节点查找

以下图为例子

image

如果现在要查找55

查找步骤:
  1. 每次都从根节点出发开始查找
  2. 先从节点内部的数据项开始匹配,如果匹配到则直接返回
  3. 如果不匹配则判断该节点是否有子节点,若有的话则继续
  4. 以查找关键字与该节点从左到右进行比较,找出比关键字大的节点(如62比55大,那么子节点的索引则是1)
  5. 重复2-4步骤(所以该查找同样也适用同内部的Node)
private List<Node234> childNodes = new List<Node234>(MaxItems+1);

public int FindNodeIndex(int value)
{
    var curNode = this;
    int childNumber;
    while (curNode!=null)
    {
        if ((childNumber = curNode.FindItemIndex(value)) != -1)
            return childNumber;
        else if (curNode.IsLeaf)
            return -1;
        else
            curNode = FindChildNode(value);
    }
    return -1;
}

private Node234 FindChildNode(int value)
{
    var i = 0;
    for (; i <itemArray.Count; i++)
    {
        if (value < itemArray[i])
            return childNodes[i];
    }
    return childNodes[i];
}

插入与分裂

情况1:如果没有分裂的情况下,插入非常的简单,如上先找到节点,然后就可以插入了

public void Insert(int value)
{
    var curNode = this;
    while (true)
    {
        if (curNode.IsLeaf)
            break;
        curNode = FindChildNode(curNode, value);
    }
    curNode.InsertItem(value);
}

情况2:根节点满

现在必须来解决分裂的问题,我们根据2种规则来分裂

首先要解决连接问题

public void ConnectChild(Node234 child)
{
    if (child == null) return;
    childNodes.Add(child);
    childNodes=childNodes.OrderBy(e=>e.itemArray[0]).ToList();
    if (child != null)
        child._parent = this;
}

public Node234 DisconnectChild(int index)
{
    if (childNodes.Count - 1 <= index) return null;
    var node = childNodes[index];
    childNodes.RemoveAt(index);
    return node;
}

规则1:根满

  1. 创建了2个新节点
  2. C(成为兄弟节点)
  3. B成为新的根节点,并成为A,C的父节点
  4. A保持不变
  5. 右侧2个节点断开与C连接,左侧2个节点保持不变

 

private void SplitRoot()
{
    var itemC = this.RemoveItem();
    var itemB = this.RemoveItem();
    Node234 parent = null, rightNode = null;
    parent = new Node234();
    //Connect Node
    parent.ConnectChild(this);

    var index=parent.InsertItem(itemB);
    
    
    for (var i = this.itemArray.Count-1; i >index; i--)
    {
        var child = this.DisconnectChild(i);
        parent.ConnectChild(child);
    }
    var child2 = this.DisconnectChild(2);
    var child3 = this.DisconnectChild(3);

    //Connect RightNode
    rightNode = new Node234();

    rightNode.InsertItem(itemC);
    rightNode.ConnectChild(child2);
    rightNode.ConnectChild(child3);

    parent.ConnectChild(rightNode);
    Root = parent;
}

规则2:不是根满的情况

  1. C被移到新的节点中
  2. B被移动到父节点
  3. A保持不变
  4. 断开右侧两个子节点并与新节点连接C,将新节点与父节点连接
private void SplitSubNode()
{
    //move C
    var itemC = this.RemoveItem();
    var newNode = new Node234();
    newNode.InsertItem(itemC);

    //move B
    var itemB = this.RemoveItem();
    Node234 parent = this.Parent;
    parent.InsertItem(itemB);

    //Connect RightNode
    var child2 = this.DisconnectChild(2);
    var child3 = this.DisconnectChild(3);
    newNode.ConnectChild(child2);
    newNode.ConnectChild(child3);

    //Connect Parent With NewNode
    parent.ConnectChild(newNode);
}

在Insert方法中添加判断

if (curNode.IsFull)
{
    curNode.Split();
    curNode = curNode.Parent;    // back up
    // search once
    curNode = curNode.FindChildNode(value);
}

 

代码中可能还有未知的错误,这里没有写tree234,只写了node,所以加了一个全局的node变量.全代码贴上吧.

只做技术演示而用

public class Node234
    {
        public Node234()
        {
            if (Node234.Root == null)
                Node234.Root = this;
        }

        public bool IsLeaf { get {return (childNodes.Count==0) ? true : false; } }

        private int numItems;

        private List<Node234> childNodes = new List<Node234>(MaxItems+1);

        public int FindNodeIndex(int value)
        {
            var curNode = this;
            int childNumber;
            while (curNode!=null)
            {
                if ((childNumber = curNode.FindItemIndex(value)) != -1)
                    return childNumber;
                else if (curNode.IsLeaf)
                    return -1;
                else
                    curNode = FindChildNode(value);
            }
            return -1;
        }

        private Node234 FindChildNode(int value)
        {
            var i = 0;
            for (; i <itemArray.Count; i++)
            {
                if (value < itemArray[i])
                    return childNodes[i];
            }
            return childNodes[i];
        }

        public void Insert(int value)
        {
           
            var curNode = this;
            while (true)
            {
                if (curNode.IsFull)
                {
                    curNode.Split();
                    curNode = curNode.Parent;    // back up
                    // search once
                    curNode = curNode.FindChildNode(value);
                }
                if (curNode.IsLeaf)
                    break;
                curNode = FindChildNode(value);
            }
            curNode.InsertItem(value);
        }

        private Node234 _parent;
        public Node234 Parent { get { return _parent; } }

        public void ConnectChild(Node234 child)
        {
            if (child == null) return;
            childNodes.Add(child);
            childNodes=childNodes.OrderBy(e=>e.itemArray[0]).ToList();
            if (child != null)
                child._parent = this;
        }

        public Node234 DisconnectChild(int index)
        {
            if (childNodes.Count - 1 <= index) return null;
            var node = childNodes[index];
            childNodes.RemoveAt(index);
            return node;
        }

        private void SplitRoot()
        {
            var itemC = this.RemoveItem();
            var itemB = this.RemoveItem();
            Node234 parent = null, rightNode = null;
            parent = new Node234();
            //Connect Node
            parent.ConnectChild(this);

            var index=parent.InsertItem(itemB);
            
            
            for (var i = this.itemArray.Count-1; i >index; i--)
            {
                var child = this.DisconnectChild(i);
                parent.ConnectChild(child);
            }
            var child2 = this.DisconnectChild(2);
            var child3 = this.DisconnectChild(3);

            //Connect RightNode
            rightNode = new Node234();

            rightNode.InsertItem(itemC);
            rightNode.ConnectChild(child2);
            rightNode.ConnectChild(child3);

            parent.ConnectChild(rightNode);
            Root = parent;
        }

        private void SplitSubNode()
        {
            //move C
            var itemC = this.RemoveItem();
            var newNode = new Node234();
            newNode.InsertItem(itemC);

            //move B
            var itemB = this.RemoveItem();
            Node234 parent = this.Parent;
            parent.InsertItem(itemB);

            //Connect RightNode
            var child2 = this.DisconnectChild(2);
            var child3 = this.DisconnectChild(3);
            newNode.ConnectChild(child2);
            newNode.ConnectChild(child3);

            //Connect Parent With NewNode
            parent.ConnectChild(newNode);
        }

        public static Node234 Root;

        public void Split()
        {
            if (this.Parent == null)
            {
                SplitRoot();
            }
            else
                SplitSubNode();
        }

        private static int MaxItems = 3;

        private List<int> itemArray = new List<int>(MaxItems);

        public bool IsFull
        {
            get{ return (itemArray.Count == MaxItems) ? true : false; }
        }

        public int NumItems
        {
            get { return itemArray.Count; }
        }

        public int FindItemIndex(int key)
        {
            return itemArray.IndexOf(key);
        }

        public int GetItem(int index)
        {
            return itemArray[index];
        }
        
        public int InsertItem(int key)
        {
            itemArray.Add(key);
            itemArray.Sort();
            return itemArray.IndexOf(key);
        }

        public int RemoveItem()
        {
            var item = itemArray[itemArray.Count - 1];
            itemArray.RemoveAt(itemArray.Count - 1);
            return item;
        }

        public Node234 GetChild(int index)
        {
            return childNodes[index];
        }

        public static void TestInsert()
        {
            var node = new Node234();
            node.Insert(30);
            node.Insert(10);
            node.Insert(20);
            node.Insert(40);
            node.Insert(50);
        }


    }