树基础总结(算法导论)

0.树的存储结构

a.当孩子个数有限时(全部单列出来)

struct Tree
{
	int data;
	Tree *ltree;
	Tree *rtree;
};
b.当孩子个数不定时(将孩子节点连在一起)
struct Node
{
	int data;
	vector child;//孩子节点的序号 
};
vector tree; 
c.对于完全/满二叉树(可以利用,父亲节点序号i,左孩子2i,右孩子2i+1的性质)
vector data;//data[0]不存储元素 

d.类似于并查集

vector node;//整棵树的节点 
node[childId]=fatherId; 

1.树的遍历

a.前、中、后序遍历

struct Tree
{
	int data;
	Tree *ltree;
	Tree *rtree; 
};
//前序
void preTraversal(Tree *head)
{
	if(head==NULL)
	{
		return;
	}
	cout<data;
	preTraversal(head->ltree);
	preTraversal(head->rtree);
} 
//中序
void preTraversal(Tree *head)
{
	if(head==NULL)
	{
		return;
	}
	preTraversal(head->ltree);
	cout<data;
	preTraversal(head->rtree);
}
//后序
void preTraversal(Tree *head)
{
	if(head==NULL)
	{
		return;
	}
	preTraversal(head->ltree);
	preTraversal(head->rtree);
	cout<data;
}
b.广、深度遍历
struct Tree
{
	int data;
	vector chlid;
};
//广度优先
void breadthTraversal(Tree *head)
{
	queue q;
	q.push(head);
	while(!q.empty())
	{
		Tree *temp=q.front();
		q.pop();
		cout<data;
		for(int i=0;ichlid.size();i++)
		{
			q.push(temp->chlid[i]);
		}
	}
}
//深度优先
void depthTraversal(Tree *head)
{
	if(head==NULL)
	{
		return;
	}
	cout<data;
	for(int i=0;ichlid.size();i++)
	{
		depthTraversal(temp->chlid[i]);
	}
} 

2.树的应用

a.二叉查找树

#include
#include
#include
using namespace std;

template
struct Node
{
	Type cdata;
	Node *lchild;
	Node *rchild;
	Node(Type temp):cdata(temp),lchild(NULL),rchild(NULL){}//跟类初始化列表一样 
};

template
class BSTree
{
	private:
		Node *root;//这里写Node *root是错的,需要给结构体模板传参Node *root
		void destroy();
	public:
		BSTree():root(NULL){} //构造函数,初始化 
		BSTree(BSTree &c)//重载复制构造函数,防止浅拷贝 
		{
			if(c.getRoot()!=NULL)
			{
				queue< Node* > n1;
				queue< Node* > n2;
				root=new Node(c.getRoot()->cdata);
				Node *storage=root;
				n1.push(c.getRoot());
				n2.push(root);
				while(!n1.empty())//层次 
				{
					Node *te=n1.front();
					Node *te2=n2.front();
					n1.pop();
					n2.pop();
					if(te->lchild!=NULL)
					{
						te2->lchild=new Node(te->lchild->cdata);
						n1.push(te->lchild);
						n2.push(te2->lchild);
					}
					if(te->rchild!=NULL)
					{
						te2->rchild=new Node(te->rchild->cdata);
						n1.push(te->rchild);
						n2.push(te2->rchild);
					}
				}	
				root=storage;
			}	
		}
		~BSTree()//析构函数,回收内存 
		{
			destroy();
		}
		Node* getRoot()
		{
			return root;
		}
		bool insertNode(Node* t);//增 
		Node *findX(T x)const;//查 
		void removeX(T x);//删 
		void midtraversal(Node *te)const;
		bool isTreeEmpty()const;
};

template
inline bool BSTree::insertNode(Node *t)
{
	if(root==NULL)
	{
		root=t;
		return true;
	}
	Node *te=root;
	while(true)
	{
		if(t->cdatacdata)
		{
			if(root->lchild!=NULL)
			{
				root=root->lchild;
			}
			else
			{
				root->lchild=t;
				root=te;
				return true;
			}	
		}
		else if(t->cdata>root->cdata)
		{
			if(root->rchild!=NULL)
			{
				root=root->rchild;
			}
			else
			{
				root->rchild=t;
				root=te;
				return true;
			}
		}
		else
		{
			root=te;
			return false;
		}
	}
}

