目录
二叉树的数据结构天然满足递归。以前序遍历为例,访问根节点->左子树->右子树,访问到左子树树时,又同样地访问根节点->左子树->右子树... 因此可以用递归创建二叉树。
创建
用递归创建二叉树时,按照前序遍历输入数据,并且叶子节点用一个特殊符号表示(这里我用-1表示)。比如以下二叉树可以输入为:1 -1 2 3 -1 -1 -1
无入参有返回值的创建
这里我使用一级指针创建二叉树,二级指针创建的方式用visual studio Code运行会报错(有朋友知道为什么的请指教~)
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
};
struct TreeNode* creatTree() // 无参创建,函数的返回值是节点指针
{
int num;
scanf("%d", &num);
struct TreeNode* node = NULL; // 先创建一个节点
if (num != -1) {
struct TreeNode* node = (struct TreeNode*)malloc(sizeof(struct TreeNode));
node->val = num;
node->left = creatTree();
node->right = creatTree();
return node; // 最后返回根节点
} else {
node = NULL;
return NULL; // 返回NULL,给节点的左右孩子赋值
}
}
int main()
{
struct TreeNode* root = NULL;
root = creatTree();
int returnSize = 0;
int* a = preorderTraversal(root, &returnSize);
for (int i = 0; i < returnSize; i++) {
printf("%d ", a[i]);
}
}
有入参有返回值的创建
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
};
struct TreeNode* creatTree( struct TreeNode* node) // 有入参创建,函数的返回值是节点指针
{
int num;
scanf("%d", &num); // 无需在函数内部创建节点
if (num != -1) {
node = (struct TreeNode*)malloc(sizeof(struct TreeNode));
node->val = num;
node->left = creatTree(node->left); // 注意入参
node->right = creatTree(node->right);
return node; // 最后返回根节点
} else {
node = NULL;
return NULL; // 返回NULL,给节点的左右孩子赋值
}
}
int main()
{
struct TreeNode* root = NULL;
struct TreeNode* node = NULL;
root = creatTree(node);
}
比较两种创建方式,发现第二种有入参的创建node指针中的内容并不是最终创建的树,root指针中是最终创建的树,所以这种创建方式与第一种没有区别,反而更加麻烦,因为在进行函数自调用递归时需要额外输入参数。因此推荐第一种创建方式。
前序、中序、后序遍历
void preorder(struct TreeNode* root, int* res, int* returnSize)
{
if (root == NULL) {
return;
}
res[(*returnSize)++] = root->val; // *p++不加括号表示取地址内存,把P移动到下一位。这里需要对returnSize+1,所以需要加()
preorder(root->left, res, returnSize);
preorder(root->right, res, returnSize);
}
int* preorderTraversal(struct TreeNode* root, int* returnSize){
int* res = (int*)malloc(sizeof(int) * 102); // 102: 节点数不超过100
memset(res, 0, sizeof(int) * 102);
*returnSize = 0;
preorder(root, res, returnSize);
return res;
}
二叉树的中序和后序遍历与前序遍历差别不大
void InOrder(root)
{
if (root == NULL) return;
Inorder(root->left);
res[] = root->val;
Inorder(root->right);
}
void posOrder(root)
{
if (root == NULL) return;
posOrder(root->left);
posOrder(root->right);
res[] = root->val;
}
层序遍历
使用队列(queue)存储二叉树的节点;
变量front表示队列头,rear表示队列尾;
先将根节点入队,然后对根节点进行取值,并将根节点的左右孩子节点入队;再对队列中的下一个节点做相同的操作,以此类推,可以写成以下循环:
外层循环的终止条件是队列的首尾指针相等,也就是队列为空。
内层循环需要得到每一层的数组,首先用一个临时变量last记录每一层的队尾,将队尾与队首的下标相减得到每一层的个数,用于申请内存,并且内层循环的终止条件应该是队首指针 < 每一层的队尾指针。将队首出队即queueFront++,将出队节点的值存入返回内存中,并将它的左右孩子加入队列。内存循环结束后,完成了一层的遍历,将这一行的列数存入returnColumnSizes。
int** levelOrder(TreeNode *root, int* returnSize, int** returnColumnSizes)
{
TreeNode* queue[MAX] = {0};
*returnSize = 0;
int queueFront = 0;
int queueRear = 0;
queue[queueRead++] = root; // 先将根节点入队
TreeNode* cur = NULL; // 取队头
int** res = (int**) malloc(sizeof(int*) * MAX); // 返回的二级指针申请内存
*returnColumnSizes = (int*)malloc(sizeof(int) * MAX); // 返回一个数组,每个元素是每行的列数
while (queueFront != queueRear) { // 终止条件是首尾指针不相等
int last = queueRear; //记录每一层的队尾
res[(*returnSize)] = (int*)malloc(sizeof(int) * (last - queueFront)); // 对每个二级指针指向的数组进行内存申请
int col = 0;
while (quequeFont < last) {
cur = queue[queueFront++];
res[(*returnSize)][col++] = cur->val;
if (cur->left != NULL) {
queue[queueRear++] = cur->left;
}
if (cur->right != NULL) {
queue[queueRear++] = cur->right;
}
}
(*returnColumnSizes)[(*returnSize)++] = col;
}
return res;
}