王道数据结构--树的一些典型算法【更新中】

  1. 求深度
void BiTDepthNonCur(BiTNode * T,int &depth) {
	if (!T) {
		depth = 0;
		return;
	}
	cout << "test1" << endl;
	queue<BiTree> q;
	BiTNode* p = T;
	q.push(T);
	int len=0;
	while(!q.empty()) {
		depth++;
		len = q.size();
		cout << "test2 当前队列大小:  "<<len<<endl;
		while (len--) {
			p = q.front();
			q.pop();
			if (p->lchild) q.push(p->lchild);
			if (p->rchild) q.push(p->rchild);
		}
	}
//需要记录每一层的节点数,按照结点数进行入队列,一层队列完了进入下一层
}//非递归求法 求深度
int BiTDepth(BiTree T) {
	if (!T) return 0;
	int ldepth=0,rdepth;
	ldepth = BiTDepth(T->lchild);
	rdepth = BiTDepth(T->rchild);
	return max(ldepth,rdepth)+1;
}
  1. 层次遍历应用,有关叶子节点的判断 (是否为完全二叉树、叶子节点的数量,双分支结点的数量,求高度 求宽度)

1)是否为安全二叉树

bool isComplete(BiTree T){
	//判断是否为完全二叉树:
	// 标记第一个出现无左孩子无右孩子或者无右孩子有左孩子的结点,若其其后的结点并不是叶子结点,则不是一颗完全二叉树,若出现一颗含有右孩子无左孩子的结点则一定不是完全二叉树
	//标记为真,则此时访问的结点必为叶子结点否则就不是完全二叉树
	//判断是否是叶子结点p->lchild==p->rchild==NULL
	if (!T) return false;
	queue<BiTree> q;
	BiTree p = T;
	q.push(p);
	bool res = true, flag=false;//flag标记是否遇到了一个含有左孩子无右有孩子或者无左孩子且无右孩子的结点
	while (!q.empty()) {
		p = q.front();
		q.pop();
		if (flag) {
			if (p->lchild != NULL || p->rchild != NULL) {
				res = false;
				break;
			}
		}
		else if (p->lchild&&p->rchild) {
			q.push(p->lchild);
			q.push(p->rchild);
		}
		else if (p->lchild && !p->rchild) {
			flag = true;
			q.push(p->lchild);
		}else if (!p->lchild && p->rchild) {
			res = false;
			break;
		}else {
			flag = true;
		}
	}
	if (res) {
		cout << "是完全二叉树" << endl;
	}
	else {
		cout << "不是完全二叉树" << endl;
	}
	return res;
}

2)非叶子节点的、叶子结点的数量,使用层次遍历比较好获得父节点

int countDoubleKNode(BiTree T) {
	if (!T) return 0;
	queue<BiTree> q;
	BiTree p = T;
	q.push(p);
	int count = 0;
	while (!q.empty()) {
		p = q.front();
		q.pop();
		if (p->lchild&&p->rchild) count++;
		if (p->lchild) q.push(p->lchild);
		if (p->rchild) q.push(p->rchild);
	}
	return count;
}

3)14题 求非空二叉树的宽度

//与第5题非递归求二叉树的高度类似使用层次遍历,并且记录每一层结点数

int width(BiTree root) {
	int width=0;
	int len=0;
	BiTree queue[MAXSIZE];
	int front,rear;
	front = rear=0;//队列初始化
	queue[rear++] = root;
	while (front != rear) {
		root = queue[front];
		len = rear - front ;
		if (len > width) width = len;
		while (len--) {
			root=queue[front++];
			if (root->lchild) queue[rear++] = root->lchild;
			if (root->rchild) queue[rear++] = root->rchild;
		}
	}
	return width;
}
  1. 后序遍历适用 满足先左子树再右子树,再根遍历特点
  • 非递归和递归两种写法
  • 应用:查找公共祖先,最近公共祖先,输出某一结点的所有祖先结点,
  • Notes:删除结点值为x的结点,使用后序好删除,左右都为空才删除

1)13题

