二叉树先序、中序、后序遍历(递归、非递归)及层次遍历以及二叉树线索化以及线索化二叉树六种遍历、并查集(代码演示+注释解析)以及一些真题介绍

二叉树的非递归前序、中序、后序遍历算法详解及代码实现(C语言) 转载请注明出处:https://blog.youkuaiyun.com/Benja_K/article/details/88389039 二叉树的非递归前、中、后序遍历算法详解及代码实现(C语言) 1. 前序遍历和中序遍历非递归算法思路 前序和中序非递归遍历的C代码 2. 后序遍历非递归算法思路 后序非递归遍历的C代码 1. 前序遍历和中序遍历非递归算法思路 遍历过程:(如图所示) 图1 前... 阅读详情

1.四种遍历

先序遍历 依次遍历:根结点,左子树,右子树
中序遍历 依次遍历:左子树,根结点,右子树
后序遍历 依次遍历:左子树,右子树,根结点
层序遍历:深度优先遍历

这里给出一棵树,进行四种遍历:
在这里插入图片描述
递归:

#include<iostream>
#include<queue>
using namespace std;

typedef char DataType;

//二叉树
typedef struct TreeNode {
	DataType val;
	struct TreeNode* left;
	struct TreeNode* right;
	TreeNode(DataType data) :val(data), left(NULL), right(NULL) {};
}TNode;


//先序遍历:根左右
void PreOrder(TNode* root) {
	if (root == NULL) return;//如果节点为空 则返回上一级

	printf("%c ", root->val);//打印根节点
	PreOrder(root->left);//先遍历左子树
	PreOrder(root->right);//再遍历右子树
}

//中序遍历:左根右
void InOrder(TNode* root) {
	if (root == NULL) return;//如果节点为空 则返回上一级

	InOrder(root->left);//先遍历左子树
	printf("%c ", root->val);//左子树遍历完 打印当前节点
	InOrder(root->right);//再遍历右子树
}

//后序遍历:左右根
void PostOrder(TNode* root) {
	if (root == NULL) return;//如果节点为空 则返回上一级

	PostOrder(root->left);//先遍历左子树
	PostOrder(root->right);//再遍历右子树
	printf("%c ", root->val);//左右子树遍历完 打印当前节点
}

//层序遍历  广度优先遍历
//核心思路: 上一层出的时候带下一层节点进
//这里可以考虑用到数据结构中的队列存储节点
void LevelOrder(TNode* root) {
	queue<TNode*> q;
	if(root) q.push(root);
	while (!q.empty()) {
		TNode* t = q.front();//取出来队列前面的节点
		printf("%c ", t->val);
		q.pop();//删掉
		//上一层出 下一层进
		if (t->left) q.push(t->left);
		if (t->right) q.push(t->right);
	}
}


int main() {
	//节点初始化
	TNode* root = new TNode('A');
	TNode* B = new TNode('B');
	TNode* C = new TNode('C');
	TNode* D = new TNode('D');
	TNode* E = new TNode('E');
	TNode* F = new TNode('F');
	TNode* G = new TNode('G');
	TNode* H = new TNode('H');
	TNode* I = new TNode('I');

	//构建二叉树
	root->left = B;
	root->right = C;
	B->left = D;
	B->right = E;
	D->left = H;
	C->left = F;
	C->right = G;
	F->left = I;

	//先序遍历
	cout << "先序遍历:";
	PreOrder(root);
	puts("");

	//中序遍历
	cout << "中序遍历:";
	InOrder(root);
	puts("");

	//后序遍历
	cout << "后序遍历:";
	PostOrder(root);
	puts("");

	//层序遍历
	cout << "层序遍历:";
	LevelOrder(root);
	puts("");
	
	return 0;
}
先序遍历:A B D H E C F I G
中序遍历:H D B E A I F C G
后序遍历:H D E B I F G C A
层序遍历:A B C D E F G H I

三种遍历非递归:

 //节点总数
int TreeSize(struct TreeNode* root){
    return root==NULL?0:TreeSize(root->left)+TreeSize(root->right)+1;
}
 
//先序
int* preorderTraversal(struct TreeNode* root, int* returnSize){
    if(root==NULL){
        *returnSize = 0;
        return (int*)malloc(sizeof(int)*0);
    }
    int size = TreeSize(root);//获得树的节点数
    int* ans = (int*)malloc(sizeof(int)*size);//开辟数组
    struct TreeNode** s = (struct TreeNode*)malloc(sizeof(struct TreeNode*)*size*2);//模拟栈
    int top = 0;
    int i = 0;
    s[top++] = root;
    while(i<size){
        struct TreeNode* node = s[--top];//出栈
        if(node){
            if(node->right) s[top++] = node->right;
            if(node->left) s[top++] = node->left;
            s[top++] = node;
            s[top++] = NULL;//表明前一个根节点已经访问过子树
        }else{
            ans[i++] = s[--top]->val;
        }
 
    }
    free(s);//释放内存
    *returnSize = size;
    return ans;
}
 
 //中序
