文章目录
- 1.相同的树
- 2.对称的二叉树
- 3.二叉树的最大深度
- 4.将有序数组转化为二叉搜索树
- 5.平衡二叉树
- 6.二叉树的最小深度
- 7.路径总合
- 8. 完全二叉树的节点个数
- 9.翻转二叉树
- 10.二叉树的所有路径
- 11.左叶子之和
- 12. 二叉搜索树中的众数
- 13.二叉搜索树的最小绝对差
- 14.二叉树的直径
- 15.二叉树的深度
- 16.另一棵子树
- 17.根据二叉树创建字符串
- 18.合并二叉树
- 19.二叉树的层平均值
- 20. 两数之和IV ---- 输入二叉搜索树
- 21.二叉树中的第二小的节点
- 22. 二叉树中的搜索
- 23.数据流中的第K大元素(Tag)
- 24. 叶子相似的树
- 25.递增顺序搜索树
- 26.二叉搜索树的范围和
- 27.单值二叉树
- 28. 二叉树的堂兄弟
- 29.计算布尔二叉树的值
- 30.开幕式焰火
1.相同的树
- 同时递归遍历其所给的两个树的每个节点。
- 对其进行挨个比较就好。
/**
* 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) || (p != NULL && q == NULL))
{
return false;
}
//走到这里,就证明两棵树的当前两个节点都有值,那么就去比较他俩的值
if(p -> val != q -> val)
{
return false;
}
return isSameTree(p -> left,q -> left) && isSameTree(p -> right,q -> right);
}
2.对称的二叉树
这道题和上面的想同的树两道题可以说是一模一样的,如果真正的懂了上面那道题,这道题只需要改一句话就好了。
咱们可以尝试将例题中的树拆分成两课树。
将p的左孩子和q的右孩子去对应,p的右孩子和q的左孩子去作比较,这样就好了
代码如下,直接俄调用上一题的函数,只是改了一行而已。
/**
* 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) || (p != NULL && q == NULL))
{
return false;
}
//走到这里,就证明两棵树的当前两个节点都有值,那么就去比较他俩的值
if(p -> val != q -> val)
{
return false;
}
return isSameTree(p -> left,q -> right) && isSameTree(p -> right,q -> left);
}
bool isSymmetric(struct TreeNode* root)
{
return isSameTree(root-> left,root -> right);
}
3.二叉树的最大深度
我们同样还是运用递归的方式去实现,做了这几道题,也能感觉出来点啥,这几种方式,其实跟在学习数据结构图的遍历的时候,那里有一个深度优先算法,就是一条路走到黑的那种,其实还是很类似的。
- 先去判断它的左孩子,再去递归判断它的右孩子。
- 而当前这棵小树的深度又是Max(left,right)+1;
/**
* 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 i,j;
if(root -> left != NULL)
{
//左孩子不为空,进去递归求值
i = maxDepth(root -> left);
}
else
{
//左孩子为空,为 0 即可,
i = 0;
}
if(root -> right != NULL)
{
j = maxDepth(root -> right);
}
else
{
j = 0;
}
return i > j ? i+1:j+1;
}
上面的代码长,但是方便理解,下面的代码和上面的原理是一样的
int Max(int x, int y)
{
return x > y ? x : y;
}
int maxDepth(struct TreeNode* root)
{
if(root == NULL)
{
return 0;
}
return Max(maxDepth(root -> left), maxDepth(root -> right)) + 1;
}
4.将有序数组转化为二叉搜索树
这道题在刷数组的时候遇到过,当时也没有仔细写清楚题解。
BST树的中序遍历就是升序序列
因为题目中的数组已经是升序的排放好了,所以数组中间的那个数,一定是BST树的根节点。
然后左孩子的话,就去(left,mid-1)这个区间去递归实现
而右孩子的就去(mid+1,right)这个区间去。
- 如果left > right ,那么返回NULL即可,,代表没有值了
- mid 一定是当前子树的根节点。
- 然后分别去递归构造其左右孩子即可。
struct TreeNode* CreateTree(int* nums,int left,int right)
{
if(left > right)
{
return NULL;
}
else
{
struct TreeNode* root = (struct TreeNode*)malloc(sizeof(struct TreeNode));
int mid = (left + right)/2;
root -> val = nums[mid];
root -> left = CreateTree(nums,left,mid-1);
root -> right = CreateTree(nums,mid+1,right);
return root;
}
}
struct TreeNode* sortedArrayToBST(int* nums, int numsSize) {
return CreateTree(nums,0,numsSize-1);
}
5.平衡二叉树
平衡二叉树的条件是左右子树的高度差不能超过1.
我们上边已经可以求出二叉树的深度了,所以去求出每个二叉树之间的深度差不也很简单么。
方法一:从上往下
- 从上到下,对于每课树的求其深度。
- 求出来后,判断是否满足绝对值是1。
bool isBalanced(struct TreeNode* root)
{
if(root == NULL)
{
return true;
}
int i,j;
i = Depth(root -> left);
j = Depth(root -> right);
if(abs(i - j) > 1)
{
return false;
}
return isBalanced(root -> left) && isBalanced(root ->right);
}
能发现,从上往下求深度,其实对于没课子树都有在重复的进行递归,求深度,时间上并不优秀。
放法二:从下往上
如果可以从下往上求的话,就不会一直重复的求子树的深度。
- 从一开始然后就一直往下左孩子下面走,求出左孩子的深度之后再去求右孩子的深度。
- 如果正常则返回深度,不平衡的情况下,将其当前的深度设置成-1这里的-1没有任何的特殊含义
- 只是一个标志,告诉咱这颗子树已经不平衡了,就ok了。
int Max(int x, int y)
{
return x > y ? x : y;
}
//求深度
int Depth(struct TreeNode* root)
{
if(root == NULL)
{
return 0;
}
int lefthight = Depth(root -> left);
int righthight = Depth(root -> right);
//如果人任意一颗子树的高度是 -1 则代表不平衡,所以到根节点也返回-1即可
if(abs(lefthight - righthight) > 1 || lefthight == -1 || righthight == -1)
{
return -1;
}
else
{
return Max(lefthight,righthight)+1;
}
}
bool isBalanced(struct TreeNode* root)
{
//如果深度是 -1 则代表不平衡
return Depth(root) >= 0;
}
这个办法对于遍历二叉树的深度,只进行了一次,比上一个时间上提升了不少。
6.二叉树的最小深度
同样是求二叉树的深度,但是不同的是,我们每次递归的时候需要得到的是各个子树的最小的那个深度。
- 如果当前的节点即没有左孩子也没有右孩子,那么就是叶子节点,所以返回1
- 重点是每次取出那个最小的值。
- 还有就是最开始有一个 root == NULL的条件后面还需判断,因为最开始的那个仅仅是判断第一次进去这个函数的时候,如果以后不判断就会出问题比如下面这张图
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* };
*/
int Min(int x, int y)
{
return x > y ? y : x;
}
int minDepth(struct TreeNode* root)
{
if(root == NULL)
{
return 0;
}
if(root -> left == NULL && root -> right == NULL)
{
//叶子节点
return 1;
}
int ans = INT_MAX;
if(root -> left != NULL)
{
ans = Min(minDepth(root -> left),ans);
if(root -> right != NULL)
{
ans = Min(minDepth(root -> right),ans);
}
return ans + 1;
}
7.路径总合
这道题我刚开始想的是如何记录走过的节点相加起来的数 等于 targetSum 但是想不同,发现题解是走一个减去一个,我666啊,有这个思路就可以做了
- 首先必须得是叶子节点,然后去判断是否和当前的targetSum相等。
- 如果相等的话,返回true就好了。
- 没传一次targetSum - 去一下当前节点的值。
- 注意最后的是 ||
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* };
*/
bool hasPathSum(struct TreeNode* root, int targetSum)
{
if(root == NULL)
{
return false;
}
//该节点是叶子节点
if(root -> left == NULL && root -> right == NULL)
{
//如果相等 返回 true 就好了
return targetSum == root -> val;
}
return hasPathSum(root -> left,targetSum - (root -> val)) ||
hasPathSum(root -> right,targetSum - (root -> val));
}
8. 完全二叉树的节点个数
一共有多少节点,也就是遍历一遍二叉树就好了,我们学会的三种遍历方式,进行遍历,拿上一个变量记录一下就行。
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* };
*/
void Inorder(struct TreeNode* T,int* num)
{
if(T == NULL)
{
return;
}
Inorder(T -> left,num);
(*num)++;
Inorder(T -> right,num);
}
int countNodes(struct TreeNode* root)
{
int ans = 0;
Inorder(root,&ans);
return ans;
}
这样的时间很慢,题解中提到了可以用位运算,位运算不是很清楚就先算了。
9.翻转二叉树
同样是采取左右根的后序遍历方式,对其每个节点的左右孩子进行翻转。
/**
* 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 root;
}
struct TreeNode* leftchild = invertTree(root->left);
struct TreeNode* rightchild = invertTree(root->right);
root -> left = rightchild;
root -> right = leftchild;
return root;
}
10.二叉树的所有路径
方法一:DFS
运用(DFS)深度优先算法,需要一个栈来辅助。
- 首先去找第一个叶子节点,如果当前的不是叶子节点,那么就去将该节点入栈。
- 当发现当前节点是叶子节点的时候,开始去构造当前的路径
- 运用spritnf函数,同时记录自己当前的长度,下面代码中 tmp + len 不理解可以看下图:
- 构造好每一个的路径,就可以导入paths数组中去了.
/**
* 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().
*/
void ConstructionPaths(char** paths,struct TreeNode* root,int* returnSize,int* stack, int top)
{
if(root == NULL)
{
return;
}
if(root -> left == NULL && root -> right == NULL)
{
//找到叶子节点了开始录入路劲
char* tmp = (char*)malloc(sizeof(char) * 101);
int inde = 0;
int len = 0;
//添加栈中的路径
for (int i = 1; i <= top; i++)
{
len += sprintf(tmp + len,"%d->",stack[i]);
}
//添加当前叶子节点
sprintf(tmp + len,"%d",root -> val);
paths[(*returnSize)++] = tmp;
}
else
{
//不是叶子节点,入栈
stack[++top] = root -> val;
ConstructionPaths(paths,root -> left,returnSize,stack,top);
ConstructionPaths(paths,root -> right,returnSize,stack,top);
}
}
char** binaryTreePaths(struct TreeNode* root, int* returnSize)
{
char** paths = (char**)malloc(sizeof(char*) * 101);
int stack[102] = {0}; //栈
int top = 0;
*returnSize = 0;
ConstructionPaths(paths,root,returnSize,&stack,top);
return paths;
}
栈也会随着递归而变化的,所以没必要进行出栈的这一算法。
方法二:BFS
还有一种是(BFS)广度优先搜索算法,这一算法需要运用两个队列来实现。(节点队列)(路径队列)
- 首先将其根节点入队列,
- 然后进行出队列的这一循环过程中,如果当前队首元素是叶子节点,将其队列中的路径直接导出即可
- 如果不是叶子节点,那么就去将该节点入节点队列,从而将其路径入路径队列。
队列中的front 和 rear 是同时指向两个队列的
#define MAX_SIZE 101
char** binaryTreePaths(struct TreeNode* root, int* returnSize)
{
//答案 -- 最后的全部路径
char** paths = (char**)malloc(sizeof(char*) * MAX_SIZE);
*returnSize = 0;
//节点队列
struct TreeNode** nodeQueue = (struct TreeNode**)malloc(sizeof(struct TreeNode*) * MAX_SIZE);
//每个节点对应的路径队列
char** pathQueue = (char**)malloc(sizeof(char*) * MAX_SIZE);
//两个队列的前后指针,同时运行的
int rear = 0, front = 0;
//将根节点去入队列
char* tmp = (char*)malloc(sizeof(char) * MAX_SIZE);
sprintf(tmp,"%d",root -> val);
//入队列
nodeQueue[rear] = root;
pathQueue[rear++] = tmp;
while(front < rear)
{
//出队列
struct TreeNode* curNode = nodeQueue[front];
char* curPath = pathQueue[front++];
if(curNode -> left == NULL && curNode -> right == NULL)
{
//当前队首节点是叶子
paths[(*returnSize)++] = curPath;
}
else
{
//不是叶子
int len = strlen(curPath);
//左孩子入队列
if(curNode -> left != NULL)
{
char* tmp = (char*)malloc(sizeof(char) * MAX_SIZE);
//构造新的路径
for (int i = 0; i < len; i++)
{
tmp[i] = curPath[i];
}
sprintf(tmp + len,"->%d",curNode -> left -> val);
//将新的节点以及路径入队列
nodeQueue[rear] = curNode -> left;
pathQueue[rear++] = tmp;
}
//右孩子入队列
if(curNode -> right != NULL)
{
char* tmp = (char*)malloc(sizeof(char) * MAX_SIZE);
//构造新的路径
for (int i = 0; i < len; i++)
{
tmp[i] = curPath[i];
}
sprintf(tmp + len,"->%d",curNode -> right->val);
//将新的节点以及路径入队列
nodeQueue[rear] = curNode -> right;
pathQueue[rear++] = tmp;
}
}
}
return paths;
}
11.左叶子之和
方法一:DFS
用深度优先算法:当前节点的有左孩子,并且左孩子是叶子节点,那么就记录下来。
bool IsLeaf(struct TreeNode* node)
{
if(node -> left == NULL && node -> right == NULL)
{
return true;
}
else
{
return false;
}
}
void DFS(struct TreeNode* root, int* sum)
{
if(root == NULL)
{
return;
}
DFS(root -> left,sum);
if((root -> left != NULL) && (IsLeaf(root -> left)))
{
//左孩子是叶子节点。
*sum += (root -> left -> val);
}
DFS(root -> right,sum);
}
int sumOfLeftLeaves(struct TreeNode* root)
{
if(root == NULL)
{
return 0;
}
int sum = 0;
DFS(root,&sum);
return sum;
}
方法二:BFS
广度优先搜索算法
- 和上一题一样,先将根节点入队列。
- 然后去判断当前节点的左孩子是否是叶子,出队列的捅死再将其左右孩子入队列即可。
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* };
*/
//判断当前节点是否是叶子节点
bool IsLeaf(struct TreeNode* node)
{
if(node -> left == NULL && node -> right == NULL)
{
return true;
}
else
{
return false;
}
}
int sumOfLeftLeaves(struct TreeNode* root)
{
if(root == NULL)
{
return 0;
}
int sum = 0;
struct TreeNode** nodeQueue = (struct TreeNode**)malloc(sizeof(struct TreeNode*) * 10001);
int front = 0, rear = 0;
//根节点入队列
nodeQueue[rear++] = root;
while(front < rear)
{
//出队列
struct TreeNode* curNode = nodeQueue[front++];
//判断
if( curNode -> left != NULL && IsLeaf(curNode -> left))
{
//是叶子节点
sum += curNode -> left -> val;
}
//将其左右孩子入队列
if(curNode -> left != NULL)
{
nodeQueue[rear++] = curNode -> left;
}
if(curNode -> right != NULL)
{
nodeQueue[rear++] = curNode -> right;
}
}
return sum;
}
12. 二叉搜索树中的众数
这道题暴力即可求解,任意一种遍历方式遍历二叉树,然后去记录其各个数出现的次数,遍历出出现最多次数的数即可,这样做也可以,而这种方式说到底也是运用数组和哈希表的方式去做,可是时间上就不尽人意了
void InorderTraversal(struct TreeNode* root)
{
if(root == NULL)
{
return;
}
InorderTraversal(root -> left);
insert(root -> val); //插入哈希表
InorderTraversal(root -> right);
}
int* findMode(struct TreeNode* root, int* returnSize)
{
int* ans = (int*)malloc(sizeof(int) * 1000);
*returnSize = 0;
InorderTraversal(root);
int i,maxnum = INT_MIN;
for (i = 0; i < 100; i++)
{
Node* cur = hashtable[i];
while(cur != NULL)
{
if(cur -> value > maxnum)
{
maxnum = cur -> value;
}
cur = cur -> next;
}
}
for (i = 0; i < 100; i++)
{
Node* cur = hashtable[i];
while(cur != NULL)
{
if(cur -> value == maxnum)
{
ans[(*returnSize)++] = cur -> key;
}
cur = cur -> next;
}
}
//清空哈希
Clear();
return ans;
}
方法二:leetcode题解
因为是二叉搜索树(BST)所以他的中序遍历一定是一个和数组一样的有序序列。
-
把他想象成数组众的元素,从第一个开始遍历
-
因为它是有序的,所以用base 来记录当前这一段连续的数字是谁,x从上往下也就是数组众的从左往右
-
当base == x 那么就去记录出现的次数 count 自增即可
-
当base != x时候,证明此阶段的数遍历完了,去更新新的base以及出现次数 base = x count = 1
-
接着就是去判断 count 和maxCount是否一样,
-
如果一样,直接插入ans数组中就好哦了
-
如果count > maxCount 了就说明出现新的众数了,将ans数组清空,重新记录下当前的base。
int* ans;
int ansSize;
int base,count,maxCount;
void UpData(int x)
{
if(x == base)
{
count++;//自增即可。
}
else
{
//新的数出现
base = x; //
count = 1;
}
if(count == maxCount)
{
//当前base 出现的次数 等于众数次数
ans[ansSize++] = base;
}
if(count > maxCount)
{
//新的众数出现
maxCount = count;
ansSize = 0;
ans[ansSize++] = base;
}
}
void DFS(struct TreeNode* root)
{
if(root == NULL)
{
return;
}
DFS(root -> left);
UpData(root -> val);
DFS(root -> right);
}
int* findMode(struct TreeNode* root, int* returnSize)
{
base = count = maxCount = 0;
ansSize = 0;
ans = (int*)malloc(sizeof(int) * 4001);
DFS(root);
*returnSize = ansSize;
return ans;
}
13.二叉搜索树的最小绝对差
还是二叉搜索树,所以对其进行中序遍历的结果就是一个升序的,然后将其存放到数组中去,最后在一个升序的数组中去找最小绝对差值谁也会把。
void InorderTraversal(struct TreeNode* root,int* result,int* size)
{
if(root == NULL)
{
return;
}
InorderTraversal(root -> left,result,size);
result[(*size)++] = root -> val;
InorderTraversal(root -> right,result,size);
}
int getMinimumDifference(struct TreeNode* root)
{
int* result = (int*)malloc(sizeof(int) * 40001);
int size = 0;
InorderTraversal(root,result,&size);
int i,minGap = INT_MAX;
for (i = 1; i < size; i++)
{
int gap = result[i] - result[i-1];
if(minGap > gap)
{
minGap = gap;
}
}
return minGap;
}
这种办法谁也会,但是咱可以再中序遍历结束时候就可以算出差值,没有必要再多浪费一个数组空间,以及再遍历一遍它。
- 我们拿一个prev的指针记录着上一个数据是什么
- 然后比较每次求出的差值大小就好了
要注意的是,得使用二级指针,因为你要修改指针的值嘛,当然你也可以拿一个变量去记录值,这都是无所谓的。
void InorderTraversal(struct TreeNode* root,struct TreeNode** prev, int* minGap)
{
if(root == NULL)
{
return;
}
InorderTraversal(root->left,prev,minGap);
if(*prev != NULL)
{
int gap = (root -> val) - ((*prev) -> val);
if(gap < *minGap)
{
*minGap = gap;
}
}
*prev = root;
InorderTraversal(root->right,prev,minGap);
}
int getMinimumDifference(struct TreeNode* root)
{
struct TreeNode* prev = NULL;
int minGap = INT_MAX;
InorderTraversal(root,&prev,&minGap);
return minGap;
}
14.二叉树的直径
这道题主要是考察,直径在这里的概念是啥,先观察上图,这一看就能看出来,最左边的孩子到最右边的孩子一定是那个最长的,想都不用想,没有什么比从一左边拐到右边长了,最左边的孩子的路径是2,最右边的是1.所以直径是 2+1 = 3.
你看下图:下图最左边的孩子是 4 还是 6. 最右边的孩子是3还是7,现在就会发现,说成左右孩子不合理,应该是 左子树的深度+右子树的深度 = 直径
而如何去就深度大家都是会的,上面也有求深度的题,下面就是求深度的代码。
- 所以每一个根节点,都有其所对应的左右子树。
- 只需要去判断每一棵子树之间的最大长度,也就是直径,来回去比较即可。
- 只需要哦在求深度的代码上加上一句话就好了
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* };
*/
int Max(int x, int y)
{
return x > y ? x : y;
}
int Depth(struct TreeNode* root,int* max)
{
if(root == NULL)
{
return 0;
}
int leftHight = Depth(root->left,max);
int rightHight = Depth(root -> right,max);
if(leftHight + rightHight > *max)
{ //左子树+右子树的深度就是最长路径
*max = leftHight + rightHight;
}
return Max(leftHight,rightHight)+1;
}
int diameterOfBinaryTree(struct TreeNode* root)
{
int max = INT_MIN;
int depth = Depth(root,&max);
//printf("depth = %d \n",depth);
return max;
}
15.二叉树的深度
这道题的思想说到底还是运用深度优先搜索算法(DFS)
- 用一个变量(slope)随时更新坡度。
- 而DFS函数本身则是负责求出当前节点左孩子和右孩子的val之和
- 如果是空指针则返回0,否则的话就直接返回 左孩子的值 + 右孩子的值 + 自身的值。
int DFS(struct TreeNode* root,int* slope)
{
if (root == NULL)
{
return 0;
}
int leftSum = 0, rightSum = 0;
//分别计算左右孩子的和
leftSum += DFS(root->left, slope);
rightSum += DFS(root->right, slope);
*slope += abs(leftSum - rightSum);
return leftSum + rightSum + root -> val;
}
int findTilt(struct TreeNode* root)
{
int slope = 0;
DFS(root,&slope);
return slope;
}
16.另一棵子树
在刷二叉树当中,第一题中就出现了判断两个树是否相同,这无非就是去遍历二叉树中的每个节点,然后判断是否是于subRoot相同。
下面的方式是最简单粗暴的一种。
- 我们在递归遍历root时候是对其进行挨个节点的访问,然后让它与subRoot判断两树是否相同。
- 如果但凡有一个相同,直接返回true即可。
//传过来两颗树,判断这两课树是否想同
bool isSameTree(struct TreeNode* p,struct TreeNode* q )
{
//都是空指针
if(p == NULL && q == NULL)
{
return true;
}
//一个有值,另一个没有,那么就肯能相同
if((p == NULL && q != NULL) || (p != NULL && q == NULL))
{
return false;
}
//如果能走到这里,证明都不为空,所以去判断它的值
if(p -> val != q -> val)
{
return false;
}
return (isSameTree(p -> left,q -> left)) && (isSameTree(p -> right,q -> right));
}
bool isSubtree(struct TreeNode* root, struct TreeNode* subRoot)
{
if(root == NULL)
{
return false;
}
return isSubtree(root -> left,subRoot) ||
isSubtree(root -> right,subRoot) ||
isSameTree(root,subRoot);
}
17.根据二叉树创建字符串
这道题主要是有四种情况,搞清楚这四种情况,就能写出来了。
1. 左右孩子都没有:
对于这种来说,就啥也不用动,直接把自己的值写入就好了。
2. 左右孩子都有:
需要分别对它的左右孩子都加入括号。
3. 只有左孩子:
对他的右孩子加入括号
4. 只有右孩子:
相对于这种情况,你需要在左孩子上加入一个空括号,从而代表左孩子是空的,不然等结果出来又该如何判断他是左孩子还右孩子。
char* ans;
int size;
void DFS(struct TreeNode* root)
{
if(root == NULL)
{
return;
}
size += sprintf(ans + size,"%d",root -> val);
//如果当前节点 没有孩子,直接写入即可,不需要加任何括号
if(root -> left == NULL && root -> right == NULL)
{
return;
}
//如果当前节点 有两个孩子,需要在两个孩子的结果外都加上括号
if(root -> left != NULL && root -> right != NULL)
{
//左孩子
ans[size++] = '(';
DFS(root -> left);
ans[size++] = ')';
//右孩子
ans[size++] = '(';
DFS(root -> right);
ans[size++] = ')';
}
//只有左孩子,给左孩子加括号,不给右孩子加
if(root -> left != NULL && root -> right == NULL)
{
//左孩子
ans[size++] = '(';
DFS(root -> left);
ans[size++] = ')';
}
//只有右孩子,只给左孩子加上空括号,右孩子也加括号
if(root -> left == NULL && root -> right != NULL)
{
//左孩子
ans[size++] = '(';
ans[size++] = ')';
//右孩子
ans[size++] = '(';
DFS(root -> right);
ans[size++] = ')';
}
}
char* tree2str(struct TreeNode* root)
{
ans = (char*)malloc(sizeof(char) * 100000);
size = 0;
DFS(root);
ans[size] = '\0';
return ans;
}
18.合并二叉树
在数据结构中学过会构造二叉树的话,这就不是问题了,我先是死板一点的,将所有可能一一列举出来的。
1. 两个都为空的话,生成树自然也是空的
2. 两个都有值的话,生成树的节点就是他俩的和
3. 一个有一个没有的话,就去选择那一个有的值赋予即可.
//将 p q 合并
void Merge(struct TreeNode** T,struct TreeNode* p,struct TreeNode*q)
{
if(p == NULL && q == NULL)
{
(*T) = NULL;
}
else
{
(*T) = (struct TreeNode*)malloc(sizeof(struct TreeNode));
//两个都不为空 加起来的值
if(p != NULL && q != NULL)
{
(*T) -> val = p -> val + q -> val;
Merge((&(*T)->left),p -> left, q -> left);
Merge((&(*T)->right),p -> right, q -> right);
return;
}
if(p != NULL)
{
//左孩子不为空,只传左边的,右边的不动
(*T) -> val = p -> val;
Merge((&(*T)->left),p -> left, q);
Merge((&(*T)->right),p -> right, q);
}
else
{
//右孩子不为空,只传右边的,左边的不动
(*T) -> val = q -> val;
Merge((&(*T)->left),p, q -> left);
Merge((&(*T)->right),p, q -> right);
}
}
}
struct TreeNode* mergeTrees(struct TreeNode* root1, struct TreeNode* root2)
{
struct TreeNode* newT = NULL;
Merge(&newT,root1,root2);
return newT;
}
其实这也就DFS算法,官方题解更为简单,在代码上优化了不少,思想其实还是一样的。
19.二叉树的层平均值
如果会二叉树的层序遍历,这道题并不是难的,而层序遍历则需要构造出一个队列来。
- 首先将根节点入队列,保证队列中有元素。
- 然后每次循环时候,那一个变量number记录当前层元素个数,也就是目前队列中的元素个数。
- 然后挨个出队列的同时,加上自身的值,再将其孩子入队列就好了。
#define MAX_SIZE 10000
double* averageOfLevels(struct TreeNode* root, int* returnSize)
{
//队列
struct TreeNode** queue = (struct TreeNode**)malloc(sizeof(struct TreeNode*) * MAX_SIZE);
int front = 0, rear = 0;
//结果 答案
double* ans = (double*)malloc(sizeof(double) * MAX_SIZE);
*returnSize = 0;
queue[rear++] = root;
//队列不为空
while(front != rear)
{
int number = rear - front; //记录当前层有多少个元素
int size = number;
double sum = 0.0;
while(size > 0)
{
//出队列
root = queue[front++];
sum += root -> val;
if(root -> left != NULL)
{
queue[rear++] = root -> left;
}
if(root -> right != NULL)
{
queue[rear++] = root -> right;
}
size--;
}
ans[(*returnSize)++] = sum / number;
}
return ans;
}
20. 两数之和IV ---- 输入二叉搜索树
这个题目,勾起了各位的回忆没,从刚开始刷题,leetcode第一道题,两数之和。
有多少人倒在了第一题啊?是因为不熟悉这种刷题方式吧估计都是,反正我第一次刷题时候,C都没学完,就好奇的不行,想看看,我清楚的记得我当时是把,那个答案用printf打印出来了,然后以为它给了我一个main函数,我返回值是return 0;到了后来才慢慢的懂了。
- 二叉搜索树中序遍历即升序数组。
- 利用双指针一前,一后,去计算值,然后与所给的K进行比较。
- 从而选择是left++,还是right–
#define MAX_SIZE 10000
void Inorder(struct TreeNode* root,int* nums,int* size)
{
if(root == NULL)
{
return;
}
Inorder(root -> left,nums,size);
nums[(*size)++] = root -> val;
Inorder(root -> right,nums,size);
}
bool findTarget(struct TreeNode* root, int k)
{
int* nums = (int*)malloc(sizeof(int) * MAX_SIZE);
int size = 0;
//二叉搜索树中序遍历得到升序数组。
Inorder(root,nums,&size);
//双指针
int left = 0,right = size - 1;
while(left != right)
{
if(nums[left] + nums[right] < k)
{
left++;
}
else if(nums[left] + nums[right] > k)
{
right--;
}
else
{
return true;
}
}
return false;
}
还有一种办法就是利用哈希表来做了,用哈希表这种方式做的话,就没有必要一定用中序遍历的方式了。
- 去遍历二叉树的每一个节点,如果发现 k - x(当前的节点)在哈希表中没有,那么就将 x 插入哈希表
- 如果k - x 在哈希表中已经出现,直接返回true就好了。
代码中出现的哈希函数就不往出放了,太乱了,这整个哈希函数在上面的12题那个众数中也源码实现。
下面代码中之所以又套用了一个函数,是因为我的哈希表是全局变量,在leetcode上它调试别的测试用例,以前测试用例中的值还在哈希表中,所以我得提前清空一下。
#define TABLE_SIZE 10000
Node* hashtable[TABLE_SIZE];
bool helper(struct TreeNode* root,int k)
{
if(root == NULL)
{
return false;
}
int key = k - (root -> val);
if(Find(key) == NULL)
{
//如果哈希表中没有 k - x 的值 那么将 x 记录到哈希表中
insert(root -> val);
}
else
{
return true;
}
return helper(root -> left,k) || helper(root -> right,k);
}
bool findTarget(struct TreeNode* root, int k)
{
Clear();
return helper(root,k);
}
21.二叉树中的第二小的节点
我这道题最直观的,最先想到的就是数组+排序
- 遍历一遍二叉树,将其所有节点拷贝到数组中去
- 然后对数组进行排序。
- 最前面的就是最小的数么,
void Inorder(struct TreeNode* root, int* nums,int* size)
{
if(root == NULL)
{
return;
}
Inorder(root -> left,nums,size);
nums[(*size)++] = root -> val;
Inorder(root -> right,nums,size);
}
int cmp_int(void* x,void* y)
{
return *(int*)x - *(int*)y;
}
int findSecondMinimumValue(struct TreeNode* root)
{
int* nums = (int*)malloc(sizeof(int) * 10000);
int size = 0;
Inorder(root,nums,&size);
qsort(nums,size,sizeof(nums[0]),cmp_int);
int i;
for (i = 1; i < size; i++)
{
if(nums[i] != nums[i-1])
{
break;
}
}
//提前出来
if(i < size)
{
return nums[i];
}
else
{
return -1;
}
}
还有一种方法,
- 从题目中可以知道,相对于没棵子树,根节点永远是最小的。
- 所以说,当你发现根节点,比当前的这个ans里面的数据都大,那么久没必要去遍历其下边的了。
void DFS(struct TreeNode* root,int* ans,int fristVal)
{
if(root == NULL)
{
return;
}
//不等于 -1 就意味着找到了第二个了
// 如果根节点都大于了ans 那就没有必要去遍历它一下的了
if(*ans != -1 && root -> val >= *ans)
{
return;
}
//走到这里,就证明其根节点比当前的ans 里面的小,然后去和第一个最小节点的值去比较
if(root -> val > fristVal)
{
*ans = root -> val;
}
DFS(root -> left,ans,fristVal);
DFS(root -> right,ans,fristVal);
}
int findSecondMinimumValue(struct TreeNode* root)
{
int ans = -1;
int fristVal = root -> val; //最小值 第一个
DFS(root,&ans,fristVal);
return ans;
}
22. 二叉树中的搜索
他是一棵二叉搜索树,左子树的节点永远比根节点小,右子树的节点永远比根节点大。
有这句话,就够了。
struct TreeNode* searchBST(struct TreeNode* root, int val)
{
if(root == NULL)
{
return NULL;
}
if(val < root -> val)
{
return searchBST(root -> left, val);
}
else if(val > root -> val)
{
return searchBST(root -> right, val);
}
else
{
return root;
}
}
23.数据流中的第K大元素(Tag)
我看完这道题有点像在我学习B树的时候,去构造B树的过程中,挨个有序的插入。
所以我先按照数组的方式去写了写,但是时间耗时非常大,因为在数组中去要想有序的插入数据,就需要大量的挪动数据,所以不是很好。
其实下面的FindsuitIndex 和 Insert 可以合并成一个函数的。。。。
#define MAX_SIZE 100000
typedef struct
{
int* keys;
int size;
int k;
} KthLargest;
//找到和合适的索引
int FindSuitIndex(KthLargest* obj,int val)
{
int i;
for (i = 0; i < obj -> size; i++)
{
if(val < obj -> keys[i])
{
break;
}
}
return i;
}
//在该索引处插入
void Insert(KthLargest* obj, int val,int index)
{
int i;
//挪动数据
for (i = obj -> size; i > index; i--)
{
obj -> keys[i] = obj -> keys[i-1];
}
obj -> keys[index] = val;
obj -> size++;
}
KthLargest* kthLargestCreate(int k, int* nums, int numsSize)
{
KthLargest* tmp = (KthLargest*)malloc(sizeof(KthLargest));
tmp -> keys = (int*)malloc(sizeof(int) * MAX_SIZE);
tmp -> size = 0;
tmp -> k = k;
int i;
for (i = 0; i < numsSize; i++)
{
int index = FindSuitIndex(tmp,nums[i]);
Insert(tmp,nums[i],index);
}
return tmp;
}
int kthLargestAdd(KthLargest* obj, int val)
{
int index = FindSuitIndex(obj,val);
Insert(obj,val,index);
int k = obj -> k;
int i = obj -> size - 1;
while(i > 0 && k > 1)
{
k--;
i--;
}
return obj -> keys[i];
}
void kthLargestFree(KthLargest* obj)
{
free(obj -> keys);
free(obj);
}
完了我有个疑问:就下图中的第三大数,不应该是4吗? 为什么是5啊.
24. 叶子相似的树
这道题,第一时间想到的,最简单的方式还是将二叉树进行遍历,对其的叶子节点单独拿出来构造成一个数组,然后去比较两个数组就好了啊。
#define MAX_SIZE 10000
void CreateLeaf(struct TreeNode* root,int* nums,int* size)
{
if(root == NULL)
{
return;
}
//叶子节点
if(root -> left == NULL && root -> right == NULL)
{
nums[(*size)++] = root -> val;
}
CreateLeaf(root -> left,nums,size);
CreateLeaf(root -> right,nums,size);
}
bool leafSimilar(struct TreeNode* root1, struct TreeNode* root2)
{
int* leaf1 = (int*)malloc(sizeof(int) * MAX_SIZE);
int size1 = 0;
int* leaf2 = (int*)malloc(sizeof(int) * MAX_SIZE);
int size2 = 0;
CreateLeaf(root1,leaf1,&size1);
CreateLeaf(root2,leaf2,&size2);
//叶子数量不同不行
if(size1 != size2)
{
return false;
}
//比较两个数组是否相同
for (int i = 0; i < size1; i++)
{
if(leaf1[i] != leaf2[i])
{
return false;
}
}
return true;
}
25.递增顺序搜索树
他是一个二叉搜索数,所以,我们还是可以中中序遍历后,得到数组,然后构造二叉树,不过这次构造的二叉树是左子树全为空的。
#define MAX_SIZE 10000
//获取升序数组
void GetNums(struct TreeNode* root, int* nums, int* size)
{
if(root == NULL)
{
return;
}
GetNums(root -> left,nums,size);
nums[(*size)++] = root -> val;
GetNums(root -> right,nums,size);
}
//利用所给的数组构造二叉树
void CreateBiTree(struct TreeNode** root,int* nums, int* index,int size)
{
//构造完毕时候,赋值为NULL
if(*index >= size)
{
*root = NULL;
return;
}
(*root) = (struct TreeNode*)malloc(sizeof(struct TreeNode));
(*root) -> val = nums[(*index)++];
(*root) -> left = NULL;
CreateBiTree(&((*root) -> right),nums,index,size);
}
struct TreeNode* increasingBST(struct TreeNode* root)
{
int* nums = (int*)malloc(sizeof(int) * MAX_SIZE);
int size = 0;
int index = 0;
GetNums(root,nums,&size);
struct TreeNode* T = NULL;
CreateBiTree(&T,nums,&index,size);
return T;
}
上面这种方法中去遍历了一次二叉树,而在构造的时候又遍历了一遍,还额外的用了数组和树的空间。
还有一种办法可以中序遍历一遍二叉树就能得出答案,并且还不用额外的数组,直接在原树上进行修改。
-
需要一个头节点
-
-
我们还是对二叉树进行中序遍历,当root走到1节点时候
-
将curnode->right = root 并且 将root的左孩子改为NULL,尽管它本来就是空的。
-
然后就像遍历链表一样,将 curnode = root;
而走到2节点的时候也是这样的,就会把之前的关系切断。
void DFS(struct TreeNode* root,struct TreeNode** curNode)
{
if(root == NULL)
{
return;
}
DFS(root->left,curNode);
(*curNode) -> right = root;
root -> left = NULL;
//
*curNode = root;
DFS(root->right,curNode);
}
struct TreeNode* increasingBST(struct TreeNode* root)
{
//头节点
struct TreeNode* head = (struct TreeNode*)malloc(sizeof(struct TreeNode));
head -> right = head -> left = NULL;
//利用cur去在递归函数中遍历
struct TreeNode* curNode = head;
DFS(root,&curNode);
//使最后还能直接返回
return head -> right;
}
26.二叉搜索树的范围和
- 对该二叉搜索树进行一个中序遍历得到一个升序数组
- 在升序数组中选出符合[low,high]区间内的数加在一起。
void Inorder(struct TreeNode* root,int* nums, int* size)
{
if(root == NULL)
{
return;
}
DFS(root -> left, nums, size);
nums[(*size)++] = root -> val;
DFS(root -> right, nums, size);
}
int rangeSumBST(struct TreeNode* root, int low, int high)
{
int* nums = (int*)malloc(sizeof(int) * 100000);
int size = 0;
Inorder(root,nums,&size);
int i;
int sum = 0;
for (i = 0; i < size && nums[i] <= high; i++)
{
if(nums[i] >= low)
{
sum += nums[i];
}
}
return sum;
}
也可以不用数组,直接在遍历的时候对其进行计算
void Inorder(struct TreeNode* root,int low, int high, int* sum)
{
if(root == NULL)
{
return;
}
Inorder(root -> left, low, high, sum);
//在区间内
if(root -> val >= low && root -> val <= high)
{
*sum += root -> val;
}
//该节点如果都比high 大 就没有必要了,因为以后的都比该节点大
if(root -> val < high)
{
Inorder(root -> right, low, high, sum);
}
}
int rangeSumBST(struct TreeNode* root, int low, int high)
{
int sum = 0;
Inorder(root,low,high,&sum);
return sum;
}
而下面这种方式就在时间上提升了不少,
int rangeSumBST(struct TreeNode* root, int low, int high)
{
if(root == NULL)
{
return 0;
}
//当前节点上的值小于low 返回右子树上的值
if(root -> val < low)
{
return rangeSumBST(root -> right,low,high);
}
//当前节点上的值大于high 返回左子树上的值
if(root -> val > high)
{
return rangeSumBST(root -> left,low,high);
}
//当前节点上的值区间内,返回左右子树和当前节点的值
return root -> val + rangeSumBST(root -> left,low,high) + rangeSumBST(root -> right,low,high);
}
27.单值二叉树
嗯。。。。。
bool Helper(struct TreeNode* root,int val)
{
if(root == NULL)
{
return true;
}
return Helper(root -> left,val) && root -> val == val && Helper(root -> right,val);
}
bool isUnivalTree(struct TreeNode* root)
{
int val = root -> val;
return Helper(root,val);
}
28. 二叉树的堂兄弟
题目中所涉及到是否在一层,所以我这边就用广度优先搜索的方式了(BFS),这道题的关键点在于,如果在一层,还得去判断它的父亲节点是否是同一个的。
- 对于层序遍历二叉树,还是老套路,创建一个队列,然后将根节点入队列。
- 在出队列的时候,会将其孩子入队列,然后去判断x 和 y 是否都在同一个根节点下入队列。
- 如果在同一个根节点下入队列,那么肯定是一个父亲,如果不是,那么直接返回true就好了
- 可能有点绕,我就先这样表达吧。
看代码理解可能好点,和普通的层序遍历只是多了一个记录出现次数的变量,旁边都有注释的
bool isCousins(struct TreeNode* root, int x, int y)
{
int map[103] = {0};
map[x] = 1;
map[y] = 1;
//队列,层序遍历
struct TreeNode** queue = (struct TreeNode**)malloc(sizeof(struct TreeNode*) * 101);
int rear = 0,front = 0;
queue[rear++] = root;
while(front != rear)
{
int number = rear - front; //记录当前层有多少个元素
int count = 0; //记录当前层是否出现 x 和 y
//每一个根节点出队列
while(number > 0)
{
int tmpCount = 0; //记录当前节点是否同时出现了 x 和 y
//出队列
root = queue[front++];
//入孩子队列
if(root -> left != NULL)
{
queue[rear++] = root -> left;
if(map[root -> left -> val] == 1)
{
count++;
tmpCount++;
}
}
if(root -> right != NULL)
{
queue[rear++] = root -> right;
if(map[root -> right -> val] == 1)
{
count++;
tmpCount++;
}
}
//说明两个孩子的父亲节点都一致
if(tmpCount == 2)
{
return false;
}
number--;
}
//那两个孩子在一层,但是不是一个父亲
if(count == 2)
{
return true;
}
}
return false;
}
29.计算布尔二叉树的值
注意题目中是说完整二叉树,也就是完全二叉树,所以它不会出现只有右孩子没有左孩子的情况。
当我们发现此节点是叶子节点时候,返回当前前节点的值。
bool evaluateTree(struct TreeNode* root)
{
//此节点是叶子节点,那么值就是它自身
if(root -> left == NULL)
{
return root -> val;
}
if(root -> val == 2)
{
return evaluateTree(root -> left) || evaluateTree(root -> right);
}
else
{
return evaluateTree(root -> left) && evaluateTree(root -> right);
}
}
30.开幕式焰火
这道题呢,遍历二叉树的时候,然后将出现的数字记录在哈希表中。
然后再去查表即可。
void DFS(struct TreeNode* root,int* map)
{
if(root == NULL)
{
return;
}
DFS(root -> left, map);
map[root -> val]++;
DFS(root -> right,map);
}
int numColor(struct TreeNode* root)
{
int map[1001] = {0};
DFS(root,map);
int count = 0;
for (int i = 0; i < 1001; i++)
{
if(map[i] != 0)
{
count++;
}
}
return count;
}