链式二叉树基操代码实现&OJ题目

因为普通二叉树的增删查改这些基本操作没太大意义,AVL,B树,红黑树的应用较广,所以这里主要是体会递归的思想。

1.代码实现

链式二叉树的本质就是递归,分治,分而治之的思想,将问题分解拆解为规模相似的子问题,直到不能再拆。

c 复制代码
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
typedef int BTDataType;
typedef struct BinaryTreeNode {
	BTDataType data;
	struct BinaryTreeNode* left;
	struct BinaryTreeNode* right;
}BTNode;

//新建一个节点
BTNode* BuyNode(BTDataType x) {
	BTNode* node = (BTNode*)malloc(sizeof(BTNode));
	if (node == NULL) {
		perror("malloc fail");
		return NULL;
	}
	node->data = x;
	node->left = NULL;
	node->right = NULL;
	
	return node;
}

//造一棵树用于人工测试
BTNode* CreateTree() {
	BTNode* node1 = BuyNode(1);
	BTNode* node2 = BuyNode(2);
	BTNode* node3 = BuyNode(3);
	BTNode* node4 = BuyNode(4);
	BTNode* node5 = BuyNode(5);
	BTNode* node6 = BuyNode(6);
	node1->left = node2;
	node1->right = node4;
	node2->left = node3;
	node4->left = node5;
	node4->right = node6;
	return node1;
}

//销毁
void Destory(BTNode** root) {
	if (*root == NULL)
		return;
	Destory(&((*root)->left));
	Destory(&((*root)->right));
	free(*root);
	*root = NULL;
}

void Destory2(BTNode* root) {
	if (root == NULL)
		return;
	Destory(root->left);
	Destory(root->right);
	free(root);
}

上述代码对应的树如下图所示

1.1 二叉树的遍历

链式二叉树的遍历

1.1.1 先序遍历

c 复制代码
//前序
void PreOrder(BTNode* root) {
	if (root == NULL) {
		printf("NULL ");
		return;
	}	
	printf("%d ", root->data);
	PreOrder(root->left);
	PreOrder(root->right);
}

1.1.2 中序遍历

c 复制代码
//中序
void InOrder(BTNode* root) {
	if (root == NULL) {
		printf("NULL ");
		return;
	}
	InOrder(root->left);
	printf("%d ", root->data);
	InOrder(root->right);
}

1.1.3 后序遍历

c 复制代码
//后序
void PostOrder(BTNode* root) {
	if (root == NULL) {
		printf("NULL ");
		return;
	}
	PostOrder(root->left);
	PostOrder(root->right);
	printf("%d ", root->data);
}

1.2 求节点个数

求链式二叉树的节点个数

不能用局部变量,每次调用函数,建立函数栈帧都会初始化;

不能用局部静态变量,局部静态变量都不能改,只有第一次调用执行初始化,调用第二棵树怎么办;

不能用静态变量

静态变量也不行

1.2.1 使用全局变量

c 复制代码
//方法一,使用全局变量,缺点:难以修改,而且会累加,除非每次使用函数前修改size=0;而且涉及到多线程中线程安全的问题
int size=0;
void TreeSize1(BTNode* root) {
	if (root == NULL)
		return;
	size++;
	TreeSize1(root->left);
	TreeSize1(root->right);
}

下面代码的测试结果为:6,12.

c 复制代码
int main() {
	BTNode* root = CreateTree();
	TreeSize1(root);
	printf("%d\n", size);
	TreeSize1(root);
	printf("%d\n", size);
	return 0;
}

下面代码的测试结果为:6,6.

c 复制代码
int main() {
	BTNode* root = CreateTree();
	TreeSize1(root);
	printf("%d\n", size);
	size = 0;
	TreeSize1(root);
	printf("%d\n", size);
	return 0;
}

1.2.2 变量引用

方法二:变量引用

c 复制代码
void TreeSize2(BTNode* root, int* psize) {
	if (root == NULL)
		return;
	(*psize)++;
	TreeSize2(root->left,psize);
	TreeSize2(root->right,psize);
}