int* inorderTraversal(struct TreeNode* root, int* returnSize){
    if(root==NULL){
        *returnSize=0;
        return (int*)malloc(sizeof(int)*0);
    }
    int size = TreeSize(root);
    *returnSize = size;
    int* ans = (int*)malloc(sizeof(int)*size);
    struct TreeNode** s = (struct TreeNode*)malloc(sizeof(struct TreeNode*)*size*2);//模拟栈
    int i = 0;
    int top = 0;//栈顶
    s[top++] = root;
    while(i<size){
        struct TreeNode* node = s[--top];
        if(node){
            if(node->right) s[top++] = node->right;
            s[top++] = node;
            s[top++] = NULL;
            if(node->left) s[top++] = node->left;
        }else{
            ans[i++] = s[--top]->val;
        }
    }
    free(s);
    return ans;
}

//后序
int* postorderTraversal(struct TreeNode* root, int* returnSize){
    if(root==NULL) {
        *returnSize = 0;
        return (int*)malloc(sizeof(int)*0);  
    }
    int size = TreeSize(root);//获得树的节点数
    int* ans = (int*)malloc(sizeof(int)*size);//开辟数组
    struct TreeNode** s = (struct TreeNode*)malloc(sizeof(struct TreeNode*)*size*2);//模拟栈
    int top = 0;
    int i = 0;
    s[top++] = root;
    //先根右左进栈,之后左右根出栈
    //这里注意如果root本来就为空,下面则会发生越界 所以不建议while(top>0) 或者开头便判断是否为空
    while(i<size){
        struct TreeNode* node = s[--top];//弹出栈顶元素 并且pop掉
        if(node){//元素不为NULL
            s[top++] = node;
            s[top++] = NULL;//NULL标记前面根节点已经被访问
            if(node->right) s[top++] = node->right;
            if(node->left) s[top++] = node->left;
        }else{
            ans[i++] = s[--top]->val;
        }
        
    }
    free(s);//释放内存
    *returnSize = size;
    return ans;
}

王道版本非递归:

//节点总数
int TreeSize(struct TreeNode* root){
    return root==NULL?0:TreeSize(root->left)+TreeSize(root->right)+1;
}

//先序
int* preorderTraversal(struct TreeNode* root, int* returnSize){
    if(root==NULL){
        *returnSize = 0;
        return (int*)malloc(sizeof(int)*0);
    }
    int size = TreeSize(root);//获得树的节点数
    int* ans = (int*)malloc(sizeof(int)*size);//开辟数组
    struct TreeNode** s = (struct TreeNode*)malloc(sizeof(struct TreeNode*)*size*2);//模拟栈
    int top = 0;
    int i = 0;
    struct TreeNode* node = root;
    while(i<size){
        if(node){
            ans[i++] = node->val;
            s[top++] = node;
            node = node->left;
        }else{
           node = s[--top];
           node = node->right; 
        }
    }
    free(s);//释放内存
    *returnSize = size;
    return ans;
}

//中序
int* inorderTraversal(struct TreeNode* root, int* returnSize){
    if(!root){//空树
        *returnSize = 0;
        return (int*)malloc(sizeof(int*)*0);
    }
    int size = TreeSize(root);
    *returnSize = size;
    int* ans = (int*) malloc(sizeof(int)*size);
    struct TreeNode** s = (struct TreeNode*)malloc(sizeof(struct TreeNode*)*size*2);//模拟栈
    int i = 0;
    int top = 0;//栈顶
    struct TreeNode* node = root;
    while(i<size){
        if(node){//一直检索左孩子
            s[top++] = node;//入栈
            node = node->left;//一路向左
        }else{//出栈
            node = s[--top];//栈顶元素出栈
            ans[i++] = node->val;
            node = node->right;//右子树
        }
    }
    free(s);
    return ans;
}