template
inline void BSTree::destroy()
{
	cout<<"delete: ";
	if(root==NULL)
	{
		return;
	}
	stack< Node* > s;
	s.push(root);
	while(!s.empty())//先序 
	{
		if(s.top()->lchild==NULL&&s.top()->rchild==NULL)
		{		
			Node *te=s.top();
			s.pop();
			cout<cdata<<"\t"; 
			delete te;
		}
		else
		{
			while(s.top()->lchild!=NULL)
			{
				Node *te=s.top()->lchild;
				s.top()->lchild=NULL;
				s.push(te);
			}
			while(s.top()->rchild!=NULL)
			{
				Node *te=s.top()->rchild;
				s.top()->rchild=NULL;
				s.push(te);
			}
		}
	}
}

template
inline bool BSTree::isTreeEmpty()const//BSTree::isTreeEmpty()是错的,类模板需要传参BSTree::isTreeEmpty() 
{
	if(root==NULL)
	{
		return true;
	}	
	return false;
}  

template
inline void BSTree::midtraversal(Node * te)const//中序 
{
	if(te==NULL)
	{
		return;
	}
	BSTree::midtraversal(te->lchild);
	cout<cdata<<"(ad: "<::midtraversal(te->rchild);	
}

template
inline Node* BSTree::findX(T x)const
{
	Node* storage=root;
	while(storage!=NULL)
	{
		if(xcdata)
		{
			storage=storage->lchild;
		}
		else if(x>storage->cdata)
		{
			storage=storage->rchild;
		}
		else
		{
			return storage;
		}
	}
	return storage;
}

template
inline void BSTree::removeX(T x)
{
	Node *te=findX(x);
	if(te==NULL)//情况一:不存在x,直接返回 
	{
		return;
	}
	if(te->lchild==NULL&&te->rchild==NULL)//情况二:叶子节点,直接删 
	{
		delete te;//调用指针的析构函数,来释放对象内存 
		return; 
	}
	if(te->lchild==NULL&&te->rchild!=NULL)//情况三:单子树,直接删,让子树代替原节点
	{
		te->cdata=te->rchild->cdata;
		te->lchild=te->rchild->lchild;
		te->rchild=te->rchild->rchild;
		delete te->rchild;	
		return;
	} 
	if(te->lchild!=NULL&&te->rchild==NULL)
	{
		te->cdata=te->lchild->cdata;
		te->lchild=te->lchild->lchild;
		te->rchild=te->lchild->rchild;
		delete te->lchild;
		return;	
	} 
	if(te->lchild!=NULL&&te->rchild!=NULL)//情况四:双子树,让直接后继代替原节点,直接后继是叶子节点直接删,有单子树,按上操作 
	{
		//找直接后继,如找9的直接后继,就是找9的右子树中比9大的最小数
		Node *te2=te->rchild;
		while(te2->lchild!=NULL)
		{
			te2=te2->lchild;
		} 
		//直接后继代替原节点 
		te->cdata=te2->cdata;
		//删除直接后继
		if(te2->rchild==NULL)
		{
			delete te2;	
			return;	
		}  
		if(te2->rchild!=NULL)
		{ 
			te2->cdata=te2->rchild->cdata;
			te2->lchild=te2->rchild->lchild;
			te2->rchild=te2->rchild->rchild;
			delete te2->rchild;
			return;
		}
	} 
}