1.2.3 分治

c 复制代码
//方法三:分治,分而治之
int TreeSize3(BTNode* root) {
	if (root == NULL)
		return 0;
	else
		return TreeSize3(root->left) + TreeSize3(root->right) + 1;
}

1.3 求树高

c 复制代码
//计算树的高度
int TreeHeight(BTNode* root) {
	if (root == NULL)
		return 0;
	int leftheight = TreeHeight(root->left);
	int rightheight = TreeHeight(root->right);
	return leftheight > rightheight? leftheight + 1 : rightheight + 1;
}

//切记不要写成下面这种,时间复杂度是O(2^n);递归千万不要弄丢返回值,因为每个返回值都是千辛万苦递归来的
int TreeHeight2(BTNode* root) {
	if (root == NULL)
		return 0;
	return TreeHeight2(root->left)> TreeHeight2(root->right)? TreeHeight2(root->left) + 1 : TreeHeight2(root->right) + 1;
}

拿下面这幅图举例,计算出值为3的节点左右子树高度各是0,发现TreeHeight2(root->left)不大于TreeHeight2(root->right),返回TreeHeight2(root->right),需要重新调用TreeHeight2(root->right),得到返回结果是1,返回节点2, 2左子树高度是1,右子树高度是0,左子树高度大于右子树,返回TreeHeight2(root->left)+1,但没记录,需要重新计算,计算出值为3的节点左右子树高度各是0,发现TreeHeight2(root->left)不大于TreeHeight2(root->right),返回TreeHeight2(root->right),需要重新调用TreeHeight2(root->right),得到返回结果是1这个过程需要重复一遍,接着返回结点1,结点1左子树高度是2,接着5右子树高度会计算两遍, 6右子树的高度会计算四遍,因为6的高度要计算两遍,得到4的返回值是2,接着4的高度要再计算一遍.

1.4 找特定值节点

找到值为X的结点并返回其指针

c 复制代码
BTNode* FindX(BTNode* root, BTDataType x) {
	if (root == NULL)
		return NULL;
	if (root->data == x)
		return root;
	BTNode* left = FindX(root->left, x);
	if (left != NULL)
		return left;
	BTNode* right = FindX(root->right, x);
	if (right != NULL)
		return right;
	return NULL;
}

1.5 层序遍历

层序遍历,借助队列

层序变量是一种BFS,严格来说,先序是DFS,但如果不考虑何时访问节点值,中序和后序也算DFS.

BFS不等价于二叉树的层序遍历,因为BFS, DFS不仅用于二叉树的遍历,还有二维数组(迷宫问题)、多维数组、图,DFS一般递归实现,非递归也可。

队列实现代码

c 复制代码
void LevelOrder(BTNode* root) {
	Queue q;
	QueueInit(&q);
	if (root == NULL)
		return;
	QueuePush(&q, root);
	while (!QueueEmpty(&q)) {
		BTNode* front = QueueFront(&q);
		QueuePop(&q);
		/*if(front!=NULL)*/ //这个地方其实是不用判断的,因为root不为空,则队头不为空;接着,只有当左右子树不为空,才会入队.
		printf("%d ", front->data);
		if (front->left != NULL)
			QueuePush(&q, front->left);
		if(front->right!=NULL)
			QueuePush(&q, front->right);
	}
	QueueDestroy(&q);
}

1.6 判断是否为完全二叉树

借助队列

c 复制代码
bool TreeComplete(BTNode* root) {
	Queue q;
	QueueInit(&q);
	if (root)
		QueuePush(&q, root);
	while (!QueueEmpty(&q)) {
		BTNode* front = QueueFront(&q);
		QueuePop(&q);
		if (front == NULL) {
			while (!QueueEmpty(&q)) {
				front = QueueFront(&q);
				QueuePop(&q);
				if (front != NULL) {
					QueueDestroy(&q);
					return false;
				}
			}
		}	
		else {
			QueuePush(&q, front->left);
			QueuePush(&q, front->right);
		}
	}
	QueueDestroy(&q);//本项目队列实现的是不带头结点的队列,进程走到这,队列已经为空啦,不销毁也可以,但如果是带哨兵位的队列,就要销毁;所以在这里还是销毁,软件工程上成为耦合,我的程序和队列到底如何实现的无关,怎么都是对的.
	return true;
}