//后序
int* postorderTraversal(struct TreeNode* root, int* returnSize){
    if(root==NULL) {
        *returnSize = 0;
        return (int*)malloc(sizeof(int)*0);  
    }
    int size = TreeSize(root);//获得树的节点数
    int* ans = (int*)malloc(sizeof(int)*size);//开辟数组
    struct TreeNode** s = (struct TreeNode*)malloc(sizeof(struct TreeNode*)*size*2);//模拟栈
    int top = 0;
    int i = 0;
    struct TreeNode* node = root;
    struct TreeNode* r = NULL;
    while(i<size){
        if(node){
            s[top++] = node;
            node = node->left;
        }
        else{
            node = s[top-1];
            if(node->right&&r!=node->right){//防止重复遍历右子树
                node = node->right;
            }
            else{
                r = s[--top];
                ans[i++] = r->val;
                node = NULL;
            }
        }
    }
    free(s);//释放内存
    *returnSize = size;
    return ans;
}

2.线索二叉树

(1)二叉树线索化

//线索二叉树
typedef struct ThreadTree{
	DataType val;
	struct ThreadTree* left;
	struct ThreadTree* right;
	int ltag,rtag;
}ThreadTree;

//全局变量,指向当前访问节点前驱
ThreadTree* pre = NULL;

void visit(ThreadTree* p){
	//空链线索化
	//当前左前驱
	if(p->left==NULL){
		p->ltag = 1;
		p->left = pre;
	}
	//pre右后续
	if(pre!=NULL&&pre->right==NULL){
		pre->rtag = 1;
		pre->right = p;
	}
	pre = p;
	//开始访问下一节点
}

//先序遍历二叉树,同时进行线索化
//这里跟先序遍历没有太大区别,只是在访问的过程中对节点线索化
void PreThreadTree(ThreadTree* p){
	if(!p) return ;
	visit(p);
	//易错点
	//注意前面visit可能已经修改了节点左孩子指向前驱,避免循环在这里需要判断
	if(p->ltag==0) PreThreadTree(p->left);
	PreThreadTree(p->right);
}

//中序遍历二叉树,同时进行线索化
//这里跟中序遍历没有太大区别,只是在访问的过程中对节点线索化
void InThreadTree(ThreadTree* p){
	if(!p) return ;
	InThreadTree(p->left);
	visit(p);
	InThreadTree(p->right);
}

//后序遍历二叉树,同时进行线索化
//这里跟后序遍历没有太大区别,只是在访问的过程中对节点线索化
void PostThreadTree(ThreadTree* p){
	if(!p) return ;
	InThreadTree(p->left);
	InThreadTree(p->right);
	visit(p);
}

int main(){
	TreadTree* root;
	init(root);//初始化树
	//先遍历线索化
	if(root){
		PreThreadTree(root);
		//注意处理先序遍历的最后一个节点,后续为空
		pre->rtag = 1;
		pre->right = NULL;
	}
	//中序遍历线索化
	if(root){
		InThreadTree(root);
		//注意处理中序遍历的最后一个节点,后续为空
		pre->rtag = 1;
		pre->right = NULL;
	}
	//后序遍历线索化
	if(root){
		PostThreadTree(root);
		//注意处理后序遍历的最后一个节点,后续为空
		if(!pre){
			pre->rtag = 1;
			pre->right = NULL;
		}
	}
}

(2)线索二叉树遍历

①中序线索二叉树遍历

图解:
在这里插入图片描述在这里插入图片描述
代码:

typedef struct ThreadTree{
	DataType val;
	struct ThreadTree* left;
	struct ThreadTree* right;
	int ltag,rtag;
}ThreadTree;

//中序线索二叉树找中序后续
//相当于非递归实现中序遍历
//首先找到以p为根的子树中第一个被中序遍历的节点
ThreadTree* FirstNode(ThreadTree* p){
	//循环找到最左下节点,即中序遍历的首节点
	while(p->ltag==0) p = p->left;
	return p;
}
//寻找节点p的后续节点
ThreadNode* NextNode(ThreadTree* p){
	//若节点p的右子树有孩子,则无法找到直接后续,需要中序遍历右子树,寻找右子树最左下节点(即后续)
	if(p->rtag==0) return FirstNode(p->right);
	//rtag==1直接得到后续
	return p->right;
}
//通过找中序后续实现中序遍历(利用线索实现非递归中序遍历)
void InOrder(ThreadTree* root){
	for(Thread* p = FirstNode(root);p!=NULL;p = NextNode(p)){
		visit(p);
	}
}


