Splay 伸展树

应该算比较精简了,不过 指针 和 指向指针的指针 实在头疼!

#include "stdio.h"
#include "stdlib.h"

typedef struct _Node{
	int val;
	int rev;
	int child;
	struct _Node* pre;
	struct _Node* ch[2];  //0,左孩子; 1,右孩子;
}Node, *pNode;

int n, m;
pNode root;

/*更新节点的孩子数*/
void update(pNode r){
	int lf, rt;
	lf = r->ch[0]?(r->ch[0]->child+1):0;
	rt = r->ch[1]?(r->ch[1]->child+1):0;
	r->child = lf+rt;
}

void insert(pNode* r, int data){
	pNode w = *r;
	if(!w){
		w = (pNode)malloc(sizeof(Node));
		w->val = data;
		w->child = 0;
		w->rev = 0;
		w->pre = 0;
		w->ch[0] = w->ch[1] = 0;
		*r = w;
		return;
	}
	if(data > w->val){
		insert(&(w->ch[1]), data);
		if(w->ch[1]->child==0) w->ch[1]->pre = w;
	}else{
		insert(&(w->ch[0]), data);
		if(w->ch[0]->child==0) w->ch[0]->pre = w;
	}
	update(w);
}

/*旋转,0为往左旋转,1为往右旋转。旋转要注意各种指针的更新,要仔细啊!*/
void rotate(pNode* r, int op){ //0,lf; 1,rt;
	pNode t, w = *r;
	t = w->ch[!op];  //获得左/右孩子
	w->ch[!op] = t->ch[op]; 
	if(t->ch[op]) t->ch[op]->pre = w;
	t->ch[op] = w;
	t->pre = w->pre;
	if(w->pre){  //更新w父节点的孩子指针
		if(w == w->pre->ch[0])
			w->pre->ch[0] = t;
		else
			w->pre->ch[1] = t;
	}else
		root = t;  //这句非常重要!!!如果t旋转到了最顶点,即w->pre为0,那么要更新root指向!
	w->pre = t;
	update(w);
	update(t);
	*r = t;  //将新的节点赋值到*r中
}

/*如果r被标记,则旋转r的“直接”子树,并将标记下传到“直接”孩子*/
void push(pNode r){
	pNode t;
	if(r && r->rev){
		t = r->ch[0];
		r->ch[0] = r->ch[1];
		r->ch[1] = t;

		r->rev = 0;
		if(r->ch[0])
			r->ch[0]->rev ^= 1;
		if(r->ch[1])
			r->ch[1]->rev ^= 1;
	}
}

/*寻找中序遍历的第k个节点*/
pNode find(int k, pNode r){
	int t;
	if(!r) return 0;
	push(r);
	t = r->ch[0]?(r->ch[0]->child+1):0;
	if(k==t+1)
		return r;
	if(k>t+1)
		return find(k-t-1, r->ch[1]);
	else
		return find(k, r->ch[0]);
}

/*伸展运动,将a旋转到b的下面。当b为0时,即,将a旋转为root*/
void splay(pNode a, pNode b){
	pNode k;
	k = a->pre;
	if(!k || k==b)
		return;

	if(a==k->ch[0])
		rotate(&k, 1);
	else
		rotate(&k, 0);
	splay(k, b);
}

void show(pNode r){
	if(!r) return;
	show(r->ch[0]);
	printf("%d ", r->val);
	show(r->ch[1]);
}

/*删除树,养成好习惯。不过要稍微多耗一点时间*/
void del(pNode r){
	if(!r) return;
	del(r->ch[0]);
	del(r->ch[1]);
	free(r);
}


void main(){
	int i;
	for(i=0; i<10; i++)
		insert(&root, i);
	show(root); printf("\n");
	del(root);
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值