1.7 求第K层的节点个数

c 复制代码
//计算第K层的节点个数(根节点为第一层)
int KLevel(BTNode* root, int k) {
	assert(k > 0);
	if (root == NULL)
		return 0;
	if (k == 1)
		return 1;
	else
		return KLevel(root->left,k-1) + KLevel(root->right,k-1);		
}

1.8 求叶子结点个数

c 复制代码
int BinaryTreeLeafSize(BTNode* root) {
	if (root == NULL)
		return 0;
	if (root->left == NULL && root->right == NULL)
		return 1;
	else
		return BinaryTreeLeafSize(root->left) + BinaryTreeLeafSize(root->right);
}

2. OJ题

2.1 相同的树

100. 相同的树//接口型

c 复制代码
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     struct TreeNode *left;
 *     struct TreeNode *right;
 * };
 */
bool isSameTree(struct TreeNode* p, struct TreeNode* q) {
    if(p==NULL && q==NULL)
        return true;
    if(p==NULL || q==NULL)
        return false;
    
    //p!=NULL q!=NULL
    if(p->val!=q->val)//这个地方不能用p!=q,因为此时p->val可能等于q->val
        return false;
    return isSameTree(p->left,q->left)&&isSameTree(p->right,q->right);
}

2.2 二叉树的最大深度

104. 二叉树的最大深度

c 复制代码
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     struct TreeNode *left;
 *     struct TreeNode *right;
 * };
 */
int maxDepth(struct TreeNode* root) {
    if(root==NULL)
        return 0;
    int left=maxDepth(root->left);
    int right=maxDepth(root->right);
    return left>right?left+1:right+1;
}

2.3 二叉树的前序遍历

二叉树的先序序列存储到数组中,返回数组的起始地址;注意在先序遍历的时候传的是i的指针,不然在递归子程序中修改的变量,其栈帧销毁后,不改变源程序变量的值,修改形参不改变实参。
144. 二叉树的前序遍历

c 复制代码
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     struct TreeNode *left;
 *     struct TreeNode *right;
 * };
 */
/**
 * Note: The returned array must be malloced, assume caller calls free().
 */

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

void PreOrder(struct TreeNode* root,int *a, int* pi){
     if(root==NULL)
        return;
    a[*pi]=root->val;
    (*pi)++;
    PreOrder(root->left, a,pi);
    PreOrder(root->right, a,pi);
}

int* preorderTraversal(struct TreeNode* root, int* returnSize) {
    *returnSize=TreeSize(root);
    int* a=(int*)malloc(*returnSize*sizeof(int));
    int i=0;
    PreOrder(root,a,&i);
    return a;
}

2.4 翻转二叉树

将二叉树进行镜像翻转
226. 翻转二叉树

c 复制代码
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     struct TreeNode *left;
 *     struct TreeNode *right;
 * };
 */
struct TreeNode* invertTree(struct TreeNode* root) {
    if(root==NULL)
        return NULL;
    
    struct TreeNode* tmp=root->left;
    root->left=root->right;
    root->right=tmp;

    invertTree(root->left);
    invertTree(root->right);

    return root;
}

2.5 另一棵树的子树

树A是否为树B的子树,将树A与树B的每一个子树进行判断是否为同一棵树
572. 另一棵树的子树

c 复制代码
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     struct TreeNode *left;
 *     struct TreeNode *right;
 * };
 */

bool isSameTree(struct TreeNode* p, struct TreeNode* q) {
    if(p==NULL&&q==NULL)
        return true;
    if(p==NULL||q==NULL)
        return false;
    if(p->val==q->val)
    {
        return isSameTree(p->left,q->left)&&isSameTree(p->right,q->right);
    }
    return false;
}