//中序线索二叉树找中序前驱
//相当于非递归实现逆中序遍历
//首先找到以p为根的子树中最后一个被中序遍历的节点
ThreadTree* LastNode(ThreadTree* p){
	//循环找到最右下节点(不一定是叶子结点)左根右(左根)
	while(p->rtag==0) p = p->right;
	return p;
/*这段代码我最开始理解错了,想着应该是叶子结点,但是是左根右,只要是最右下就行了,现在找到了最右下,即使有左孩子,此时根据中序特征,也可知左子树优先于根。这里是对应先序遍历的最后一个叶子结点。
	//此时已经是最后一个叶子节点,即先序遍历的最后一个节点
	if(p->ltag&&p->rtag)  return p;
	//此节点还有右子树
	if(p->rtag==0) return LastNode(p->right);
	//此根节点还有左子树
	if(p->ltag==0) return LastNode(p->left); 
*/
}

//寻找节点p的前驱节点
ThreadNode* PreNode(ThreadTree* p){
	//若节点p的左子树有孩子,则无法找到直接前驱,需要中序遍历左子树,寻找右子树最右下节点(即前驱)
	if(p->ltag==0) return LastNode(p->left);
	//ltag==1直接得到前驱
	return p->left;
}
//通过找中序前驱实现逆中序遍历
void RevInOrder(ThreadTree* root){
	for(Thread* p = LastNode(root);p!=NULL;p = PreNode(p)){
		visit(p);
	}
}

②先序线索二叉树遍历

图解:
在这里插入图片描述

在这里插入图片描述
代码:

typedef struct ThreadTree{
	DataType val;
	struct ThreadTree* left;
	struct ThreadTree* right;
	struct ThreadTree* parent;
	int ltag,rtag;
}ThreadTree;

//先序线索二叉树找先序后续
//相当于非递归实现先序遍历
//首先找到以p为根的子树中第一个被先序遍历的节点,即根节点
//寻找节点p的后续节点
ThreadNode* NextNode(ThreadTree* p){
	//若节点p的右子树有孩子,则无法找到直接后续
	if(p->rtag==0){
		//若有左孩子,则后续为左孩子
		if(!p->ltag) retuen p-left;
		//若无则后续为右孩子
		return p->right;//这步可省略
	}
	//rtag==1直接得到后续
	return p->right;
}
//通过找先序后续实现先序遍历(利用线索实现非递归先序遍历)
void PreOrder(ThreadTree* root){
	for(ThreadTree* p = root;p!=NULL;p = NextNode(p)){
		visit(p);
	}
}


//先序线索二叉树找先序前驱
//相当于非递归实现逆先序遍历
//首先找到以p为根的子树中最后一个被先序遍历的节点(这里注意与上面中序遍历找最后一个节点的区别)
ThreadTree* LastNode(ThreadTree* p){
	//循环找到最右下节点(一定要是叶子结点),因为根左右,此时只是最右下但是有左孩子则并不满足条件(根左),需要找到左子树的最后一个右叶子结点。
	//此时已经是最后一个叶子节点,即先序遍历的最后一个节点
	if(p->ltag&&p->rtag)  return p;
	//此节点还有右子树
	if(p->rtag==0) return LastNode(p->right);
	//此根节点(即最右的非叶子节点)还有左子树
	if(p->ltag==0) return LastNode(p->left); 
}
//寻找节点p的前驱节点
ThreadNode* PreNode(ThreadTree* p){
	//若节点p的左子树有孩子,则无法找到直接后续
	if(p->ltag==0){//满足右左根
		ThreadTree* par = p->parent;//父节点
		if(par){//能找到父节点
			//若p是左孩子或者p是右孩子且父节点左孩子为空,则父节点为其前驱
			if(par->left==p||par->ltag) rerurn par;
			//p不为左孩子且左孩子非空
			return par->left;
		}
		//找不到父节点,说明已遍历到根节点,p没有前驱
		return NULL;
	}
	//ltag==1直接得到前驱
	return p->left;
}
//通过找先序前驱实现逆先序遍历
void RevPreOrder(ThreadTree* root){
	for(ThreadTree* p = LastNode(root);p!=NULL;p = PreNode(p)){
		visit(p);
	}
}

③后序线索二叉树遍历

图解:
在这里插入图片描述
在这里插入图片描述
代码:

typedef struct ThreadTree{
	DataType val;
	struct ThreadTree* left;
	struct ThreadTree* right;
	struct ThreadTree* parent;
	int ltag,rtag;
}ThreadTree;