int main()
{
	{
		int c[]={3,9,-1,4,2,7,9,19,10,12}; 
		BSTree a;
		for(int i=0;i<10;i++)
		{
			a.insertNode(new Node(c[i]));
		}
		a.midtraversal(a.getRoot());
		cout<cdata< b(a);
		b.midtraversal(b.getRoot());
		cout<

思考:在构建二叉查找树之前,对输入数据进行洗牌,会不会更好。但是如果数据不是像例子中一次性给出,那么也就没办法对输入数据进行洗牌了。
b.红黑树

是一种二叉查找树,也是一种平衡树。典型的用途是实现关联数组(c++中的map,set ?)

节点数据结构如下:

template
struct Node
{
	Type val;//节点存储数据 
	bool color;//true为红,false为黑 
	Node* lchild;
	Node* rchild;
	Node* parent;
	Node(Type temp):val(temp),color(true),lchild(NULL),rchild(NULL),parent(NULL){}	
};
成为一颗红黑树的条件:

1.节点非黑即红(对于分析没有实际意义)

2.根节点是黑色的(说不出来,但写插入时,很有用,情况四再次向上调整时有用,父亲节点是红色时,一定存在祖父节点的判定...模糊)

3.叶节点(空节点)是黑色的(为方便代码编写和算法实现)

4.红节点的子节点是黑色的(并不意味着每条路径上的节点都是黑红交替的)

5.对于每个节点,从该节点到其子孙叶节点(空节点)的所有路径上包含的黑节点数目是相同的

红黑树的平衡性讨论

由条件5可知:因为从一点出发,任何一条路径到叶节点所经历的黑节点个数是相同的,所以一条路径上的红节点个数越多,路径也就越长

由条件4和上条推论可知:因为红节点的子节点一定是黑的,意味着两个红节点是不可能连续,所以红节点最多时,就是在每两个黑节点之间插上1个,所以红节点最多与黑节点个数相同

由上可推出:

最长路径是红黑交替出现长度为2n(n为黑节点个数)

最短路径是纯黑节点长度为n

最长路径最多是最短路径的两倍,这保证了这棵树的平衡性。(当然对比严格的平衡二叉树来说,平衡性肯定是要差些的)

红黑树的插入:(中心思想:插入新节点,不能破坏性质1~5,这样才能保证树的平衡)

写的很好:http://www.cnblogs.com/fornever/archive/2011/12/02/2270692.html

情况1:根节点为空,插入节点设为根节点,color设为黑

情况2:已经存在该节点,直接返回

情况3:父亲节点为黑,插入后,无需调整,直接返回

情况4:父亲节点为红,且叔叔节点也为红,插入后,将父亲节点设为黑,叔叔节点设为黑,祖父节点(父亲节点为红,则至少有祖父root为黑)设为红,将祖父节点作为新节点,再次向上调整

情况5:父亲节点为红,叔叔节点为黑,父亲节点为左(右)孩子,新节点也为左(右)孩子,右(左)单旋,原父亲节点站在了原祖父节点的位置上,原父亲节点转黑,原祖父节点转红

情况6:父亲节点为红,叔叔节点为黑,父亲节点为左(右)孩子,新节点为右(左)孩子,左(右)旋,各节点颜色不变。现在成了情况5,转情况5处理

红黑树的删除:(中心思想:删除某个节点,要保证黑节点个数不变,这样性质5才不会被破坏)

写的很好:http://blog.csdn.net/spch2008/article/details/9338923

最重要的一点,以下结构会破坏性质5,所以不会出现(盗图):

树基础总结(算法导论)_第1张图片

详细参看上述博客

红黑树代码:(仅插入部分,删除真的好麻烦!)

#include
#include
using namespace std;

template
struct Node
{
	Type val;//节点存储数据 
	bool color;//true为红,false为黑 
	Node* lchild;
	Node* rchild;
	Node* parent;
	Node(Type x):val(x),color(true),lchild(NULL),rchild(NULL),parent(NULL){}	
};

template
class RBTree//析构函数和复制构造函数不再重载,大致与二叉查找树相同 
{
	private:
		Node* root;
		Node* findInsertPos(T x);//寻找插入点 
		bool judgeLR(T x,Node *y);//判断是左孩子(true)还是右孩子(false) 
		void adjust(Node *x);//插入节点之后需要调整 
	public:
		Node* getRoot()
		{
			return root;
		}
		void insertNode(T x);
		void midtraversal(Node *te)const;
		void leveltraversal()const;
};

template
inline void RBTree::leveltraversal()const
{
	cout<<"层次:"< *storage=root;
	queue< Node* > q;
	q.push(storage);
	while(!q.empty())
	{
		Node * te=q.front();
		q.pop();
		cout<val<<"("<color<<")\t";
		if(te->lchild!=NULL)
		{
			q.push(te->lchild);
		}
		if(te->rchild!=NULL)
		{
			q.push(te->rchild);
		}
	}
	cout<
inline void RBTree::midtraversal(Node *te)const
{
	if(te==NULL)
	{
		return;
	}
	RBTree::midtraversal(te->lchild);
	cout<val<<"("<color<<")"<<"\t";
	RBTree::midtraversal(te->rchild);
}

template
inline bool RBTree::judgeLR(T x,Node *y)
{
	if(xval)
	{
		return true;
	}
	else
	{
		return false;
	}
}

template
inline Node* RBTree::findInsertPos(T x)
{
	Node *storage=root;
	while(storage!=NULL)
	{
		if(xval)
		{
			if(storage->lchild!=NULL)
			{
				storage=storage->lchild;
			}
			else
			{
				return storage;
			}
		}
		else if(x>storage->val)
		{
			if(storage->rchild!=NULL)
			{
				storage=storage->rchild;
			}	
			else
			{
				return storage;
			}
		}	
		else
		{
			return NULL;
		}
	}
	return storage;
}

template
inline void RBTree::adjust(Node *x)	 
{
	if(x==root)//< 距离root0,就会直接返回了 >
	{
		x->color=false;
		return;
	}
	if(!x->parent->color)//情况三:父亲节点是黑色的,无需操作,直接返回<  距离root1,因为root是黑的,所以到此步也就返回了 > 
	{
		return;
	}
	//< 距离root2以上距离的,才会做以下操作,所以x->father->father不会非法 > 
	Node *f=x->parent;
	Node *u;
	bool fcolor=f->color;
	bool ucolor;
	bool fgchild=judgeLR(f->val,f->parent);
	bool mfchild=judgeLR(x->val,f); 
	if(fgchild)
	{
		u=f->parent->rchild;
	} 
	else
	{
		u=f->parent->lchild;
	}
	if(u==NULL)
	{
		ucolor=false;
	}  
	else
	{
		ucolor=u->color;
	}
	if(fcolor&ucolor)//情况四:父亲节点与叔叔节点都是红的,父亲节点和叔叔节点全改为黑,祖父节点改为红,把祖父节点当做新插入节点,继续调整 
	{
		f->color=false;
		u->color=false;
		f->parent->color=true;
		adjust(f->parent);
		return;
	}
	
	bool tag;
	bool gffchild=false;
	Node *g=f->parent;
	Node *gf=g->parent;
	if(gf==NULL)//g是root 
	{
		tag=true;
	}
	else
	{
		tag=false;
		gffchild=judgeLR(g->val,gf);
	}
	if(fgchild&mfchild)//情况五:父亲节点为红,叔叔节点为黑,父亲节点为左,我也为左,即同侧,左单旋 
	{
		g->parent=f;
		g->lchild=f->rchild;
		g->color=true;
		f->rchild=g;
		f->color=false;
		if(tag)
		{
			f->parent=NULL;
			root=f;
			return;
		}
		else
		{
			if(gffchild)
			{
				gf->lchild=f;
			}
			else
			{
				gf->rchild=f;
			}
			f->parent=gf;
			return;
		}
	}
	if((!fgchild)&(!mfchild))//情况五:父亲节点为红,叔叔节点为黑,父亲节点为右,我也为右,即同侧,右单旋 
	{
		g->parent=f;
		g->rchild=f->lchild;
		g->color=true;
		f->lchild=g;
		f->color=false;
		if(tag)
		{
			f->parent=NULL;
			root=f;
			return;
		}
		else
		{
			if(gffchild)
			{
				gf->lchild=f;
			}
			else
			{
				gf->rchild=f;
			}
			f->parent=gf;
			return;
		}
	}
	if(fgchild&(!mfchild))//情况六:父亲节点为红,叔叔节点为黑,父亲节点为左,我为右,单旋一次就成了情况五 
	{
		f->rchild=x->lchild;
		f->parent=x;
		g->lchild=x;
		x->parent=g;
		x->lchild=f;
		adjust(f);
		return;
	} 
	if((!fgchild)&mfchild)//情况六:父亲节点为红,叔叔节点为黑,父亲节点为右,我为左,单旋一次就成了情况五
	{
		f->lchild=x->rchild;
		f->parent=x;
		g->rchild=x;
		x->parent=g;
		x->rchild=f;
		adjust(f);
		return;
	}
}

template
inline void RBTree::insertNode(T x)
{
	if(root==NULL)//情况一:树空,根节点为黑 
	{
		root=new Node(x);
		root->color=false;
		return;
	}
	if(findInsertPos(x)==NULL)//情况二:已经存在,无需插入,直接返回 
	{
		return;
	}
	
	/*****下列情况,考查的只有,本身、爹、叔叔的颜色、左右身份*****/ 
	/*****下列情况,先插入,再调整*****/
	Node *f=findInsertPos(x);
	bool mfchild=judgeLR(x,f);
	if(mfchild)
	{
		f->lchild=new Node(x);
		f->lchild->parent=f;
		adjust(f->lchild);
	} 
	else
	{
		f->rchild=new Node(x);
		f->rchild->parent=f;
		adjust(f->rchild);
	}
}
int main()
{
	int c[]={3,9,-1,4,2,7,91,19,10,12,9};
	RBTree a;
	for(int i=0;i<11;i++)
	{
		a.insertNode(c[i]);
	}
	cout<<"中序:"<

红黑树与严格的AVL树(高度平衡二叉树)(考研时数构考过)的比对:

转自:http://blog.csdn.net/klarclm/article/details/7780319

相对于AVL树而言,红黑树牺牲了严格的高度平衡的优越条件为 代价,红黑树能够以O(log n)的时间复杂度进行搜索、插入、删除操作。此外,由于它的设计,任何不平衡都会在三次旋转之内解决(参看插入操作)。当然,还有一些更好的,但实现起来更复杂的数据结构 能够做到一步旋转之内达到平衡,但红黑树能够给我们一个比较“便宜”的解决方案。

当然,红黑树并不适应所有应用树的领域。如果数据基本上是静态的,那么让他们待在他们能够插入,并且不影响平衡的地方会具有更好的性能。如果数据完全是静态的,例如,做一个哈希表,性能可能会更好一些。

c.哈夫曼树(编码)

#include
#include
#include
using namespace std;
struct Tree
{
	Tree* lchlid;
	Tree* rchlid;
	int f;//频率
	int data;//值 
};
struct cmp//与sort的cmp不同,结构体中重载调用操作符。也可以重载>操作符,priority_queue,greater> Q;
{
 	bool operator()(Tree a,Tree b)
 	{
 		return a.f>b.f;//>升序,<降序 
 	}
};
int random(int start,int end)
{
	return start+rand()%(end-start);
}
void HuffmanTree(priority_queue,cmp> &q) 
{
	while(q.size()>=2)
	{/*
		Tree a=q.top();
		cout<<"&a="<<&a<f<<"\t";
		}
		if(temp->lchlid!=NULL)
		{
			q.push(temp->lchlid);
		}
		if(temp->rchlid!=NULL)
		{
			q.push(temp->rchlid);
		}
	}
}
int main()
{
	int num;
	priority_queue,cmp> q;
	cin>>num;
	for(int i=0;i

d.并查集

初始化,查根,合并代码如下

#include
#define Maxv 200
using namespace std;
int data[Maxv];
//data[i]=j,元素i的祖宗是j
void initial(int data[])//初始化
{
	for(int i=0;i
并查集应用实例:畅通工程,Kruscal最小生成树

Kruscal最小生成树:

#include
#include
using namespace std;
#define Maxv 100+5
struct Node
{
	int v2;
	int v1;
	int len;
};
struct cmp
{
	bool operator()(Node a,Node b)
	{
		return a.len>b.len;
	}
};

int dis[Maxv][Maxv];//dis[i][j]等于0时表示不连通 ,不等于1时表示边权值 
int fa[Maxv];//father,并查集 

int Getfa(int i)//查找根节点的函数 
{
	if(fa[i]!=i)//如果不是根节点 
		fa[i]=Getfa(fa[i]);//找根节点 
	return fa[i];//返回节点i所在集合的根节点	
} 
int main()
{
   	 int sum;//最小生成树代价
     priority_queue,cmp> Q;//声明小顶堆,返回最小数 
 
 	 int vn;//图中的顶点个数 
	 int i;
	 int j;	  
 	 cin>>vn;
	//输入图
	 for(i=1;i<=vn;i++)
	 {
		for(j=1;j<=vn;j++)
		{
			cin>>dis[i][j];	
   		}
	 } 
	 for(i=1;i<=vn;i++)
	 {
		fa[i]=i;//并查集,father,一开始有vn个节点,就有vn个集合 
	 }
	 while(!Q.empty())
	 {
		Q.pop();
	 }
	 //把每条边压入堆中
	 for(i=1;i





你可能感兴趣的:(数据结构与基础算法)