[重修数据结构0x04]特殊二叉树:二叉查找树(BST)、平衡二叉树(AVL树)、哈夫曼树

前言

看了Acwing老师的一些课
**补充知识:访问vector最后一个元素用end() -1
而不是end();-------待补充。
** 完全二叉树的存储技巧,用数组,左孩子一定是2x,右孩子一定是2x+1
完全二叉树达到空结点的标志是当前结点root编号大于结点个数n 。

一.二叉查找树

1.1二叉查找树定义

二叉查找树有很多名字,还有排序二叉树,二叉搜索树等。
其定义如下:
①要么是空树。
②要么其左右子树都是二叉查找树,且左子树所有节点都小于等于根节点,右子树所有节点都大于等于根节点。

1.2二叉查找树上的操作

1.查找,遍历树

void search (node* root, int x )
{
	if(root == NULL)
		return ;
	if(x == root->data)
		cout<<"ok";
	else if(x < root->data)
		search(root->lchild,x);
	else
		search(root->rchild);
}

2.插入操作
同样是遍历的思路

void insert( node*  &root ,int x )
{
	if(root ==NULL)
	{
		root = newNode(x);
		return;  //   return很重要!一定要设置出口
	}
	if(x == root->data)
		return ;
	else if (x <= root ->data)
		insert(root->lchild,x);
	else
		insert(root ->rchild,x);
}
	

3.二叉查找树的建立

注意:同一组数据,输入顺序不同,建立的二叉查找树也不同

node* Create(int data[],int n)
{
	node* root = NULL;
	for(int i=0;i<n;i++)
		insert(root,data[i]);
	return root;
}

4.二叉查找树的删除

如图1,若删除根节点,可以考虑用比根小的最大结点4覆盖(后继),也可以考虑用比根大的最小结点6覆盖(前驱),首先建立find函数找到他们。
图1