//后序线索二叉树找后序后续
//相当于非递归实现后序遍历
//首先找到以p为根的子树中第一个被后序遍历的节点
ThreadTree* FirstNode(ThreadTree* p){
	//循环找到最左下节点,即后序遍历的首节点
	while(p->ltag==0) p = p->left;
	return p;
}
//寻找节点p的后续节点
ThreadNode* NextNode(ThreadTree* p){//左右根
	//若节点p的右子树有孩子,则无法找到直接后续
	if(p->rtag==0){
		//后序遍历中左右子树节点只可能是根的前驱,不可能是后续
		ThreadTree par = p->parent;//找p的父节点
		if(par){//p的父节点不为空
			//如果p是左孩子且右兄弟为空||p是右孩子,则父节点即为后续
			if((par->left==p&&par->rtag)||par->right==p) return par;
			//如果p是左孩子且右兄弟非空,则后续为右兄弟子树后序遍历的第一个节点
			return FirstNode(par->right);
		}
		//par为空说明p为根节点,p无后续
		return NULL;
	}
	//rtag==1直接得到后续
	return p->right;
}
//通过找先序后续实现先序遍历(利用线索实现非递归先序遍历)
void PostOrder(ThreadTree* root){
	for(ThreadTree* p = FirstNode(p);p!=NULL;p = NextNode(p)){
		visit(p);
	}
}


//后序线索二叉树找后序前驱
//相当于非递归实现逆后序遍历
//首先找到以p为根的子树中最后一个被后序遍历的节点,即根节点
//寻找节点p的前驱节点
ThreadNode* PreNode(ThreadTree* p){
	//若节点p的左子树有孩子,则无法找到直接前驱
	if(p->ltag==0){
		//如果p有右孩子,则前驱为p->right
		if(!p->rtag) return p->right;
		//若p无右孩子则前驱为其左孩子
		rerturn p-left;//此步可省略
	}
	//ltag==1直接得到前驱
	return p->left;
}
//通过找后序前驱实现逆后序遍历
void RevPostOrder(ThreadTree* root){
	for(ThreadTree* p = root;p!=NULL;p = PreNode(p)){
		visit(p);
	}
}

下面这些题值得一看,测试自己,知识点可融会贯通

3.关于二叉树的遍历的练习题

  1. 清华大学上机复试二叉树遍历
  2. 根据先序和中序遍历输出后序遍历
  3. 根据后序和中序遍历输出先序遍历
  4. 根据后序和中序遍历输出层序遍历
  5. 二叉树的三种遍历(递归、非递归)

4.二叉树的一些拓展练习题

  1. 二叉树的最大深度
  2. 二叉树的叶子结点个数
  3. 列出叶节点
  4. 二叉树中和为目标值的路径
  5. 翻转二叉树

5.并查集

在这里插入图片描述在这里插入图片描述

const int  N=1005					//指定并查集所能包含元素的个数
int pre[N];     					//存储每个结点的前驱结点,即父节点 
int rank[N];    					//树的高度 

//双亲表示法
//初始化函数,对n个结点进行初始化 
void init(int n)     				
{
    for(int i = 0; i < n; i++){
        pre[i] = i;     			//每个结点的父节点都是自己 
        rank[i] = 1;    			//每个结点构成的树的高度为 1 
    } 
}

 //查找结点x的根结点,即代表元
int find(int x)     	 		   
{
    if(pre[x] == x) return x;  		//递归出口:x的父节点为 x本身,则x为根结点 
    return find(pre[x]); 			//递归查找 
} 
 //改进查找算法:完成路径压缩,将x的前驱直接变为根结点
 //先找到根节点,再将此路径上所有节点前驱都改为根节点
int find(int x)     				
{
    if(pre[x] == x) return x;		//递归出口:x的父节点为 x本身,即x为根结点 
    return pre[x] = find(pre[x]);   //此代码相当于先找到根结点 rootx,然后 pre[x]=rootx 
} 

//判断两个结点是否连通 
bool isSame(int x, int y)      		
{
	//判断两个结点的根结点(即代表元)是否相同 
    return find(x) == find(y);  	
}

//合并算法
void join(){
	x = find(x);						//寻找 x的代表元
    y = find(y);						//寻找 y的代表元
    if(x == y) return ;			//如果 x和 y的代表元一致,说明他们共属同一集合,则不需要合并,返回,表示合并失败;否则,执行下面的逻辑
    pre[x] = y;							//让x的根为y
}


/*
改进合并算法(加权标记法)
rank记录树高,小树合并到大树
加权标记法的核心在于对rank数组的逻辑控制,其主要的情况有:
	1、如果rank[x] < rank[y],则令pre[x] = y;
	2、如果rank[x] == rank[y],则可任意指定上级;
	3、如果rank[x] > rank[y],则令pre[y] = x;
*/
void join(int x,int y)
{
    x = find(x);						//寻找 x的代表元
    y = find(y);						//寻找 y的代表元
    if(x == y) return ;			//如果 x和 y的代表元一致,说明他们共属同一集合,则不需要合并,返回,表示合并失败;否则,执行下面的逻辑
    if(rank[x] > rank[y]) pre[y]=x;		//如果 x的高度大于 y,则令 y的根为 x
    else								//否则
    {
        if(rank[x]==rank[y]) rank[y]++;	//如果 x的高度和 y的高度相同,则令 y的高度加1
        pre[x]=y;						//让x的根为y
	}
}

