某数据库为提升搜索效率,对某一整型字段构建二叉搜索树(BST)

声明:下面的方法不一定正确,个人学习过程中所写,并未做大量测试,仅供参考,应该有BUG,暂时不改了。

描述:某数据库为提升搜索效率,对某一整型字段构建二叉搜索树(BST)。每个结点包含两个数据信息: 1)结点的数据,2) 该结点子树的元素数目。为了压缩搜索树规模,该数据库为每个结点增加一个字段,该字段用于存储中序遍历时在访问该结点之前访问的结点数据。在该改进下,被存储的结点如果为叶子结点,该结点在新树中将被删除,以提高存储效率。如果一个叶子结点无中序后继,则无需删除。

给定该BST的先序遍历(第二个字段未给出),请编写程序,输出经过压缩后新BST的先序遍历结果。

某数据库为提升搜索效率,对某一整型字段构建二叉搜索树(BST)_第1张图片

 输入格式
输入共两行:第一行为一个int数据n,表示该BST的总结点个数。1<=n<=100000:第二行为n个int数据,为该BST的先序遍历结果(保证顺序是正确的,并保证数据两两不同)。 每个数据的范围:0<=X<=1*10^7。

输出格式
输出共一行:第一行为新BST的先序遍历结果,依序输出结点数据以及保存的其他结点数据(若没有则输出字符-)

某数据库为提升搜索效率,对某一整型字段构建二叉搜索树(BST)_第2张图片

 某数据库为提升搜索效率,对某一整型字段构建二叉搜索树(BST)_第3张图片

 

#include 
#include 
#include 
#include 

using namespace std;

struct Node
{
    int val, childNodes = 0;
    Node* left, *right;
    int left_type = 0;//类型标志
	int right_type = 0;//类型标志
    Node(int val, int childNodes):val(val),childNodes(childNodes),left(nullptr), right(nullptr) {}
};


// 根据先序遍历,创建一个二叉搜索树
Node* createBstFromPreOrder(vector& preOrderNums, int left, int right) {
    if (left > right) {
        return nullptr;
    }
    Node* root = new Node(preOrderNums[left], right - left);
    // 如果没有找到,mid为最右边值大1,这样左边的右边界为mid-1刚好就是数组最右边
    int mid = right+1;
    for (int i = left + 1; i <= right; i++) {
        if (preOrderNums[i] > preOrderNums[left]) {
            mid = i;
            break;
        }
    }
    root->left = createBstFromPreOrder(preOrderNums, left+1, mid-1);
    root->right = createBstFromPreOrder(preOrderNums, mid, right);
    return root;
}

// 线索化时的前一个结点
Node* pre = nullptr;
map mp_delete;

//中序对二叉树进行线索化
void InThreading(Node* p){
    //如果当前结点存在
    if (p) {
        InThreading(p->left);//递归当前结点的左子树,进行线索化
        //如果当前结点没有左孩子,左标志位设为1,左指针域指向上一结点 pre
        if (!p->left) {
            p->left_type = 1;
            p->left=pre;
        }
        //如果 pre 没有右孩子,右标志位设为 1,右指针域指向当前结点。
        if (pre&&!pre->right) {
            pre->right=p;
            pre->right_type = 1;
            p->childNodes = pre->val;
            // 表示需要删除的节点
            cout << "需要删除的节点:" << pre->val << endl; 
            mp_delete[pre->val] = 1;
        }
        pre=p;//pre指向当前结点
        InThreading(p->right);//递归右子树进行线索化
    }
}

void preorderBst(Node *root)
{
    if(root != NULL)
    {
        Node *p = root;
        while (p != NULL)
        {
            while (p->left_type == 0) // 左指针不是线索,则边访问边左移
            {
                cout << p->val << " " << p->childNodes << endl; // 访问结点
                p = p->left; // 左移,访问左子树
            }
            cout << p->val << " " << p->childNodes << endl; // 此时p左必为线索,但还没有被访问,则访问
            p = p->right; // 此时p左孩子不存在,则右指针若非空,则不论是否为线索都指向其后继
        }
    }
}

Node *Next(Node *t) //已知节点t找t的"后继"结点位置 
{   
	if(t->right_type==1) //右标志为1,可以直接得到"后继"结点 
	{
		t=t->right;
	 } 
	 else /*右标志为0,不能直接的到"后继"结点,
	        则需要找到右子树最左下角的节点*/ 
	{
	 	t=t->right;
		 while(t->left_type==0)
		{
		 	t=t->left;
		 } //while
	}//else
	return t;
} 