bool isSubtree(struct TreeNode* root, struct TreeNode* subRoot) {
    if(root==NULL)
        return false;
    if(isSameTree(root,subRoot))
        return true;
    return isSubtree(root->left,subRoot)||isSubtree(root->right,subRoot);//这个地方用isSub不是isSame,因为后者范围窄了
}

2.6 单值二叉树

整个二叉树的值是否为同一个值
965. 单值二叉树

c 复制代码
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     struct TreeNode *left;
 *     struct TreeNode *right;
 * };
 */
bool isUnivalTree(struct TreeNode* root) {
    if(root==NULL)
        return true;
    if(root->left!=NULL&&root->left->val!=root->val)
        return false;
    if(root->right!=NULL&&root->right->val!=root->val)
        return false;
    return isUnivalTree(root->left)&&isUnivalTree(root->right);
}

2.7 二叉树遍历

将输入的先序序列转化成中序序列输出
TSINGK110 二叉树遍历//IO型

c 复制代码
#include <stdio.h>

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

struct TreeNode* CreateTree(char* a,int* pi){
    if(a[*pi]=='#'){
        (*pi)++;
        return NULL;
    }   
    struct TreeNode* root=(struct TreeNode*)malloc(sizeof(struct TreeNode));
    root->val=a[(*pi)++];
    root->left=CreateTree(a, pi);
    root->right=CreateTree(a, pi);
    return root;
}

void InOrder(struct TreeNode* root){
    if(root==NULL)
        return;
    InOrder(root->left);
    printf("%c ",root->val);
    InOrder(root->right);
}


int main() {
    char a[100];
    scanf("%s",a);
    int i=0;
    struct TreeNode* root=CreateTree(a, &i);
    InOrder(root);
    return 0;
}

2.8 对称二叉树

101. 对称二叉树

c 复制代码
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     struct TreeNode *left;
 *     struct TreeNode *right;
 * };
 */
 
 struct TreeNode* invertTree(struct TreeNode* root) {
    if(root==NULL)
        return NULL;
    
    struct TreeNode* tmp=root->left;
    root->left=root->right;
    root->right=tmp;

    invertTree(root->left);
    invertTree(root->right);

    return root;
}

bool isSameTree(struct TreeNode* p, struct TreeNode* q) {
    if(p==NULL && q==NULL)
        return true;
    if(p==NULL || q==NULL)
        return false;
    
    //p!=NULL q!=NULL
    if(p->val!=q->val)//这个地方不能用p!=q,因为此时p->val可能等于q->val
        return false;
    return isSameTree(p->left,q->left)&&isSameTree(p->right,q->right);
}

bool isSymmetric(struct TreeNode* root) {
    if(root==NULL)
        return true;
    struct TreeNode* right=invertTree(root->right);
    return isSameTree(root->left,right);
    
}
相关推荐
风筝在晴天搁浅2 小时前
hot100 25.K个一组翻转链表
数据结构·链表
小十一再加一2 小时前
【初阶数据结构】栈和队列
数据结构
长安er3 小时前
LeetCode136/169/75/31/287 算法技巧题核心笔记
数据结构·算法·leetcode·链表·双指针
_w_z_j_4 小时前
最小栈(栈)
数据结构
fufu03114 小时前
Linux环境下的C语言编程(四十八)
数据结构·算法·排序算法
思成Codes6 小时前
数据结构:基础线段树——线段树系列(提供模板)
数据结构·算法
卜锦元8 小时前
Golang后端性能优化手册(第三章:代码层面性能优化)
开发语言·数据结构·后端·算法·性能优化·golang
2401_841495649 小时前
【LeetCode刷题】打家劫舍
数据结构·python·算法·leetcode·动态规划·数组·传统dp数组
冰西瓜6009 小时前
STL——vector
数据结构·c++·算法