数据结构-二叉树(包含二叉树的层次建树、前中后序遍历、层次遍历解析及代码) 树是n(n>=0)个结点的有限集。当n=0时,称为空树。在任意一棵非空树中应满足:1)有且仅有一个特定的结点称为根的结点。2)当n>1时,其余结点可以分为m(m>0)个互不相交的有限集T1,T2,......,Tm,其中每个集合本身又是一棵树,并且称为根的子树。二叉树是另一种树形结构。其特点是每个结点至多只有两棵子树(即二叉树中不存在度大于二的结点),并且二叉树的子树有左右之分,其次序不能任意颠倒。与树相似,二叉树也以递归的形式定义。 阅读详情

相关推荐

二叉树的前序遍历

二叉树的前序遍历的记忆法则是“根左右",即先遍历根节点,再遍历左子树节点,再遍历右子树节点。 以上图为例,前序遍历的结果是【A, B, D, E, C, F, G】 一、解题思路:递归 递归是我们实现前中后序遍历最常用的方法。 什么问题可以采用递归求解呢? 需要满足三个条件: 一个问题的解可以分解为若干个子问题的解; 这个问题与分解的子问题,除了数据规模不同外,求解思路相同 存在递归终止条件。 那么在知道一个问题可以采用递归实现之后,如何写出递归代码呢? 关键在于能写出递归公式,找到终止条件。 在.

CV前沿 3万+

C语言完整代码实现:二叉树的先序遍历、中序遍历、后序遍历

序遍历就是:根、左、右,也就是先遍历根结点再遍历左结点最后再遍历右结点,注意:如果遍历到的结点不是叶子结点的话需要对该结点进行拆分,比如这棵二叉树,先遍历A,然后是B,然后再是C,但是由于B并不是叶子结点,他本身又是一棵子树的根结点,所以B可以拆分成B、D、E,同理因为C本身又是一棵子树的根结点根据先序遍历的原则C也可以拆分为C、F,然后再组合在一起便是A、B、D、E、C、F,此时D还不是叶子结点,同理再将D进行拆分为D、G,最后得到A、B、D、G、E、C、F。如下图所示...

yjz1314andyou的博客 1万+

FPGA图像处理入门 ZYNQ7020 图像中值膨胀腐蚀滤波

图像处理的三种滤波 中值滤波应用较为广泛 综合膨胀和腐蚀可以实现边缘检测。

Coast_的博客 715

二叉树的构建与遍历(先序、中序、后序、层次)

二叉树的构建与遍历

人生处处是围城,一切都是最好的安排! 1万+

TI DSP TMS320F280025 Note1:工程模板Template创建

该方法新建的工程支持寄存器开发,同时也支持库函数开发,新建后移动该工程也不需要重新修改路径,路径使用变量定义,使用方便、移植性能高

weixin_52849254的博客 2302

别再让信号‘撞墙’了!手把手教你用嘉立创阻抗计算器搞定PCB高速线(附免费压层结构选择)

本文详细介绍了如何使用嘉立创阻抗计算器解决PCB高速线设计中的阻抗匹配问题,避免信号反射和传输损耗。通过免费压层结构选择和五步计算法,帮助工程师快速掌握USB3.0差分线等高速信号的设计技巧,提升PCB设计效率和信号质量。

weixin_30736301的博客 278

前序、中序、后序遍历