bool find(BiTree root,BiTree p) {
	if (!root) return false;
	if (root == p)
		return true;
	if (root->lchild&&find(root->lchild, p)) {
		return true;
	} else return find(root->rchild,p);
}
void ANCESTOR(BiTree root,BiTree p,BiTree q,BiTree & comAns) {
	if (!root) {
		comAns = NULL;
		return;
	}
	if (root == p || root == q) {
		comAns = root;return;
	}
	bool left = find(root->lchild,p);//左子树中找p
	bool right = find(root->lchild, q);//左子树中找p
	if (left&&right) {
		//如果都在左子树中,去左子树中寻找
		ANCESTOR(root->lchild, p, q, comAns);
	}
	else if(!left&&!right){
		//都在右子树中,则去右子树中寻找
		ANCESTOR(root->rchild, p, q, comAns);
	}
	else {
		comAns = root;
		return;
	}
}

2) 12题打印值为x的所有祖先

//用栈的方法打印值为x的所有祖先的结点值
 void getPath(BiTree root, char target,vector<BiTree *>&path){
        stack<BiTree *> stk;//后序遍历的辅助栈,对于一个节点先这个节点入栈,再访问左子树,左子树访问完了,访问右子树。
        BiTree*p=root,*pre=NULL;//p指向当前访问的节点,pre是上一个已经出栈的节点指针
        while(p||!stk.empty()){
            while(p){//后序遍历,根节点进栈
                stk.push(p);//当前节点入栈
                path.push_back(p);//暂时加入存储路径的数组中
                if(p->val==target)
                    return ;
                p=p->left;//访问左子树
            }
            p=stk.top();//p指向为空即左子树为空,将p指向栈顶,并不急着出栈 
            if(p->right==NULL||p->right==pre){//如果p的右孩子被访问过或者右孩子为空则出栈
                stk.pop();
                path.pop_back();将p节点从path数组中删除
                pre=p;//更新pre
                p=NULL;
            }else{//右子树没有被访问过,访问右子树
                p=p->right;
            }
        }
    }
void ancestorPrintNonCur(BiTree root, char target) {
	if (!T) return;
	vector<TreeNode*> path;
    getPath(root,target,path);
    //从最顶层祖先开始打印
    for(int i=0;i<path.size();i++){
    	printf("%c ",path[i]);
    }
}

3)上一题的递归做法

bool ancestorPrintCur(BiTree T, int x) {
	if (!T) return false;//出口
	else if (T->lchild&&T->lchild->data == x || T->rchild&&T->rchild->data == x) {
		cout <<"结点值为x的祖先结点: "<< T->data;
		return true;//出口
	}else if (ancestorPrintCur(T->lchild, x) || ancestorPrintCur(T->rchild, x)) {//递归
			cout << T->data;
			return true;
	}else 
		return false;
}

4)删除结点

//第11题
void deleteTree(BiTree &T) {
//当左右子树都为空时,根节点好删除,所以使用后序遍历易于删除	
	if (T == NULL) return;
	delete(T->lchild);
	delete(T->rchild);
	free(T);
}
//第11题
void delete_X(BiTree & T, int x) {
	//使用层次遍历易于获得父节点
	//先删除左右子树,并且删除后要将左右子树置空,否则指针悬空在遍历 时会陷入死循环
	queue<BiTree> q;
	BiTree p = T;
	q.push(p);
	while (!q.empty()) {
		p = q.front();
		q.pop();
		if(p->lchild)
		if (p->lchild->data==x) {
			delete(p->lchild);
			p->lchild = NULL;
		}
		else {
			q.push(p->lchild);
		}
		if (p->rchild) 
			if (p->rchild->data==x) {
				delete(p->rchild);
				p->rchild = NULL;
			}
			else {
				q.push(p->rchild);
			}
				
	}
}
  1. 通过先序、后序、中序的关系建树、转换先序中序、后序

1)第6题

