树的创建与遍历

树的创建与遍历

本文记录了我在学习树的过程中习得的基础知识以及算法,特此在博客中以笔记的形式记录下来。

树的定义

树是一个n个节点的有限集,当n = 0时,我们称之为空树

  1. 除了树的根节点(没有父节点的节点称为根节点)以外,树中的所有节点都有它的前驱。

  2. 树的所有节点都可以有0个和多个后缀

  3. 除了根节点以外,每个子节点可以分为多个不相交的子树。

  4. 每一个非跟节点有且仅有一个父节点;

  5. 除了根节点以外,每个子节点可以分为多个不想交的子树。

树的相关定义

**节点的度:**节点拥有的子树的数目。

叶子:度为零的节点。

**层次:**根节点的层次为1,其余节点的层次等于该节点的父节点加1。

**有序树:**如果树中结点的各子树的次序是重要的,不可随意改变其节点位置

**无序数:**如果树中节点的各子树之间的次序是不重要的,可以交换位置。


二叉树

二叉树的定义

二叉树是每个节点最多有两个子树的树结构。它有五种基本形态:空集,有左子树,有右子树,有左右子树,无左右子树。

二叉树的种类

在我们解题过程中二叉树有两种主要的形式:满二叉树和完全二叉树。

满二叉树

如果一个树只有度为0或2的节点,而且为0的节点都在树上的同一层,那么这颗树为满二叉树。设满二叉树的深度为k,那么该满二叉树就有2^k-1个节点。

完全二叉树

在完全二叉树之中,除了最底层的节点没有填满,而其它层的节点数都达到了2个。而在对底层的节点中,节点度数为1的节点的子节点全部集合与左边。若完全二叉树的深度为k,那么该树的节点数应该在1 ~ 2^k-1的范围中。

image-20240408212513125

二叉搜索树

二叉搜索树(Binary Search Tree),又被称为二叉查找树。前面我们介绍的树其实都是节点上没有数据的树,而二叉搜索树就是节点上有数值的树,是有序树的一种

  • 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
  • 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
  • 它的左、右子树也分别为二叉排序树

二叉树的存储方式

在c语言中,二叉树是可以用数组和链表的两个方式来进行实现的,在[代码随想录](代码随想录 (programmercarl.com))中,给我们提供了这两种方式实现的思路

链式实现

img

数组实现

img

在数组之中我们有一个问题,那就是我们是怎么通过下标来进行树的遍历呢?

规律如下:

如果父节点的数组下标是 i,那么它的左孩子就是 i * 2 + 1,右孩子就是 i * 2 + 2。

二叉树的遍历方式

在二叉树的遍历当中存在两种遍历方式:

  • 深度优先(dfs)—— 不断递归或者迭代往深入遍历,直到遇到树中的叶节点,在进行回溯。
    • 前序遍历:中左右
    • 中序遍历:左中右
    • 后序遍历:左右中

image-20240411220351875

  • 广度优先(bfs) —— 按照树的层数不断进行遍历。
    • 层次遍历:按照上图的数,遍历的结果为1 2 3 4 5 6 7

二叉树遍历方式的实现

树的c语言实现

#include <stdio.h>
#include <stdlib.h>

typedef struct Tree {
	char data;
	struct Tree* left;
	struct Tree* right;
} tree;

tree* createTree(char data) {
	tree* node = (tree*)malloc(sizeof(tree));
	node->data = data;
	node->left = NULL;
	node->right = NULL;
	return node;
}

void insertTree(tree* parent, tree* left, tree* right) {
	parent->left = left;
	parent->right = right; 
}

int main() {
	tree* a = createTree('1');
	tree* b = createTree('2');
	tree* c = createTree('3');
	tree* d = createTree('4');
	tree* e = createTree('5');
	tree* f = createTree('6');
	tree* g = createTree('7');
	insertTree(a, b, c);
	insertTree(b, d, e);
	insertTree(c, f, g);
	
	return 0;
}

树的结构构建

递归实现

前序遍历
void preOrder(tree* root) {
	if(root != NULL) {
		printf("%c ", root->data);
		preOrder(root->left);
		preOrder(root->right);
	}
}

image-20240411212731886

589. N 叉树的前序遍历

中序遍历
void midOrder(tree* root) {
	if(root != NULL) {
		midOrder(root->left);
		printf("%c ", root->data);
		midOrder(root->right);
	}
}

image-20240411212802716

94. 二叉树的中序遍历

后序遍历
void postOrder(tree* root) {
	if(root != NULL) {
		postOrder(root->left);
        postOrder(root->right);
		printf("%c ", root->data);
	}
}

image-20240411213111247

590. N 叉树的后序遍历

迭代实现

用迭代实现树的前中后序本质为,用栈储存当前节点的上一位节点,在当循环遍历到节点的结尾处,从栈顶弹出上一位的指针进行回溯,根据遍历的顺序的选择,探索另一边的节点或者继续回溯到上一位,并在合适的时候打印出节点相应数值(打印节点数值与进行回溯两者的顺序是根据遍历方式来决定的)。

前序遍历
void preOrderbyStack(tree* root) {
	if (root == NULL) {
		return;
	}
	tree* stack[10];
	int top = -1;
	tree* pmove = root;
	while (top != -1 || pmove) {
		while (pmove) {
			printf("%c ", pmove->data);
			stack[++top] = pmove;
			pmove = pmove->left;
		}
		if (top != -1) {
			pmove = stack[top--];
			pmove = pmove->right;
		}
	}
}
中序遍历
void midOrderbyStack(tree* root) {
	if (root == NULL) {
		return;
	}
	tree* stack[10];
	int top = -1;
	tree* pmove = root;
	while (top != -1 || pmove) {
		while (pmove) {
			stack[++top] = pmove;
			pmove = pmove->left;
		}
		if (top != -1) {
			pmove = stack[top--];
            printf("%c ", pmove->data);
			pmove = pmove->right;
		}
	}
}
后序遍历
void postOrderbyStack(tree* root) {
	if (root == NULL) {
		return;
	}
	tree* stack[10];
	int top = -1;
	tree* mark = NULL; 
	tree* pmove = root;
	while (pmove) {
		stack[++top] = pmove;
		pmove = pmove->left;
	}
	while (top != -1) {
		pmove = stack[top--];
		if (pmove->right == NULL || pmove->right == mark) {
			printf("%c ",pmove->data);
			mark = pmove;
		} else {
			stack[++top] = pmove;
			pmove = pmove->right;
			while (pmove) {
				stack[++top] = pmove;
				pmove = pmove->left;
			}
		}	
	}
} 

在后序遍历的时候,我们需要一个标记去确认当前节点的右子树节点已经进行遍历,若检测到已经进行标记就将中间节点输出。

层次遍历

102. 二叉树的层序遍历

由于c语言并没有现成的栈和队列结构,为了方便,我直接使用c++进行层次遍历的编写

class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode* root) {
        queue<TreeNode*> que;
        vector<vector<int>> res;

        if (root) {
            que.push(root);
        }
        
        while (!que.empty()) {
            int n = que.size();
            vector<int> path;
            while (n--) {
                TreeNode* temp = que.front();
                que.pop();
                path.push_back(temp->val);
                if (temp->left) {
                    que.push(temp->left);
                }
                if (temp->right) {
                    que.push(temp->right);
                }
            }
            res.push_back(path);
        }
        return res;
    }
};

对于二叉树的基础介绍到这里就结束了,希望能对读者有所帮助。

  • 10
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值