笛卡尔树

笛卡尔树是一种同时满足二叉搜索树和堆的性质的数据结构。 可在一个数组上构造出来(时间复杂度可以达到O(n))。树中节点有几个属性, key(节点元素的大小)、index(节点在原数组中的索引)、left(左子节点)、right(右子节点)、parent(父节点)。

性质

  1. 树中的元素满足二叉搜索树性质,要求按照中序遍历得到的序列为原数组序列
  2. 树中节点满足堆性质,节点的key值要大于其左右子节点的key值

构造

    要求在给定的数组的基础上构造一棵笛卡尔树,这可以在O(n)的时间内完成。 其具体思路为:

    当按照index从1到n(或者从0到n-1)的顺序将数组中的每个元素插入到笛卡尔树中时,当前要被插入的元素的index值最大,因此根据二叉搜索的性质需要沿着当前已经完成的笛卡尔树的根的右子树链搜索。 
    由于笛卡尔树要满足堆的性质(以最大堆为例),父节点的key值要大于子节点的key值,所以沿着树根的右子树链往下走,直到搜索到的节点的key值小于等于当前要插入节点的key值。 
    此时,便找到了当前结点需要插入的位置,记为P。此时P下方的节点的key值肯定小于当前被插入节点的key,但是index也小于当前插入节点的index(即需要在二叉搜索树中当前结点之前的位置),所以将当前节点插入到P的位置,同时将以P为根的子树挂载到新插入的节点的左子树(为了保证P及其子树在新插入节点之前被二叉搜索)

 

    实际实现的时候,可以采用栈的数据结构。栈中保存当前树中的从树根开始的右子节点链,根在栈底部。 
    插入新元素的时候,从树的右子链的最末尾从下往上查找,直到找到第一个满足堆性质的节点(即找到的节点的key值大于当前需要插入的节点)。用栈来实现就是从栈顶不断弹出元素,直到栈顶的元素的key大于当前结点的key,然后将该节点入栈,同时将最后被弹出的节点的parent指向该节点,以及该节点的左子节点指向最后被弹出的节点。

 

复杂度分析: 每个节点最多入栈一次,出栈一次,因此时间复杂度为 O(n)

实现(c++)

struct DTreeNode{
    int index;            //在原来数组中的索引
    int key;            //节点的key,即原数组中 Array[index]值
    DTreeNode* left;    //左子节点
    DTreeNode* right;    //右子节点
    DTreeNode* parent;    //父节点
    DTreeNode(int i, int k) :
        index(i), key(k), left(NULL), right(NULL), parent(NULL){};
};

DTreeNode* BuildTree(int* arr, int n){
    stack<DTreeNode*> node_stack;
    DTreeNode* node = NULL, *new_node;
    for (int i = 0; i < n; i++){
        new_node = new DTreeNode(i, arr[i]);

        while (!node_stack.empty()){
            node = node_stack.top();
            if (node->key > new_node->key){ //直到栈顶的元素的key大于当前结点的key

                if (node->right){                //将原来的右子链挂载到new_node的左子树
                    node->right->parent = new_node;
                    new_node->left = node->right;
                }
                node->right = new_node;        //将新插入的节点插入,作为右链的最后
                new_node->parent = node;
                break;
            }
            node_stack.pop();
        }
        node_stack.push(new_node);        
    }

    //找出栈顶元素,就是笛卡尔树的根
    while (!node_stack.empty()){
        node = node_stack.top();
        node_stack.pop();  
    }
    return node;
}

void InorderTravel(DTreeNode* root){ //非递归进行中序遍历
    stack<DTreeNode*> node_stack;
    DTreeNode* node = root;

    while (!node_stack.empty() || node){
        if (node){
            node_stack.push(node);
            node = node->left;
        }
        else{
            node = node_stack.top();
            node_stack.pop(); 

            cout << "travel node, index = " << node->index << ", value = " << node->key << endl;

            node = node->right;
        }
        
    }
}
View Code

 

posted @ 2015-08-04 18:54  农民伯伯-Coding  阅读(4783)  评论(0编辑  收藏  举报