伸展树的读书笔记

         最近开始了自己高级数据结构之旅,在这次旅行中,我将持续把一些高级的数据结构从理论到编码都过一遍,同时通过博客形式分享出来,希望大家指出不足之处!

       二叉排序树是一种动态排序的数据结构,支持插入、删除、查找等操作,且平均时间复杂度为O(log(N)),但是普通二叉排序树不能保证树退化为一颗分支的情况,此时最坏情况下的时间复杂度为O(N)。此时,平衡二叉树的产生了。平衡二叉树是一种动态调整平衡的数据结构,但理想的平衡二叉树很难,于是人们使用AVL、红黑树、Treap、伸展树等来替代平衡二叉树,这些数据结构可以很好地改善最坏情况。但实现起来并不是很容易的事。

        伸展树较其他的数据结构,有着明显的特点。与纯的二叉查找树比起来,伸展树的查找、插入、删除等操作的平摊时间复杂度O(log(N)),且与AVL、红黑树比起来实现简单、空间效率高。关于伸展树的具体原理,可以参考算法合集之《伸展树的基本操作与应用》,这是国家队大牛写的,可读性很强。

       伸展树的基本原理是80—20原则,学计算机的人对这个原则的内容肯定不陌生:计算机80%的数据访问在集中在20%的数据上,如存储器中的Cache就是用的这个原理,并且效果很不错。伸展树正是利用这样的设计思想来进行的,将当前操作的数据通过伸展操作移到根节点,在这个过程中保持二叉查找树的性质,从而是整个过程的平摊时间复杂度降到O(log(N))。当然,对于单个操作,时间复杂度可能到达O(N),但基于整体考虑以及计算机特定规律,伸展树还是有着很好的效率的。在伸展的过程中,主要涉及两个旋转操作,左旋或右旋。在设计算法时,我们通常将时间复杂度放在首位,这点无可厚非,但有时我们的结合具体题目要求,在时间复杂度、空间复杂度、编程实现难度间寻找平衡,于是伸展树被认为是拥有很重要现实价值的”平衡二叉树“。

                         

                                           伸展树的读书笔记_第1张图片           

      上面从左到右为右旋,从右到左为左旋,其他所有操作都是由这两种基本操作组合起来的。如下

伸展树的读书笔记_第2张图片伸展树的读书笔记_第3张图片

       以上3副图包含了伸展树所有的Splay操作,而基本的左旋右选得到。

      普通伸展树支持插入、删除、修改、查询等操作。关于伸展树,有自顶向下和自底向上两种实现方法,这里我选用的是自底向上的实现方法,由于在节点中有父节点,故不用记录路径。

下面贴上我的代码:

//伸展树节点头文件SplayTreeNode.h

#include<iostream>
using namespace std;

/**********************************
*功能:防止头文件多次包含
**********************************/
#ifndef SPLAYTREENODE_H
#define SPLAYTREENODE_H

class SplayTreeNode
{
public:
	SplayTreeNode *leftChild;
	SplayTreeNode *rightChild;
	SplayTreeNode *parent;
	int key;
	SplayTreeNode(int key)
	{
		this->key=key;
		this->leftChild=NULL;
		this->rightChild=NULL;
		this->parent=NULL;
	}
};

#endif SPLAYTREENODE_H


 

//伸展树源文件SplayTree.cpp

#include<iostream>
#include"SplayTreeNode.h"
using namespace std;

class SplayTree
{
private:
	SplayTreeNode *root;
	void LeftRotate(SplayTreeNode *);
	void RightRotate(SplayTreeNode *);
	void Splay(SplayTreeNode *);
	void PreOrderSTPrint(SplayTreeNode *);
	void InOrderSTPrint(SplayTreeNode *);
	void RotateSTPrint(SplayTreeNode *,int);
	void SufOrderSTPrint(SplayTreeNode *);
	void DeleteNoOrOneChildSTNode(SplayTreeNode *,SplayTreeNode *);
public:
	SplayTree();
	void InsertSplayTree(int);
	bool DeleteSplayTree(int);
	bool UpdataSplayTree(int,int);
	SplayTreeNode *FindSplayTree(int);
	void PreOrderSTPrint();
	void InOrderSTPrint();
	void RotateSTPrint();
	void SufOrderSTPrint();
};