BiTree create(vector<int>& preorder, vector<int>& inorder, int preorder_start, int preorder_end, int inorder_start, int inorder_end) {
	if (preorder_start > preorder_end) {
		return nullptr;
	}
	BiTree node = (BiTree)malloc(sizeof(BiTNode));
	node->data = preorder[preorder_start];
	node->lchild = NULL;
	node->rchild = NULL;
	int pos;
	for (int i = inorder_start; i <= inorder_end; i++) {
		if (inorder[i] == preorder[preorder_start]) {
			pos = i;
			break;
		}
	}
	node->lchild = create(preorder, inorder, preorder_start + 1, preorder_start + pos - inorder_start, inorder_start, pos - 1);
	// preOrderStart 可能会大于preOrderEnd 比如pos==inorder_start时 preorder_start + pos - inorder_start= preorder_start<preorder_start+1
	node->rchild= create(preorder, inorder, preorder_end - inorder_end + pos + 1, preorder_end, pos + 1, inorder_end);
	return node;

}

2)15题

//15题 根据一颗满二叉树(结点值各不相同)的先序序列转换为后序序列
void postPrint(int pre[],int low,int high) {
	if (low > high) return;
	//int root = low;//先序的第 一个结点为子节点 最后输出
	if (low == high) {
		cout << pre[low]<<" ";
		return;
	}
	int mid = (low + high) / 2;//下标为low+1到mid为左子树,剩下的为右子树
	postPrint(pre, low + 1, mid);//打印左子树
	postPrint(pre, mid+1 , high);//打印右子树
	cout << pre[low]<<" " ;
}

3)15 题

void preToPost(int pre[], int post[], int prel,int preh,int postl,int posth) {
	if (prel > preh) return;
	post[posth] = pre[prel];//先放先序第一个结点至后序数组的最后一位!
	int half = (preh - prel) / 2;
	preToPost(pre, post,prel+1, prel + half, postl, postl + half - 1);
	preToPost(pre, post, prel + half + 1, preh, postl + half, posth-1);//注意这里的后序指针要前移一位,因为之前已经放入了一个结点
	
}
  1. 遍历递归算法、二分思想递归

1)第10题 查找先序第k个结点

char FINDK1(BiTree T, int & i, int k) {
	if (T == NULL) return '#';
	i++;
	if (i == k)
		return T->data + '0';
	char ch;
	ch = FINDK1(T->lchild, i, k);
	if (ch != '#')
		return ch;
	ch = FINDK1(T->rchild, i, k);
	return ch;
}

2)第9题 交换左右子树

void changeLeftAndRight(BiTree & T) {
	if (!T) return;
	BiTree tmp=T->lchild;
	T->lchild = T->rchild;
	T->rchild = tmp;
	if (T->lchild) changeLeftAndRight(T->lchild);//剪枝
	if (T->rchild) changeLeftAndRight(T->rchild);
}

3)第17题 是否相似

bool isResemble(BiTree T1, BiTree T2) {
	
	if (!T1 && !T2 ) return true;
	else if (!T1 || !T2) {
		return false;//如果在递归前没有返回false的语句则递归下一层的结果不会有false出现!!
	}
	if (isResemble(T1->lchild, T2->lchild) && isResemble(T1->rchild, T2->rchild)) {
		return true;
	}else return false;
}

4)第16题 构造单链表!!!需要头结点!

LinkList head, p;
void Link(BiTree root, BiTree pre) {
	//前序遍历一棵树,当且仅当左右孩子为空时且pre不为空时,将pre的右指针指向该结点,并且pre指向当前结点
	if (!root) return;
	if (root->lchild&&root->rchild) {
		if (!pre) {
			head = (LinkList)malloc(sizeof(LNode));
			head->data = root->data;
			p = head;
			pre = root;//!!!!!重要!!!! 不写这一行跳不到else     

		}
		else {
			pre->rchild = root;
			pre = root;
			p->next = (LinkList)malloc(sizeof(LNode));
			p->next->data = root->data;
		}
	}
	else {
		if (root->lchild) Link(root->lchild, pre);
		if (root->rchild) Link(root->rchild, pre);
	}
}
  • 6
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值