跳转至

笛卡尔树

概述

笛卡尔树同时满足二叉搜索树(按键值)和堆(按优先级)的性质,常用于RMQ问题和构建Treap。

笛卡尔树特点

  1. BST性质:中序遍历按键有序
  2. 堆性质:父节点优先级优于子节点
  3. 唯一性:键值互不相同时唯一
  4. 线性构建:可O(n)时间构建

数据结构

C
typedef struct CartesianNode {
    int key;
    int value;
    struct CartesianNode *left;
    struct CartesianNode *right;
    struct CartesianNode *parent;
} CartesianNode;

typedef struct {
    CartesianNode *root;
} CartesianTree;

创建节点

C
CartesianNode* createCartesianNode(int key, int value) {
    CartesianNode *node = (CartesianNode*)malloc(sizeof(CartesianNode));
    node->key = key;
    node->value = value;
    node->left = NULL;
    node->right = NULL;
    node->parent = NULL;
    return node;
}

CartesianTree* createCartesianTree() {
    CartesianTree *tree = (CartesianTree*)malloc(sizeof(CartesianTree));
    tree->root = NULL;
    return tree;
}

构建笛卡尔树(单调栈)

C
CartesianTree* buildCartesianTree(int keys[], int values[], int n) {
    CartesianTree *tree = createCartesianTree();
    
    if (n == 0) return tree;
    
    CartesianNode **stack = (CartesianNode**)malloc(sizeof(CartesianNode*) * n);
    int top = -1;
    
    for (int i = 0; i < n; i++) {
        CartesianNode *node = createCartesianNode(keys[i], values[i]);
        CartesianNode *last = NULL;
        
        while (top >= 0 && stack[top]->value > values[i]) {
            last = stack[top--];
        }
        
        node->left = last;
        if (last) last->parent = node;
        
        if (top >= 0) {
            stack[top]->right = node;
            node->parent = stack[top];
        }
        
        stack[++top] = node;
    }
    
    tree->root = stack[0];
    
    while (tree->root->parent != NULL) {
        tree->root = tree->root->parent;
    }
    
    free(stack);
    return tree;
}

RMQ查询

C
int queryRMQ(CartesianNode *root, int l, int r) {
    if (root == NULL) return INT_MAX;
    
    if (root->key < l) {
        return queryRMQ(root->right, l, r);
    } else if (root->key > r) {
        return queryRMQ(root->left, l, r);
    } else {
        return root->value;
    }
}

LCA查询

C
CartesianNode* lcaCartesian(CartesianNode *root, int l, int r) {
    if (root == NULL) return NULL;
    
    if (root->key < l) {
        return lcaCartesian(root->right, l, r);
    } else if (root->key > r) {
        return lcaCartesian(root->left, l, r);
    }
    
    return root;
}

中序遍历

C
1
2
3
4
5
6
7
void inorderCartesian(CartesianNode *root, int result[], int *index) {
    if (root == NULL) return;
    
    inorderCartesian(root->left, result, index);
    result[(*index)++] = root->value;
    inorderCartesian(root->right, result, index);
}

C++ 实现

C++
#include <vector>
#include <stack>
#include <algorithm>

class CartesianTree {
private:
    struct Node {
        int key, value;
        Node *left, *right, *parent;
        Node(int k, int v) : key(k), value(v), 
                            left(nullptr), right(nullptr), parent(nullptr) {}
    };
    
    Node *root;
    
public:
    CartesianTree() : root(nullptr) {}
    
    void build(const std::vector<int>& keys, const std::vector<int>& values) {
        int n = keys.size();
        if (n == 0) return;
        
        std::stack<Node*> st;
        
        for (int i = 0; i < n; i++) {
            Node *node = new Node(keys[i], values[i]);
            Node *last = nullptr;
            
            while (!st.empty() && st.top()->value > values[i]) {
                last = st.top();
                st.pop();
            }
            
            node->left = last;
            if (last) last->parent = node;
            
            if (!st.empty()) {
                st.top()->right = node;
                node->parent = st.top();
            }
            
            st.push(node);
        }
        
        root = st.top();
        while (root->parent) root = root->parent;
    }
    
    int rmq(int l, int r) {
        Node *curr = root;
        
        while (curr) {
            if (curr->key < l) {
                curr = curr->right;
            } else if (curr->key > r) {
                curr = curr->left;
            } else {
                return curr->value;
            }
        }
        
        return INT_MAX;
    }
    
    std::vector<int> inorder() {
        std::vector<int> result;
        inorderHelper(root, result);
        return result;
    }
    
private:
    void inorderHelper(Node *node, std::vector<int>& result) {
        if (!node) return;
        inorderHelper(node->left, result);
        result.push_back(node->value);
        inorderHelper(node->right, result);
    }
};

笛卡尔树与RMQ

笛卡尔树将RMQ问题转化为LCA问题: - 数组元素值作为堆优先级 - 数组下标作为BST键值 - 区间[l,r]的最小值 = lca(l,r)的值

笛卡尔树与Treap

如果优先级随机,笛卡尔树就是Treap: - 期望高度O(log n) - 期望操作时间O(log n)

时间复杂度

操作 时间复杂度
构建 O(n)
RMQ O(n) 最坏
LCA O(n) 最坏
中序遍历 O(n)

应用场景

  1. RMQ问题:区间最值查询
  2. 构建Treap:快速构建
  3. 字符串算法:后缀数组构建
  4. 虚树构建:LCA相关

参考资料

  • Vuillemin (1980) 笛卡尔树论文
  • 《数据结构与算法分析》