SplayTree::SplayTree()
{
	this->root=NULL;
} 

/**************************************************************
*参数:待左旋的节点
*返回值:空
*功能:左旋
***************************************************************/
void SplayTree::LeftRotate(SplayTreeNode *tempSTNode)
{
	SplayTreeNode *rChild=tempSTNode->rightChild;
	if(NULL!=tempSTNode->parent)//不为根节点
	{
		if(tempSTNode->parent->leftChild==tempSTNode)
			tempSTNode->parent->leftChild=rChild;
		else 
			tempSTNode->parent->rightChild=rChild;
	}
	rChild->parent=tempSTNode->parent;
	tempSTNode->parent=rChild;
	if(rChild->leftChild!=NULL)
		rChild->leftChild->parent=tempSTNode;
	tempSTNode->rightChild=rChild->leftChild;
	rChild->leftChild=tempSTNode;
	if(NULL==rChild->parent)
		this->root=rChild;
}

/**************************************************************
*参数:待右旋的节点
*返回值:空
*功能:右旋
***************************************************************/
void SplayTree::RightRotate(SplayTreeNode *tempSTNode)
{
	SplayTreeNode *lChild=tempSTNode->leftChild;
	if(NULL!=tempSTNode->parent)//不为根节点
	{
		if(tempSTNode->parent->rightChild==tempSTNode)
			tempSTNode->parent->rightChild=lChild;
		else 
			tempSTNode->parent->leftChild=lChild;
	}
	lChild->parent=tempSTNode->parent;
	tempSTNode->parent=lChild;
	if(lChild->rightChild!=NULL)
		lChild->rightChild->parent=tempSTNode;
	tempSTNode->leftChild=lChild->rightChild;
	lChild->rightChild=tempSTNode;
	if(NULL==lChild->parent)
		this->root=lChild;
}

/**************************************************************
*参数:待伸展的节点
*返回值:空
*功能:将当前节点伸展的根节点
***************************************************************/
void SplayTree::Splay(SplayTreeNode *tempSTNode)
{
	while(NULL!=tempSTNode&&NULL!=tempSTNode->parent)
	{
		if(tempSTNode->parent->leftChild==tempSTNode)//父亲节点右旋
			RightRotate(tempSTNode->parent);
		else LeftRotate(tempSTNode->parent);
	}
}

/**************************************************************
*参数:带插入元素
*返回值:空
*功能:将当前元素插入伸展树
***************************************************************/
void SplayTree::InsertSplayTree(int tempKey)
{
	SplayTreeNode *pre=NULL,*cur=this->root;
	while(cur!=NULL)
	{
		pre=cur;
		if(cur->key>tempKey)//tempKey插到左子树
			cur=cur->leftChild;
		else cur=cur->rightChild;//插到左子树
	}
	SplayTreeNode *tempSTNode=new SplayTreeNode(tempKey);
	tempSTNode->parent=pre;
	if(pre==NULL)//若插入的为根节点
	{
		this->root=tempSTNode;
	}
	else if(pre->key>tempSTNode->key)
		pre->leftChild=tempSTNode;
	else pre->rightChild=tempSTNode;
	Splay(tempSTNode);
}

/**************************************************************
*参数:带查找元素
*返回值:返回查找元素在伸展树中的位置
*功能:查找当前元素是否在伸展树
***************************************************************/
SplayTreeNode *SplayTree::FindSplayTree(int tempKey)
{
	SplayTreeNode *cur=this->root;
	while(cur!=NULL)
	{
		if(cur->key==tempKey)
			break;
		else if(cur->key>tempKey)
			cur=cur->leftChild;
		else cur=cur->rightChild;
	}
	Splay(cur);
	return cur;
}