node* findMax(node* root)
{
	while(root->rchild !=NULL)
		root = root->rchild;
	return root;
{
node* findMin(node* root)
{
	while(root->lchild != NULL)
		root = root->lchild;
	return root;
}
void deleteNode(node* &root ,int x)
{
	if(root == NULL) return ;
	if(root->data == x)
	{
		if(root->lchild == NULL && root->rchild == NULL)
			root = NULL ;
		else if (root-> lchild != NULL )
		{
			node* pre = findMax(root->lchild);
			root->data = pre->data;
			deleteNode(root->lchild,pre->data);
		}
		else
		{
			node* next = findMin(root->rchild);
			root->data = next ->data;
			deleteNode(root->rchild,next->data);
		}
		else if (root->data < x)
			delete(root->rchild,x);
		else if(root->data > x )
			delete(root->lchild,x);
}

1.3二叉查找树的性质

**对BTS进行中序遍历,结果是有序的!**

二.平衡二叉树

2.1 AVL树的定义

1.AVL仍然是一棵BST树,只是链式的BST树复杂度达到了O(n),只是增加了“平衡”
的要求:任意结点左子树和右子树高度差绝对值不超过1.
每个结点都有一个平衡因子,表示左子树高度减去右子树高度。
在这里插入图片描述
只要保证每个节点平衡因子不大于1,就能O(logn)级别。

struct node
{
	int v;  //权值
	int height; //当前子树高度
	node* lchild, rchild;
}

新建结点
node* newNode(int v)
{
	node* Node = new node;
	Node->v = v;
	Node->height = 1 ;
	Node->lchild = Node->rchild = NULL;
	return Node;
}

更新height
void updateHeight(node* root)
{
	root->height = max(getHeight(root->lchild),getHeight(root->rchild));
}
int getHeiht(node* root)
{
	if(root==NULL)
		return 0;
	return root->height;
}

计算结点平衡因子
int getbalanceFactor(node* root) 
{
	return getHeight(root->lchild)-getHeight(root-rchild);
}

2.2平衡二叉树的操作

1.查找操作,时间复杂度O(logn)
与上面BST查找方式一致。
2.插入操作

先定义左旋操作:假如要BST中的A结点和B结点互换,且交换之后仍是二叉查找树。
左旋步骤如下:
①让B的左子树成为A的右子树。
②让A成为B的右子树。
最后把根节点设置为B.
左旋

						心中有图,需要记忆
void L(node* &root)
{
	node* temp = root->rchild;  //temp就是B
	root->rchild = temp->lchild;  
	temp-lchild  = root ;
	updateHeight(root);
	updateHeight(temp);
	root = temp;
} 

右旋:
①让A的右子树成为B的左子树。
②让B成为A的右子树。
最后把根节点设置为B.

右旋

			                     记忆,心中有图
void R(node* &root)
{
	node* temp = root->lchild;
	root->lchild = temp->rchild;
	temp->rchild = root;
	update(root);
	update(temp);
	root = temp ;
}

现在考虑AVL树上插入节点之后失衡的情况。
共四种情况。
LL和LR
LL平衡化
LR平衡化
RR和RL失衡情况
RR平衡化
RL平衡化
总结

void insert(node* &root,int v)
{
	if(root == NULL)	
	{
		root = newNode(v);
		return ;
	}
	if(v<root->v)
	{
		insert(root->lchild,v);
		updateHeight(root);
		if(getBalanceFactor(root)==2)
		{
			if(getBalanceFactor(root->lchild)==1)
				R(root);
			else if(getBalanceFactor(root->lchild)==-1)
			{
				L(root->lchild);
				R(root);
			}
		}
	}
		else
	{
		insert(root->rchild,v);
		updateHeight(root);
		if(getBalanceFactor(root)==-2)
		{
			if(getBalanceFactor(root->rchild)==-1)
				L(root);
			else if(getBalanceFactor(root->rchild)==--1)
			{
				R(root->rchild);
				L(root);
			}
		}
	}

2.3 AVL树的建立

node* Create(int data[],int n)
{
	node* root = NULL;
	for(int i=0;i<n;i++)
		insert(root,data[i]);
	return root;
}
	

三.哈夫曼树

3.1哈夫曼树定义和实现

先看这么一个问题:合并果子

题意:有好几堆果子,要合并成一堆,每一次合并,多多可以把两堆果子合并到一起,
消耗的体力等于两堆果子的重量之和。所有的果子经过n-1次合并之后,就只剩下一堆了。
多多在合并果子时总共消耗的体力等于每次合并所耗体力之和。
求合成一堆消耗的最小体力。

哈夫曼树是采用了贪心的思想,每次那最小的两堆。
已知n个数,寻找一棵树,使得树的所有叶子节点的权值恰好为这n个数,并且使得这棵树
的带权路径长度最小。

#include <iostream>
#include <queue>
using namespace std;

priority_queue<int ,vector<int>,greater<int>> q;

int main()
{
   int n;
   int temp ,x,y;
   int ans = 0;
   cin>>n;
   for(int i=0;i<n;i++)
   {
       cin>>temp;
       q.push(temp);
   }
   while(q.size()>1)
   {
       x = q.top();
       ans = ans + x;
       q.pop();
       y = q.top();
       q.pop();
       ans = ans + y ;
       y = y + x;
       q.push(y);
   }
   cout<<ans;
    return 0;
}

四.练习

1.题目链接:UVA1525 Falling leaves

#include <iostream>
#include <cstring>
using namespace std;

struct node
{
    char data;
    node* lchild;
    node* rchild;
};
node* newNode(char c)
{
    node* Node = new node;
    Node->data = c;
    Node->lchild = Node->rchild = NULL;
    return Node;

}

void insert(node* &root,char c)
{
    if(root == NULL)
    {
        root = newNode(c);
        return ;
    }
    if(c < root->data)
        insert(root->lchild,c);
    else if(c > root->data)
        insert(root->rchild,c);
}

void preorder(node* root)
{
    if(root == NULL)
        return;
    cout<<root->data;
    preorder(root->lchild);
    preorder(root->rchild);
}

int main()
{
    string s1;
    while(1)
    {
        string s = "";
        while(cin>>s1&&s1[0]!='*'&&s1[0]!='$')
            s = s + s1;
        node* root = NULL;
        for(int i =s.size()-1;i>=0;i--)
            insert(root,s[i]);
        preorder(root);
        cout<<endl;
        if(s1[0]=='$')
            break;

    }
    return 0;
}

	          PTA1043 Is It a Binary Search Tree (25)
#include <iostream>
#include <vector>
using namespace std;
const int maxn = 1010;
int temp[maxn];
struct node
{
    int data;
    node* lchild;
    node* rchild;
}Node[maxn];

node* root = NULL;
vector<int> origin,pre,post,preM,postM;
node* newNode(int x)
{
    node* root = new node;
    root->data = x;
    root->lchild = NULL;
    root->rchild = NULL;
    return root;
}

void insert(node* &root,int x)
{
    if(root==NULL)
    {
       root =  newNode(x);
       return;
    }
    if(x<root->data)
        insert(root->lchild,x);
    else if(x>=root->data)
        insert(root->rchild,x);

}
void preorder(node* root,vector<int>& vi)
{
    if(root == NULL)
        return ;
    vi.push_back(root->data);
    preorder(root->lchild,vi);
    preorder(root->rchild,vi);

}
void postorder(node* root,vector<int>& vi)
{
    if(root == NULL)
        return;
    postorder(root->lchild,vi);
    postorder(root->rchild,vi);
    vi.push_back(root->data);
}
void premirro(node* root,vector<int>& vi)
{
    if(root==NULL)
        return;
    vi.push_back(root->data);
    premirro(root->rchild,vi);
    premirro(root->lchild,vi);
}
void postmirro(node* root,vector<int>& vi)
{
    if(root==NULL)
        return;
    postmirro(root->rchild,vi);
    postmirro(root->lchild,vi);
    vi.push_back(root->data);
}

int main()
{
    int n;
    cin>>n;
    for(int i=0;i<n;i++)
    {
        cin>>temp[i];
        origin.push_back(temp[i]);
        insert(root,temp[i]);
    }
    preorder(root,pre);
    postorder(root,post);
    premirro(root,preM);
    postmirro(root,postM);

    if(pre==origin)
    {
        cout<<"YES"<<endl;
        for(int i=0;i<post.size()-1;i++)
            cout<<post[i]<<' ';
        int t = post.size()-1;
        cout<<post[t];
    }
    else if(preM==origin)
    {
        cout<<"YES"<<endl;
        for(int i=0;i<postM.size()-1;i++)
            cout<<postM[i]<<' ';
        int t = postM.size()-1;
        cout<<postM[t];
    }
    else
        cout<<"NO";

    return 0;
}

	1064 Complete Binary Search Tree (30)
#include <iostream>
#include <algorithm>

using namespace std;
const int maxn = 1010;
int BST[maxn];
int num[maxn];
int n;
int index = 0;
void inorder(int root)
{
    if(root>n) return ;
    inorder(root*2);
    BST[root] = num[index++];
    inorder(root*2+1);
}
int main()
{
    cin>>n;
    for(int i=0;i<n;i++)
        cin>>num[i];
    sort(num,num+n);
    inorder(1);
    for(int i=1;i<n;i++)
        cout<<BST[i]<<' ';
    cout<<BST[n];
    return 0;
}
			1099 Build A Binary Search Tree (30)
#include <iostream>
#include <queue>
#include <algorithm>

using namespace std;

const int maxn = 110;
int tem[maxn];
int cnt = 0;
int cnt2 = 0;
int n;
struct node
{
    int data;
    int lchild;
    int rchild;
}Node[maxn];

void inorder(int root)
{
    if(root == -1)
        return;
    inorder(Node[root].lchild);
    Node[root].data = tem[cnt++];
    inorder(Node[root].rchild);
}

void BFS(int root)
{
    queue<int> q;
    q.push(root);

    while(!q.empty())
    {
        int t = q.front();
        cout<<Node[t].data;
        cnt2++;
        if(cnt2!=n)
            cout<<' ';
        q.pop();
        if(Node[t].lchild != -1) q.push(Node[t].lchild);
        if(Node[t].rchild != -1) q.push(Node[t].rchild);
    }
}

int main()
{
    cin>>n;
    for(int i=0;i<n;i++)
    {
        cin>>Node[i].lchild>>Node[i].rchild;
    }
    for(int i=0;i<n;i++)
        cin>>tem[i];
    sort(tem,tem+n);
    inorder(0);
    BFS(0);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值