数据结构算法题个人笔记——二叉树篇

这篇博客整理了19道关于二叉树的算法题目,涵盖了翻转、比较、对称性、层次遍历、高度计算、完全二叉树判断、双分支节点计数、节点查找、子树删除、宽度计算、最近公共祖先、遍历序列转换等操作。通过这些题目,深入理解二叉树的特性和操作方法。
摘要由CSDN通过智能技术生成

前言

struct TreeNode {
     int val;
     struct TreeNode *left;
     struct TreeNode *right;
};

题目一:翻转二叉树

/*
题目来源:https://leetcode-cn.com/problems/invert-binary-tree/
*/

//先序遍历框架
struct TreeNode* invertTree(struct TreeNode* root){
        if(!root) return root;
        else{
            //交换左右子树
            struct TreeNode* node = root->left;
            root->left = root->right;
            root->right = node;
            //递归交换左子树和右子树
            root->left = invertTree(root->left);
            root->right = invertTree(root->right);
            return root;
        }
}

//后序遍历框架
struct TreeNode* swap(TreeNode* root){
	if(root==NULL) return NULL;
	TreeNode* left = swap(root->left);
	TreeNode* right = swap(root->right);
	root->left = right;
	root->right = left;
	return root;
	}
}

题目二:相同的树

/*
题目来源:https://leetcode-cn.com/problems/same-tree/
*/
bool isSameTree(struct TreeNode* p, struct TreeNode* q){
    if(!p && !q) return true;
    if(!p || !q) return false;
    return p->val==q->val && isSameTree(p->left,q->left) && isSameTree(p->right, q->right);
}

题目三:对称二叉树

/*
题目来源:https://leetcode-cn.com/problems/symmetric-tree/
*/
bool check(struct TreeNode* root1, struct TreeNode* root2){
    if(!root1 && !root2) return true;
    if(!root1 || !root2) return false;
    if(root1->val != root2->val) return false; //!是root->val
    return check(root1->left, root2->right) && check(root1->right, root2->left);
}

bool isSymmetric(struct TreeNode* root){
    return check(root, root);
}

题目四:写出二叉树自下而上、从从右到左的层次遍历

/*
思想:利用层次遍历,出队后紧接着将指针入队。当所有结点指针入栈后,依次出栈并开始访问所求结点。
*/
void invertLevel(TreeNode* root){
	Stack S;
	Queue Q;
	TreeNode *p = NULL;							//工作指针
	if(root!=NULL){
		InitStack(S);							//初始化栈,栈中存放二叉树结点的指针
		InitQueue(Q);							//队列初始化,队列存放二叉树结点的指针
		EnQueue(Q, bt);
		while(!Isempty(Q)){						//若队列非空
			//出队,压栈(关键)
			DeQueue(Q, p);						
			push(S,p);	
			
			if(p->lchild)						//若左孩子不为空,入队
				EnQueue(Q, p->left);
			if(p->right)						//若左孩子不为空,入队
				EnQueue(Q, p->right);		
		}
		//依次输出栈中元素
		while(!isEmpty(s)){
			Pop(S, p);
			visis(p->data);	
		}					
	}
}

题目五:二叉树采用二叉链表存储,编程实现非递归算法求二叉树高度

int maxDepth(TreeNode* root){
	if(root==NULL) return 0;
	else{
		TreeNode* p = NULL;			
		int i ,depth = 0;
		EnQueue(Q, root);
		while(!isEmpty(Q)){
			depth++;
			for(i=0; i<Q.size(); ++i){				//关键
				DeQueue(Q, p);					//出队
				if(p->left) EnQueue(Q, p->left);
				if(p->right) EnQueue(Q, p->right);
			}
		}
		return depth;
	}
}

题目六:判断该二叉树是否为完全二叉树