/**********************************************************
*参数:pre待删除节点的父节点,cur待删除节点
*返回值:空
*功能:删除左右孩子有为空的情况
************************************************************/
void SplayTree::DeleteNoOrOneChildSTNode(SplayTreeNode *pre,SplayTreeNode *cur)
{
	if(NULL==cur->leftChild&&NULL==cur->rightChild)//左右孩子为空
	{
		if(NULL==pre)
			this->root=NULL;
		else if(pre->leftChild==cur)
			pre->leftChild=NULL;
		else pre->rightChild=NULL;
		delete cur;
	}
	else if(cur->rightChild!=NULL)//若右子树不为空
	{
		if(NULL==pre)
		{
			this->root=cur->rightChild;
			cur->rightChild->parent=NULL;
		}
		else if(pre->leftChild==cur)
		{
			pre->leftChild=cur->rightChild;
			cur->rightChild->parent=pre;
		}
		else 
		{
			pre->rightChild=cur->rightChild;
			cur->rightChild->parent=pre;
		}
		delete cur;
	}
	else if(cur->leftChild!=NULL)//若左子树不为空
	{
		if(NULL==pre)
		{
			this->root=cur->leftChild;
			cur->leftChild->parent=NULL;
		}
		else if(pre->leftChild==cur)
		{
			pre->leftChild=cur->leftChild;
			cur->leftChild->parent=pre;
		}
		else
		{
			pre->rightChild=cur->leftChild;
			cur->leftChild->parent=pre;
		}
		delete cur;
	}
}


/**********************************************************
*参数:待删除节点元素
*返回值:空
*功能:删除元素主函数
************************************************************/
bool SplayTree::DeleteSplayTree(int tempKey)
{
	SplayTreeNode *pre=NULL,*cur=root;
	while(cur!=NULL)//寻找待删除元素
	{
		if(cur->key==tempKey)
			break;
		else
		{
			pre=cur;
			if(cur->key>tempKey)
				cur=cur->leftChild;
			else cur=cur->rightChild;
		}
	}
	if(NULL==cur)return false;
	if(NULL==cur->leftChild||NULL==cur->rightChild)
	{
		DeleteNoOrOneChildSTNode(pre,cur);
		Splay(pre);
	}
	else //左右子树都不为空
	{
		SplayTreeNode *rPre=cur,*rCur=cur->rightChild;//找到右子树最小元素
		while(rCur->leftChild!=NULL)
		{
			rPre=rCur;
			rCur=rCur->leftChild;
		}
		cur->key=rCur->key;
		DeleteNoOrOneChildSTNode(rPre,rCur);
		Splay(rPre);
	}
	return true;
}

/**********************************************************
*参数:待修改节点元素、修改后的元素
*返回值:返回修改是否成功
*功能:修改函数
************************************************************/
bool SplayTree::UpdataSplayTree(int oldKey,int newKey)
{
	if(DeleteSplayTree(oldKey))
	{
		InsertSplayTree(newKey);
		return true;
	}
	return false;
}

/**********************************************************
*参数:当前子树根节点
*返回值:空
*功能:前序遍历二叉查找树
************************************************************/
void SplayTree::PreOrderSTPrint(SplayTreeNode *tempSTNode)
{
	if(NULL==tempSTNode)
		return ;
	cout<<tempSTNode->key<<"    ";
	PreOrderSTPrint(tempSTNode->leftChild);
	PreOrderSTPrint(tempSTNode->rightChild);
}
void SplayTree::PreOrderSTPrint()
{
	PreOrderSTPrint(this->root);
}