1、树有很多种,每个节点最多只能有两个子节点的一种形式称为二叉树。 2、二叉树的子节点分为左节点和右节点。 3、前序遍历:先输出父节点,再遍历左子树和 右子树。 4、中序遍历:先遍历左子树,再输出父节点,再遍历右子树。 5、后序遍历:先遍历左子树,再遍历右子树,最后输出父节点。 6、前序遍历代码: public List<Integer> postorderTraversal(TreeNode root) { List<Integer>res=new ArrayList<

friendship1998的博客 1433

深入解析postcss-px-to-viewport-8-plugin:如何精准控制响应式转换范围

本文深入解析了postcss-px-to-viewport-8-plugin插件在响应式开发中的核心痛点——如何精准控制单位转换范围。针对插件默认全局转换的问题,文章提供了两种实战解决方案:通过编写巧妙的exclude正则表达式实现“曲线救国”,或直接修改插件源码补全include功能。同时,文章还分享了在Next.js、Vite等现代框架中的集成要点及常见问题排查方法,帮助开发者实现“外科手术式”的精准样式转换。

yoga7的博客 910

一文搞懂---先序遍历二叉树(内含完整实现代码)

如何用代码实现先序遍历二叉树(C语言或C++) 想必有很多小伙伴和我一样,最开始的时候在网上或者是书上看了一系列有关于二叉树的遍历原理以及递归、非递归的实现方法,但是奈何书中也只是给出了伪代码之类的,并没有一套完整的代码去实现,想自己动手但是还是有点难度,不要担心,小编总结了一个代码,已经在VS2017上调试过了,不过在正式看代码前,让我们先来看几幅图,考考看官们是否理解了先序遍历,如果没有理解原理的话,建议先去看看这位博主的文章: 看懂二叉树的三种遍历 以下三幅图的先序遍历应该是什么顺序,您能写出来吗?

qq_40077565的博客 2万+

量产导入 | 产品可靠性测试标准完整大集合(JEDEC/IEC/SAE…)

产品可靠性测试是产品质量保证中的重要一环, 包含有Pre-con, aging(寿命)和ESD(静电)等, 下面就收集了权威标准JEDEC全系列, 请参照如下 同时也附上其它的可靠性标准供大家参考及交叉理解, 可能侧重点不同, 大家可以参考下。

叫好与叫座虽然不是对立面,但想在同一个作品中达到双重效果很难。 1660

产品包装质检实战:基于YOLOv5的智能包装合格检测系统全流程指南

摘要: 针对产品包装质检效率低、漏检率高的问题,本文提出基于YOLOv5的智能检测系统。系统通过采集包装缺陷数据(破损、缺标、封装不良等),利用YOLOv5实现多类别缺陷识别(准确率≥95%),并开发Flask Web界面实现检测可视化与报告生成。实验表明,该系统在30帧/秒的产线速度下可稳定运行,显著提升质检效率。项目涵盖数据标注、模型训练、系统开发全流程,具有工业落地价值,适用于智能制造场景的自动化质检需求。(149字)

LXY_LOVE36的博客 92

二叉树前序遍历函数 代码图解(先序遍历 深度优先遍历)

【代码】二叉树前序遍历函数 代码图解(先序遍历 深度优先遍历)

2301_79629384的博客 789

096、PCIE环回模式(Loopback):调试时自己和自己对话的利器

摘要: PCIE环回模式是调试利器,通过让芯片内部数据自环来定位问题层级。文章详解三种环回模式:物理层环回验证基础收发,数据链路层环回测试DLLP机制,事务层环回验证端到端协议。分享了实战中的典型问题(时钟异常、状态机卡死、计数器失真)和"从下往上"的调试流程,强调要区分调试与生产功能,建议早期验证环回模式。通过具体代码示例和调试案例,说明合理使用环回模式能显著提升排查效率,是PCIE调试必备技能。(149字)

z534437748的博客 105

C++先序遍历

先序遍历是一种树形数据结构的遍历方式,它的顺序是先访问根节点,再访问左子树,最后访问右子树。输出: Preorder traversal: 1 2 4 5 3 6 7。的函数,该函数接受一个指向树节点的指针作为参数,并对树进行先序遍历。这段代码实现了一个名为。

babyai996的博客 1359

SAP 系统间 SSO 的真正价值,从 SM59 到可信 RFC 的安全设计

SAP S/4HANA系统间Single Sign-On(SSO)的核心价值在于集中管理通信身份与信任关系,而非仅减少密码输入。传统固定账号密码方式在系统规模扩大后会面临密码管理、审计追踪等挑战。通过SM59配置可信RFC目标系统,结合SSL证书或SAP Trusted System等强认证机制,能有效降低安全凭据暴露风险。SSO配置需配合授权对象、角色管理等建立清晰的信任边界,实现系统间安全通信的同时保持可审计性。这种方案特别适用于Fiori前端与S/4HANA后端等场景,通过可信RFC传递用户身份,避免

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 1062

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

《数据结构》—— 二叉树的先、中、后序遍历的逻辑与代码实现

二叉树的先中后序遍历,递归方式与非递归方式

博樽 2120

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

【java】代码实现:二叉树的先序遍历、中序遍历、后序遍历、层序遍历

文章目录一、节点类二、创建对象三、先序遍历(递归)四、中序遍历(递归)五、后序遍历(递归)六、层序遍历(队列) 一、节点类 public class Node { public Node left = null; public Node right = null; public int i; public Node(int i) { thi...

苍狼的博客 3126

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

C语言--数据结构--实现先序遍历、中序遍历、后序遍历、二叉树的高度、二叉树结点总数;

C语言--数据结构--实现先序遍历、中序遍历、后序遍历、二叉树的高度、二叉树结点总数;

weixin_69491512的博客 2920

二叉树遍历思路及代码(先序、中序、后序、层遍历)

1.定义:二叉树是n个结点的有限集合,该集合或者为空集,或者由一个根结点和两棵互不相交的、分别称为根结点的左子树和右子树的二叉树构成。2.形态:二叉树有5种形态,分别是空二叉树、只有一个根结点、根和左孩子、跟和右孩子、根和左右孩子。3.分类:满二叉树、完全二叉树、不完全二叉树 (满二叉树是特殊的完全二叉树)对当前树进行编号从上到下从左到右依次编号,如果编号中间没有断开,而且最底层是全的,就是满二叉树;如果编号中间没有断开,最底层不是满的,是完全二叉树;如果中间编号断开,一定是不完全二叉树。

weixin_48716357的博客 3691

【二叉树】二叉树的遍历(先序遍历,中序遍历,后序遍历)和创建(结尾附代码)

总体思路:节点包含数据域和左右两个子树,通过对左右子树递归的方法对二叉树进行延伸,直到叶子结点全为空。这三种都是先左(L)后右(R),区别在于根节点(V)的次序。注意:调试时只能用一种创建方式,需要把另一种方式屏蔽掉。例如,创建上图的:ABC##DE##F##G#H##方式3:直接用字符串,避免了重复性的复制粘贴。源文件testmain.cpp。方式2:传递返回值的方式创建。方式1:传地址的方式创建。头文件common.h。1.先序遍历:VLR。2.中序遍历:LVR。3.后序遍历:LRV。

weixin_49088965的博客 768

二叉树先序遍历(递归+迭代)——java

1、递归实现讲解: (1)确定递归放回值以及参数:根据题目需要返回遍历的节点,所以需要List集合存储节点的值,同时需要将二叉树头节点传入; ​ (2)确定递归终止条件:遍历二叉树节点为空,表明已经无节点,直接return (3)确定递归逻辑:递归顺序为中,左,右,递归开始时,先将根节点添加List集合,然后遍历左子树,最后遍历右子树。 ​...

weixin_50616848的博客 1819

二叉树的先序遍历详解(小白也懂)(附带中序,后序代码)

二叉树的先序遍历详解,中序遍历,后序遍历

m0_63112274的博客 2581

C语言实现非递归先序、中序、后序遍历

C语言实现非递归先序、中序、后序遍历

呵呵哒!的博客 7182

树的遍历代码

【代码】树的遍历代码。

qq_52538610的博客 497

二叉树的先中后序遍历以及c语言实现二叉树

二叉树的先中后序遍历以及c语言实现二叉树 目录二叉树的先中后序遍历以及c语言实现二叉树先序遍历中序遍历后序遍历规律分析代码实现(递归)二叉树完整代码 二叉树主要有三种遍历方式 先序遍历(根节点>左节点>右节点) 中序遍历(左节点>根节点>右节点) 后序遍历(左节点>右节点>根节点) ps:这个先中后其实是根据根节点的读取顺序定义的,根节点先遍历则为先序遍历,根节点后遍历则为后序遍历。 举个例子: 先序遍历:ABDGHKECFIJ 中序遍历:GDKHBEACIFJ 后序

Whlit_Lie的博客 5500

二叉树的前序遍历,中序遍历,后序遍历-详解-配套例题

二叉树作为数据结构中一种简单而且重要的数据结构,他的存储结构和算法都相对比较简单,因此他也显得特别重要,因为很多问题都可以抽象为二叉树的问题。 在这里我们对于二叉树的基本概念不做详细介绍,我们这里主要介绍二叉树的前序遍历,中序遍历,和后序遍历三种问题和如何通过他的前序遍历,中序遍历,后序遍历构造相应的二叉搜索树的问题。 在这之前,我们先简单了解一下二叉树的三种遍历到底是什么样子的。 我的记忆方式为: 前序遍历-DLR(根...

爱吃橘子的小翟 1万+
上一篇: LeetCode1819: 序列中不同最大公约数的数目
下一篇: 清华上机复试题二叉树的遍历
天皓Oo
博客等级 码龄5年 337粉丝 61原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值