bool isCompleteTree(TreeNode* root){
	TreeNode* p;
	InitQueue(Q);
	EnQueue(Q, root);							//入队
	while((p = DeQueue(Q))!=NULL){				//广度遍历,把NULL结点也放入队列
		EnQueue(Q, root->left);
		EnQueue(Q, root->right);
	}
	//如果是完全二叉树,那么队列中剩下的都为空
	while(!isEmpty(Q)){
		p = DeQueue(Q);
		if(p!=NULL) return false;
	}
	return true;
}

题目七:计算二叉树双分支结点个数

int DsonNodes(TreeNode* root){
	if(root==NULL) return 0;
	else{
		if(root->left!=NULL && root->right!=NULL){
			return 1 + DsonNodes(root->left) + DsonNodes(root->right);
		}else{
			return DsonNodes(root->left) + DsonNodes(root->right);
		}
	}
}
//本题也可以设置一个全局变量,每遍历到一个结点,判断每个结点是否是双分支等于2的结点

题目八:二叉链表存储二叉树,求先序遍历序列中第K个结点的值

int i=1;
ElemType preNode(TreeNode* root, int k){
	if(root==NULL) return '#';
	//如果找到
	if(i=k) return root->data;
	//如果未找到
	i++;
	//左子树中遍历寻找
	ElemType p = preNode(root->left, k);
	if(p!='#') return p;
	//右子树中遍历寻找,不管找到没,最后都返回
	p = preNode(root->right, k);
	return p;
}

题目九:二叉链表存储的二叉树,对于树中每个元素值为x的结点,删除以它为根的子树

/*
思想:层次遍历
DeleteXTree()函数的作用:删除指针所指结点的孩子结点
*/
void search(TreeNode* root, ElemType x){
	if(root!=NULL){
		if(root->data==x){
			DeleteXTree(root);
			return;
		}else{
			InitQueue(Q);
			EnQueue(Q, root);
			TreeNode* p = NULL;
			while(!isEmpty(Q)){
				DeQueue(Q, p);
				
				if(p->left){					//左子树不为空
					if(p->left->data==x){		//左子树符合删除条件
						DeleteXTree(p->left);
						p->left = NULL;
					}else EnQueue(Q, p->left);
				}

				if(p->right){					//右子树不为空
					if(p->right->data==x){		//右子树符合删除条件
						DeleteXTree(p->right);
						p->right = NULL;
					}else EnQueue(Q, p->right);
				}
			}
		}
	}
}

题目十:计算二叉树高度

//递归算法
//后序遍历框架
int hight(TreeNode* root){
	if(root) return 0;
	int lhight=hight(root->left);							//左
	int rhight=hight(root->right);							//右
	return (lhight>rhight?lhight:rhight)+1;					//根
}

题目十一:计算二叉树宽度

/*
count[二叉树高度]:其中存储的值为对应该层结点的个数
*/
int count[100];								//100为二叉树高度
int MAX=-1;
void findWidth(TreeNode* root, int k){		//k代表层数
	if(root==NULL) return;
	count[k]++;								//该层+1,因为每进入递归一次,层数+1
	if(MAX<count[k]) MAX=count[k];
	//递归下一层,下一层的k为当前层数+1
	findWidth(root->left, k+1);
	findWidth(root->right, k+1);
}

题目十二:找二叉树p和q分别指向的两个结点的最近公共祖先

/*
框架:后序遍历
*/
TreeNode lowerCommonAncestor(TreeNode* root,TreeNode* p,TreeNode* q){
	//base case
	if(root==NULL) return NULL;
	if(root==p || root==q) return root;
	TreeNode* left = lowestCommonAncestor(root->left,p,q);
	TreeNode* right = lowestCommonAncestor(root->right,p,q);
	//情况一:
	if(left!=NULL && right!=NULL) return root;
	//情况二:
	if(left==NULL && right==NULL) return NULL;
	//情况三:
	return left==NULL?right:left;
}

题目十三:二叉树先序遍历

void preTravel(TreeNode* root){
	if(root!=NULL){
		Stack s;
		InItStack(s);
		push(s, root);
		TreeNode* p=NULL;
		while(!isEmpty()){
			p=pop(s);
			Visit(p);
			if(p->right!=NULL) push(s,p->right);
			if(p->left!=NULL) push(s,p->left);
		}
	}
}