// 改方法用来遍历线索二叉树,当删除相关节点之后,线索被取消了,这个方法不能用
void InorderTraverse(Node *temp)//利用线索实现中序遍历 
{
	if(!temp)
	{
		return;
	}
	
	while(temp->left_type==0)//查找第一个节点 
	{               //因为二叉树的创建creat是以先序遍历序列创建,所以t所指向的第一个结点并不是中序遍历所要访问的第一个结点 
		temp=temp->left;
	}
	cout << "val:" << temp->val << " left_type:" << temp->left_type 
             << " right_type:" << temp->right_type << " childNodes:" << temp->childNodes << endl;
	while(temp->right)// 此处以"t的右孩子不为空"为循环条件,是因为,先前设定了最后一个结点的"后继"为空,表示结束 
	{               //根据线索访问后续结点 
		temp=Next(temp);
		cout << "val:" << temp->val << " left_type:" << temp->left_type 
             << " right_type:" << temp->right_type << " childNodes:" << temp->childNodes << endl;
	}
} 

// 用mp记录第一次出现时,用来向左走时判断,如果之前出现过,则不向走了,表是左边已经走过了
map mp;
// 删除相关节点,将线索取消掉
void deleteBstNode(Node* root) {
    if (root == nullptr) return;
    stack stk;
    Node* p = root;
    while (p != nullptr || stk.size()) {
        // 当没有出现过,且左边有孩子
        while(p->left && p->left->left_type == 0 && mp[p->val] == 0) {
            stk.push(p);
            mp[p->val] = 1;
            p = p->left;
        }
        // 如果左边的孩子拥有前驱结点,则取消前驱标志
        if (p->left && p->left->left_type == 1 && p->left->left && mp[p->val] == 0) {
            stk.push(p);
            mp[p->val] = 1;
            // 将其中序的前一个结点置为-1,标志为“-”
            p->childNodes = -1;
            p = p->left;
            p->left = nullptr;
            p->left_type = 0;
        }
        // 表示左边需要删除
        else if (p->left && mp[p->val] == 0) {
            cout << "删除了---" << p->left->val << endl;  //2
            p->left->right = nullptr;
            p->left = nullptr;
        }
        // 如果右边没有了,或者刚好右边有一个需要删除
        if((p->right == nullptr && stk.size()) || (p->right && p->right->right_type == 1)) {
            while ((p->right == nullptr && stk.size()) || (p->right && p->right->right_type == 1)) {
                // 右边需要删除
                if (p->right && p->right->right_type == 1) {
                    cout << "删除了+++" << p->right->val << endl; // 5
                    p->right->right = nullptr;
                    p->right = nullptr;
                } else if(p->right){
                    // 不需要删除
                    p = p->right;
                    break;
                }
                p = stk.top();
                stk.pop();
            }
            // 不满足上述条件时,将p右移动
            if (p->right && p->right->right_type == 0) {
                p = p->right;
            } 
        } else if (p->right && p->right->right_type == 0) {
            p = p->right;
        } else {
            // 最后不满足时一定要返回
            return;
        }
    }
   
}

// 二叉树递归先序遍历
void preOrder(Node* root) {
    if (root == nullptr) {
        return;
    }
    cout << root->val << " " << root->childNodes << " " << endl;;
    preOrder(root->left);
    preOrder(root->right);
}

// 二叉树递归中序遍历
void inOrder(Node* root) {
    if (root == nullptr) {
        return;
    }
    inOrder(root->left);
    cout << root->val << " " << root->childNodes << " " << endl;;
    inOrder(root->right);
}

// 二叉树递归后续遍历
void postOrder(Node* root) {
    if (root == nullptr) {
        return;
    }
    postOrder(root->left);
    postOrder(root->right);
    cout << root->val << " " << root->childNodes << " " << endl;;
}

// 下面这个访问线索二叉树方法有问题
// void inOrderTraverse(Node* root)
// {
// 	//从根节点开始先找到最左边
// 	if (root == NULL)
// 	{
// 		return;
// 	}
// 	Node* temp = root;
// 	//先找到最左边结点 然后根据线索化直接向右遍历
// 	while (temp != NULL && temp->left_type == 0)
// 	{
// 		temp = temp->left;
// 	}
// 	while (temp != NULL)
// 	{
// 		//输出
//         cout << "val:" << temp->val << " left_type:" << temp->left_type 
//              << " right_type:" << temp->right_type << " childNodes:" << temp->childNodes << endl;
// 		temp = temp->right;
// 	}
// }

int main() {
    vector nums = {6, 4, 2, 5, 9, 7, 8};
    Node * head = createBstFromPreOrder(nums, 0, nums.size()-1);
    preOrder(head);
    cout << "=====================================" << endl;
    inOrder(head);
    cout << "=====================================" << endl;
    postOrder(head);
    cout << "=====================================" << endl;
    // 线索化二叉树
    InThreading(head);
    // 删除相关节点
    deleteBstNode(head);
    // 遍历二叉树
    preOrder(head);
    cout << "=====================================" << endl;
    cin.get();
    return 0;
}

你可能感兴趣的:(算法)