笛卡尔树是根据一组数据创建的树数据结构,这些数据遵循以下结构不变式:
- 树遵循最小(或最大)堆属性–每个节点小于(或大于)其子节点。
- 节点的有序遍历产生的值与它们在初始序列中出现的顺序相同。
假设我们有一个输入数组-{5,10,40,30,28}。则最大堆的笛卡尔树将是。
上面输入数组的最小堆笛卡尔树将是-
笔记:
- 笛卡尔树不是高度平衡的树。
- 具有不同数字序列的笛卡尔树始终是唯一的。
具有不同数字序列的笛卡尔树始终是唯一的。 如何构造笛卡尔树? AO(nlogn)算法: 从左到右扫描给定序列,添加新节点,如下所示: 输出: 时间复杂度: 辅助空间: 笛卡尔树的应用 参考:
我们将使用归纳法证明这一点。作为基本情况,空树始终是唯一的。对于归纳情况,假定对于所有包含n'
这里讨论了用于构造笛卡尔树的AO(n 2 )解(注意,上面的程序在这里构造了“特殊二叉树”(除了笛卡尔树之外什么都没有))
平均可以用O(NlogN)时间的数据序列构建笛卡尔树。从空树开始
CPP
// A O(n) C++ program to construct cartesian tree
// from a given array
#include
Java
// A O(n) Java program to concartesian tree
// from a given array
/* A binary tree node has data, pointer to left
child and a pointer to right child */
class GFG
{
static class Node
{
int data;
Node left, right;
};
/* This funtcion is here just to test buildTree() */
static void printInorder (Node node)
{
if (node == null)
return;
printInorder (node.left);
System.out.print(node.data + " ");
printInorder (node.right);
}
// Recursively consubtree under given root using
// leftChil[] and rightchild
static Node buildCartesianTreeUtil (int root, int arr[],
int parent[], int leftchild[], int rightchild[])
{
if (root == -1)
return null;
// Create a new node with root's data
Node temp = new Node();
temp.data = arr[root] ;
// Recursively conleft and right subtrees
temp.left = buildCartesianTreeUtil( leftchild[root],
arr, parent, leftchild, rightchild );
temp.right = buildCartesianTreeUtil( rightchild[root],
arr, parent, leftchild, rightchild );
return temp ;
}
// A function to create the Cartesian Tree in O(N) time
static Node buildCartesianTree (int arr[], int n)
{
// Arrays to hold the index of parent, left-child,
// right-child of each number in the input array
int []parent = new int[n];
int []leftchild = new int[n];
int []rightchild = new int[n];
// Initialize all array values as -1
memset(parent, -1);
memset(leftchild, -1);
memset(rightchild, -1);
// 'root' and 'last' stores the index of the root and the
// last processed of the Cartesian Tree.
// Initially we take root of the Cartesian Tree as the
// first element of the input array. This can change
// according to the algorithm
int root = 0, last;
// Starting from the second element of the input array
// to the last on scan across the elements, adding them
// one at a time.
for (int i = 1; i <= n - 1; i++)
{
last = i - 1;
rightchild[i] = -1;
// Scan upward from the node's parent up to
// the root of the tree until a node is found
// whose value is greater than the current one
// This is the same as Step 2 mentioned in the
// algorithm
while (arr[last] <= arr[i] && last != root)
last = parent[last];
// arr[i] is the largest element yet; make it
// new root
if (arr[last] <= arr[i])
{
parent[root] = i;
leftchild[i] = root;
root = i;
}
// Just insert it
else if (rightchild[last] == -1)
{
rightchild[last] = i;
parent[i] = last;
leftchild[i] = -1;
}
// Reconfigure links
else
{
parent[rightchild[last]] = i;
leftchild[i] = rightchild[last];
rightchild[last] = i;
parent[i] = last;
}
}
// Since the root of the Cartesian Tree has no
// parent, so we assign it -1
parent[root] = -1;
return (buildCartesianTreeUtil (root, arr, parent,
leftchild, rightchild));
}
static void memset(int[] arr, int value)
{
for (int i = 0; i < arr.length; i++)
{
arr[i] = value;
}
}
/* Driver code */
public static void main(String[] args)
{
/* Assume that inorder traversal of following tree
is given
40
/ \
10 30
/ \
5 28 */
int arr[] = {5, 10, 40, 30, 28};
int n = arr.length;
Node root = buildCartesianTree(arr, n);
/* Let us test the built tree by printing Inorder
traversal */
System.out.printf("Inorder traversal of the" +
" constructed tree : \n");
printInorder(root);
}
}
// This code is contributed by PrinciRaj1992
C#
// A O(n) C# program to concartesian tree
// from a given array
/* A binary tree node has data, pointer to left
child and a pointer to right child */
using System;
class GFG
{
class Node
{
public int data;
public Node left, right;
};
/* This funtcion is here just to test buildTree() */
static void printInorder (Node node)
{
if (node == null)
return;
printInorder (node.left);
Console.Write(node.data + " ");
printInorder (node.right);
}
// Recursively consubtree under given root using
// leftChil[] and rightchild
static Node buildCartesianTreeUtil (int root, int []arr,
int []parent, int []leftchild, int []rightchild)
{
if (root == -1)
return null;
// Create a new node with root's data
Node temp = new Node();
temp.data = arr[root] ;
// Recursively conleft and right subtrees
temp.left = buildCartesianTreeUtil( leftchild[root],
arr, parent, leftchild, rightchild );
temp.right = buildCartesianTreeUtil( rightchild[root],
arr, parent, leftchild, rightchild );
return temp ;
}
// A function to create the Cartesian Tree in O(N) time
static Node buildCartesianTree (int []arr, int n)
{
// Arrays to hold the index of parent, left-child,
// right-child of each number in the input array
int []parent = new int[n];
int []leftchild = new int[n];
int []rightchild = new int[n];
// Initialize all array values as -1
memset(parent, -1);
memset(leftchild, -1);
memset(rightchild, -1);
// 'root' and 'last' stores the index of the root and the
// last processed of the Cartesian Tree.
// Initially we take root of the Cartesian Tree as the
// first element of the input array. This can change
// according to the algorithm
int root = 0, last;
// Starting from the second element of the input array
// to the last on scan across the elements, adding them
// one at a time.
for (int i = 1; i <= n - 1; i++)
{
last = i - 1;
rightchild[i] = -1;
// Scan upward from the node's parent up to
// the root of the tree until a node is found
// whose value is greater than the current one
// This is the same as Step 2 mentioned in the
// algorithm
while (arr[last] <= arr[i] && last != root)
last = parent[last];
// arr[i] is the largest element yet; make it
// new root
if (arr[last] <= arr[i])
{
parent[root] = i;
leftchild[i] = root;
root = i;
}
// Just insert it
else if (rightchild[last] == -1)
{
rightchild[last] = i;
parent[i] = last;
leftchild[i] = -1;
}
// Reconfigure links
else
{
parent[rightchild[last]] = i;
leftchild[i] = rightchild[last];
rightchild[last] = i;
parent[i] = last;
}
}
// Since the root of the Cartesian Tree has no
// parent, so we assign it -1
parent[root] = -1;
return (buildCartesianTreeUtil (root, arr, parent,
leftchild, rightchild));
}
static void memset(int[] arr, int value)
{
for (int i = 0; i < arr.Length; i++)
{
arr[i] = value;
}
}
/* Driver code */
public static void Main(String[] args)
{
/* Assume that inorder traversal of following tree
is given
40
/ \
10 30
/ \
5 28 */
int []arr = {5, 10, 40, 30, 28};
int n = arr.Length;
Node root = buildCartesianTree(arr, n);
/* Let us test the built tree by printing Inorder
traversal */
Console.Write("Inorder traversal of the" +
" constructed tree : \n");
printInorder(root);
}
}
// This code is contributed by 29AjayKumar
Inorder traversal of the constructed tree :
5 10 40 30 28
乍一看,由于buildCartesianTree()中有两个循环,因此代码似乎要花费O(n 2 )时间。但实际上,排序的遍历平均需要O(NlogN)时间和O(n ^ 2)。
我们为每个节点以及三个额外的数组声明一个结构-leftchild [],rightchild [],parent [],以保存输入数组中每个值的左子项,右子项,父项的索引。因此,总体O(4 * n)= O(n)额外空间。
http://wcipeg.com/wiki/Cartesian_tree