/**********************************************************
*参数:当前子树根节点
*返回值:空
*功能:中序遍历二叉查找树
************************************************************/
void SplayTree::InOrderSTPrint(SplayTreeNode *tempSTNode)
{
	if(NULL==tempSTNode)
		return ;
	InOrderSTPrint(tempSTNode->leftChild);
	cout<<tempSTNode->key<<"   ";
	InOrderSTPrint(tempSTNode->rightChild);
}
void SplayTree::InOrderSTPrint()
{
	InOrderSTPrint(this->root);
}

/**********************************************************
*参数:当前子树根节点
*返回值:空
*功能:后序遍历二叉查找树树
************************************************************/
void SplayTree::SufOrderSTPrint(SplayTreeNode *tempSTNode)
{
	if(NULL==tempSTNode)
		return ;
	SufOrderSTPrint(tempSTNode->leftChild);
	SufOrderSTPrint(tempSTNode->rightChild);
	cout<<tempSTNode->key<<"    ";
}
void SplayTree::SufOrderSTPrint()
{
	SufOrderSTPrint(this->root);
}

/**********************************************************
*参数:当前子树根节点,缩进列数
*返回值:空
*功能:翻转打印伸展树
************************************************************/
void SplayTree::RotateSTPrint(SplayTreeNode *tempSTNode,int tempColumn)
{
	if(NULL==tempSTNode)
		return ;
	RotateSTPrint(tempSTNode->leftChild,tempColumn+1);
	for(int i=0;i<tempColumn;i++)
		cout<<"    ";
	cout<<"---"<<tempSTNode->key<<endl;
	RotateSTPrint(tempSTNode->rightChild,tempColumn+1);
}
void SplayTree::RotateSTPrint()
{
	RotateSTPrint(this->root,0);
}


void Menu()
{
	int val,choice,newVal;
	SplayTree mySplayTree;
	while(true)
	{
		do
		{
			cout<<"&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&"<<endl;
			cout<<"       1.插入"<<endl;
			cout<<"       2.删除"<<endl;
			cout<<"       3.修改"<<endl;
			cout<<"       4.查找"<<endl;
			cout<<"       5.显示"<<endl;
			cout<<"       6.返回"<<endl;
			cout<<"请输入你的选项[ ]\b\b";
			cin>>choice; 
		}while(choice!=1&&choice!=2&&choice!=3&&choice!=4&&choice!=5&&choice!=6);
		if(1==choice)
		{
			cin>>val;
			mySplayTree.InsertSplayTree(val);
		}
		else if(2==choice)
		{
			cin>>val;
			if(mySplayTree.DeleteSplayTree(val))
				cout<<"删除成功!"<<endl;
			else cout<<"删除失败!"<<endl;
		}
		else if(3==choice)
		{
			cin>>val>>newVal;
			if(mySplayTree.UpdataSplayTree(val,newVal))
				cout<<"修改成功!"<<endl;
			else cout<<"修改失败!"<<endl;
		}
		else if(4==choice)
		{
			cin>>val;
			if(NULL!=mySplayTree.FindSplayTree(val))
				cout<<"查找成功!"<<endl;
			else cout<<"查找失败!"<<endl;
		}
		else if(5==choice)
		{
			cout<<endl<<"*****************************"<<endl;
			cout<<endl<<"==========前序=============="<<endl;
			mySplayTree.PreOrderSTPrint();
			cout<<endl<<"==========中序================"<<endl;
			mySplayTree.InOrderSTPrint();
			cout<<endl<<"==========后续==============="<<endl;
			mySplayTree.SufOrderSTPrint();
			cout<<endl<<"==========对称+旋转==============="<<endl;
			mySplayTree.RotateSTPrint();
			cout<<endl<<"*****************************"<<endl;
		}
		else return ;
	}
}


int main()
{
	while(true)
		Menu();
	system("pause");
	return 0;
}

 

关于伸展树的其他操作,后续在其他博客中补上。由于时间有限,缺乏测试,可能有错,欢迎大家斧正!

 

 

 

你可能感兴趣的:(二叉查找树,平衡二叉树,伸展树)