题目十四:二叉树中序遍历

void inorderTravel(TreeNode* root){
	if(root!=NULL){
		Stack s;
		InItStack(s);
		push(s);
		while(root && isEmpty(s)!=NULL){
			while(root){
				push(s,root);
				root=root->left;
			}
			//开始出栈
			root=pop(s);
			visit(root);
			root=root->right;
		}
	}
}

题目十五:二叉树后序遍历

/*思想:
前序:根-左-右
后序:左-右-根

那么我们先把后序当作:根-右-左,然后反转的结果
具体代码思路:先用根-右-左的方法依次把遍历的放在数组中,然后数组逆置,其结果即为后序遍历
输出的结果
*/
void postTravel(TreeNode* root){
	if(root!=NULL){
		Stack s;
		TreeNode* p;
		int i=0,arr[100];
		InItStack(s);
		push(s,root);
		while(!isEmpty(s)){
			p=pop(s);
			arr[i]=p->val;		
			if(p->left!=NULL) push(s, p->left);
			if(p->right!=NULL) push(s, p->right); 
		}
		//反转数组
		reverse(arr);									//伪码:考试时别这么写
	}
}

题目十六:根据先序序列和中序建立该二叉树的二叉链表

/*
A:先序序列
B:中序序列
pre_start、pre_end:先序起点和终点
inorder_start、inorder_end:中序起点和终点
*/
TreeNode* preInCreat(ElemType A[],ElemType B[],int pre_start,int pre_end,int inorder_start,int inorder_end){
	root=(TreeNode*)malloc(sizeof(TreeNode));				//为根结点开辟根结点
	root->data=A[pre_start];								//先序的第一个即为根
	//遍历中序,找到根在中序的位置
	for(index=inorder_start; B[i]!=root->data; index++);
	//分别计算根结点左、右子树元素个数
	lLen=index-inorder_start;
	rLen=inorder_end-index;
	
	//递归建立左子树
	if(lLen)
		root->left=preInCreat(A,B, pre_start+1, pre_start+rLen, inorder_start, inorder_start+lLen-1);
	else
		root->left=NULL;
	//递归建立右子树
	if(rLen)
		root->right=preInCreat(A,B, pre_end-rLen+1, pre_end, inorder_end-rlen+1, inorder_end);
	else
		root->right=NULL;
	//返回根
	return root;
}

题目十七:满二叉树先序转后序

/*
思想:先序序列第一个结点作为后序序列的最后一个结点
*/
void preToPost(ElemType pre[],int pre_strart,int pre_end, ElemType post[],int post_start,int post_end){
	int halfLen;
	if(pre_end >= pre_end){
		post[post_end] = pre[post_start];			//先序第一个结点作为后序最后一个结点
		halfLen = (pre_end - pre_start)/2;
		//转化左子树
		preToPost(pre,pre+1,pre+halfLen, post, post_pre,post_pre+halfLen-1);
		//转化右子树
		preToPosst(pre,pre+halfLen+1,pre_end, post, post_start+halfLen, post_end-1);
	}
}

题目十八:判断二叉树是否存在从根结点到叶子结点路径权值之和等于sum

/*
结束的条件:到达叶子结点
*/
bool hasPathSum(TreeNode* root, int sum){
	if(root==NULL) return false;
	//到达叶子结点
	if(root->left==NULL && root->right==NULL) return sum==root->val;
	
	//递归判断左子树
	return hasPathSum(root->left, sum-root->val);
	//递归判断右子树
	return hasPathSum(root->right, sum-root->val);
}

题目十九:判断二叉树是否相似

boolen similar(TreeNode* root1, TreeNode* root2){
	if(root1==NULL && roo2==NULL) return true;
	if(root1==NULL || root2==NULL) return false;
	return similar(root1->left,root2->left) && similar(root1->right,root2->right);
}
  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值