前言
这是五月集训的第十七日,今日的训练内容是 广度优先搜索
解题报告
1.力扣LCP44
原题链接
题目概述
「力扣挑战赛」开幕式开始了,空中绽放了一颗二叉树形的巨型焰火。
给定一棵二叉树 root 代表焰火,节点值表示巨型焰火这一位置的颜色种类。请帮小扣计算巨型焰火有多少种不同的颜色。
解题思路
定义一个哈希表,然后定义一个函数使用广度优先搜索的方式对树进行遍历,在主函数中先将数填入哈希表,然后再遍历哈希表就可以了,至于遍历的方式将在源码剖析中写出。
源码剖析
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* };
*/
int hash[1001];
void count(struct TreeNode* root)
{
if(root)
{
hash[root -> val]++;
count(root -> right);
count(root -> left);
}
}
int numColor(struct TreeNode* root)
{
memset(hash, 0, sizeof(hash));
count(root);
int sum = 0;
for(int i = 1; i <= 1000; i++)
{
if(hash[i])
sum++;
}
return sum;
}
if(root)
只要结点不为空就继续遍历,这是递归的出口,出口具体是遍历到数的结点没有孩结点为止,否则就对其孩结点进行遍历。
2.力扣102
原题链接
题目概述
给你二叉树的根节点 root ,返回其节点值的 层序遍历 。 (即逐层地,从左到右访问所有节点)。
解题思路
见源码剖析的注释
源码剖析
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* };
*/
/**
* Return an array of arrays of size *returnSize.
* The sizes of the arrays are returned as *returnColumnSizes array.
* Note: Both returned array and *columnSizes array must be malloced, assume caller calls free().
*/
struct que{
struct TreeNode* node; //结构体分为结点域
struct que* next; //指针域
};
void que_enter(struct que* a,struct TreeNode *node){ //尾插法插入结点
struct que* tmp = a; //首先定义一个队列结点
while(tmp->next!=NULL){ //如果当前结点的下一个结点不为空时
tmp = tmp->next; //使得tmp变为指向的下一个结点
} //这一步的作用就是,使得结点指向队列的尾部之后
struct que *b=(struct que*)malloc(sizeof(struct que)); //申请一个新的队列结点b
b->node = node; //将树结点填入队列结点的结点域
b->next = NULL; //使得结点指向NULL
tmp->next = b; //然后再使得tmp指向的结点变为b
}
struct TreeNode* deQueue(struct que *queueHead) //这个函数返回一个树结点,结点就是当前的队列结点中的树结点
{
struct que* queueIt = queueHead->next; //定义一个队列结点指向头结点的下一个
struct TreeNode* node; //定义一个树结点
if(queueIt != NULL) //假如指向不是队尾
{
queueHead->next = queueIt->next; //头结点的下一个结点变为下下个结点
node = queueIt->node; //把当前结点的值给树结点
free(queueIt); //释放结点
return node; //返回树结点
}
return NULL; //如果是队尾 node中肯定没有值,直接返回NULL
}
void que_free(struct que *a){ //释放队列
struct que *tmp = a->next; //定义一个结点代表当前结点的下一个结点
struct que *pre = a->next; //定义一个结点代表前一个结点
if(tmp!=NULL){ //当前结点next结点不为NULL时(如果是NULL就只剩头结点了)
pre = tmp; //pre就等于tmp
tmp = tmp->next; //tmp变为下一个结点
free(pre); //随后释放pre,这时后续的结点(包括tmp)就都被断开了
}
free(a); //再释放头结点
}
void bfs(int *returnSize,int **returnColumnSizes,int **returnNum,struct que *queueHead){ //广度优先搜索
struct que* queueIt = queueHead->next;
if (queueIt->node == NULL) //如果当前队列已经没有树了,结束递归
{
return;
}
int count = 0; //计数器
returnNum[*returnSize] = (int*)malloc(sizeof(int) * 2000); //为返回的数组申请内存
while(1)
{
struct TreeNode * node = deQueue(queueHead); //把队列中的当前的结点的树值给树
if(node == NULL) //如果树空
{
break; //直接结束循环
}
returnNum[*returnSize][count] = node->val; //返回值就为树的值
count++; //计数器加一
if(node->left != NULL) //如果左孩不为空
{
que_enter(queueHead,node->left); //将左树加入队列继续递归
}
if(node->right != NULL) //右孩类似
{
que_enter(queueHead,node->right);
}
}
que_enter(queueHead,NULL); //如果上面出现break了说明已经只有null了
(*returnColumnSizes)[*returnSize ] = count; //列数就等于计数器(每行的结点数)
*returnSize = *returnSize + 1;
bfs(returnSize,returnColumnSizes,returnNum,queueHead); //递归出口在最开头
}
int** levelOrder(struct TreeNode* root, int* returnSize, int** returnColumnSizes){
*returnSize = 0;
if(root == NULL)
return NULL;
int** returnNum = (int**)malloc(sizeof(int*) * 2000);
*returnColumnSizes = (int*)malloc(sizeof(int) * 2000);
struct que *queueHead = malloc(sizeof(struct que));
queueHead->next = NULL;
queueHead->node = NULL;
que_enter(queueHead,root);
que_enter(queueHead,NULL);
bfs(returnSize,returnColumnSizes,returnNum,queueHead);
que_free(queueHead);
return returnNum;
}
3.力扣1609
原题链接
题目概述
如果一棵二叉树满足下述几个条件,则可以称为 奇偶树 :
二叉树根节点所在层下标为 0 ,根的子节点所在层下标为 1 ,根的孙节点所在层下标为 2 ,依此类推。
偶数下标 层上的所有节点的值都是 奇 整数,从左到右按顺序 严格递增
奇数下标 层上的所有节点的值都是 偶 整数,从左到右按顺序 严格递减
给你二叉树的根节点,如果二叉树为 奇偶树 ,则返回 true ,否则返回 false 。
解题思路
将上一题的代码复用,然后判断一下数组的情况
源码剖析
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* };
**/
struct que{
struct TreeNode* node; //结构体分为结点域
struct que* next; //指针域
};
void que_enter(struct que* a,struct TreeNode *node){ //尾插法插入结点
struct que* tmp = a; //首先定义一个队列结点
while(tmp->next!=NULL){ //如果当前结点的下一个结点不为空时
tmp = tmp->next; //使得tmp变为指向的下一个结点
} //这一步的作用就是,使得结点指向队列的尾部之后
struct que *b=(struct que*)malloc(sizeof(struct que)); //申请一个新的队列结点b
b->node = node; //将树结点填入队列结点的结点域
b->next = NULL; //使得结点指向NULL
tmp->next = b; //然后再使得tmp指向的结点变为b
}
struct TreeNode* deQueue(struct que *queueHead) //这个函数返回一个树结点,结点就是当前的队列结点中的树结点
{
struct que* queueIt = queueHead->next; //定义一个队列结点指向头结点的下一个
struct TreeNode* node; //定义一个树结点
if(queueIt != NULL) //假如指向不是队尾
{
queueHead->next = queueIt->next; //头结点的下一个结点变为下下个结点
node = queueIt->node; //把当前结点的值给树结点
free(queueIt); //释放结点
return node; //返回树结点
}
return NULL; //如果是队尾 node中肯定没有值,直接返回NULL
}
void que_free(struct que *a){ //释放队列
struct que *tmp = a->next; //定义一个结点代表当前结点的下一个结点
struct que *pre = a->next; //定义一个结点代表前一个结点
if(tmp!=NULL){ //当前结点next结点不为NULL时(如果是NULL就只剩头结点了)
pre = tmp; //pre就等于tmp
tmp = tmp->next; //tmp变为下一个结点
free(pre); //随后释放pre,这时后续的结点(包括tmp)就都被断开了
}
free(a); //再释放头结点
}
void bfs(int *returnSize,int **returnColumnSizes,int **returnNum,struct que *queueHead){ //广度优先搜索
struct que* queueIt = queueHead->next;
if (queueIt->node == NULL) //如果当前队列已经没有树了,结束递归
{
return;
}
int count = 0; //计数器
returnNum[*returnSize] = (int*)malloc(sizeof(int) * 2000); //为返回的数组申请内存
while(1)
{
struct TreeNode * node = deQueue(queueHead); //把队列中的当前的结点的树值给树
if(node == NULL) //如果树空
{
break; //直接结束循环
}
returnNum[*returnSize][count] = node->val; //返回值就为树的值
count++; //计数器加一
if(node->left != NULL) //如果左孩不为空
{
que_enter(queueHead,node->left); //将左树加入队列继续递归
}
if(node->right != NULL) //右孩类似
{
que_enter(queueHead,node->right);
}
}
que_enter(queueHead,NULL); //如果上面出现break了说明已经只有null了
(*returnColumnSizes)[*returnSize ] = count; //列数就等于计数器(每行的结点数)
*returnSize = *returnSize + 1;
bfs(returnSize,returnColumnSizes,returnNum,queueHead); //递归出口在最开头
}
int** levelOrder(struct TreeNode* root,int* returnSize, int** returnColumnSizes){
*returnSize = 0;
if(root == NULL)
return NULL;
int** returnNum = (int**)malloc(sizeof(int*) * 2000);
*returnColumnSizes = (int*)malloc(sizeof(int) * 2000);
struct que *queueHead = malloc(sizeof(struct que));
queueHead->next = NULL;
queueHead->node = NULL;
que_enter(queueHead,root);
que_enter(queueHead,NULL);
bfs(returnSize,returnColumnSizes,returnNum,queueHead);
que_free(queueHead);
return returnNum;
}
bool isEvenOddTree(struct TreeNode* root){
int* a;
int** returnColumnSizes;
int** mat = levelOrder(root,a,returnColumnSizes);
int i,j;
for(i = 0;i<a;++i){
if(i&1){ //偶数行递增
int min = 0;
for(j = 0;j<returnColumnSizes[i];++j){
if(mat[i][j]==NULL) continue;
else{
if(min<mat[i][j]) min = mat[i][j];
else return false;
}
}
}else{ //奇数行递减
int max = 10001;
for(j = 0;j<returnColumnSizes[i];++j){
if(mat[i][j]==NULL) continue;
else{
if(max>mat[i][j]) max = mat[i][j];
else return false;
}
}
}
}
return true;
}
代码有一个小问题,就是对于数组的引用,具体的修改要等更加熟悉这里的内容之后。
4.力扣1263
原题链接
题目概述
「推箱子」是一款风靡全球的益智小游戏,玩家需要将箱子推到仓库中的目标位置。
游戏地图用大小为 m x n 的网格 grid 表示,其中每个元素可以是墙、地板或者是箱子。
现在你将作为玩家参与游戏,按规则将箱子 ‘B’ 移动到目标位置 ‘T’ >:
玩家用字符 ‘S’ 表示,只要他在地板上,就可以在网格中向上、下、左、右四个方向移动。
地板用字符 ‘.’ 表示,意味着可以自由行走。
墙用字符 ‘#’ 表示,意味着障碍物,不能通行。
箱子仅有一个,用字符 ‘B’ 表示。相应地,网格上有一个目标位置 ‘T’。
玩家需要站在箱子旁边,然后沿着箱子的方向进行移动,此时箱子会被移动到相邻的地板单元格。记作一次「推动」。
玩家无法越过箱子。
返回将箱子推到目标位置的最小 推动 次数,如果无法做到,请返回 -1。
暂做记录,之后会去完整的实现一下这个游戏。