给你二叉树的根节点 root
,返回其节点值的 层序遍历 。 (即逐层地,从左到右访问所有节点)。
示例 1:
输入:root = [3,9,20,null,null,15,7] 输出:[[3],[9,20],[15,7]]
示例 2:
输入:root = [1] 输出:[[1]]
示例 3:
输入:root = [] 输出:[]
提示:
[0, 2000]
内-1000 <= Node.val <= 1000
关键词:层次遍历
任务1: 对二叉树进行层序遍历
任务2:以层为单位,返回二维数组
要实现层次遍历,我们需要借助“队列”这一数据结构。因为队列有着“先进先出”的原则。利用该原则,我们将已经遍历的节点存入队列中,以实现顺序不变。
如上图所示,层序遍历有以下步骤。
1、根节点root入列
2、根节点出列,如果根节点有左子树的话,左子树的根节点入列,如果没有就跳过;如果根节点有右子树的话,右子树的根节点入列,如果没有就跳过。
3、当队列不为空时,将队首节点出队,并将队首节点的左右孩子入列,如果没有则跳过
4、重复步骤3,直到队列为空。
代码如下:
queue dui;//定义一个队列
dui.push(root);//把根节点存入
while(!dui.empty())
{
TreeNode p;
p = dui.front();//p为队列首个元素
dui.pop();//弹出p
if(p->left!=nullptr) //如果p的左子树不为空
{dui.push(p->left);} //左孩子入列
if(p->right!=nullptr) //如果p的右子树不为空
{dui.push(p->right);} //右孩子入列
//任务2处理
}
要把每层节点作为一组,输出所有层的节点。
要实现每层为单位输出元素,可以维护一个二元组dui。在任务1处理时,遍历到i节点,将该节点和层数同时存入result中。
因为每个结点的层数必定是父节点的层数加1。所以,在父节点i出队列时,将其左右孩子同时入队列,同时,记录下他们的层数。
在二元组队列dui中,遍历到某个元素f时,有以下步骤。
1、找到该元素f的第一个元素(p节点)和p节点的层次deep
2、如果在最终数组result中,没有p节点所在层数的节点的话,在result中新加一个数组{},并把该节点的值放入数组{}中。如果已经有了该层的某个节点,只需在数组{}中,继续添加即可
3、如果该节点p有左孩子的话,把p的左孩子和左孩子的层次deep+1放入dui中;如果没有,则跳过。同理,处理右孩子。
4、重复步骤2和3,直到队列为空。最终输出result。
代码如下:
class Solution {
public:
vector> levelOrder(TreeNode* root) {
vector> result;
if(!root) return result;//如果根节点为空 就返回根节点
queue> dui;//定义一个队列
dui.push({root,0});//把根节点和其层数存入
while(!dui.empty())
{
pair f;
f = dui.front();//f为队列首个元素
dui.pop();//弹出f
auto p=f.first;//p为弹出的节点
int deep = f.second;//deep为弹出节点的层数
if(p->left!=nullptr) //如果左孩子不为空
{dui.push({p->left,deep+1});}//将左孩子的层数加入队列中
if(p->right!=nullptr) //如果右孩子不为空
{dui.push({p->right,deep+1});}//将右孩子的层数加入队列中
if(result.size()val);//将p的值加入到该层数组中
}
return result;
}
};
一个机器人位于一个 m x n
网格的左上角 (起始点在下图中标记为 “Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish”)。
现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?
网格中的障碍物和空位置分别用 1
和 0
来表示。
示例 1:
输入:obstacleGrid = [[0,0,0],[0,1,0],[0,0,0]]
输出:2
解释:3x3 网格的正中间有一个障碍物。
从左上角到右下角一共有 2
条不同的路径:
1. 向右 -> 向右 -> 向下 -> 向下
2. 向下 -> 向下 -> 向右 -> 向右
示例 2:
输入:obstacleGrid = [[0,1],[0,0]] 输出:1
提示:
m == obstacleGrid.length
n == obstacleGrid[i].length
1 <= m, n <= 100
obstacleGrid[i][j]
为 0
或 1
关键词1:路径总数
关键词2:只能向左或者向下
关键词3:障碍物
任务1:从起点到终点的所有路径
任务2:排除所有非向右或者向下的路径
任务3:排除路径上有障碍物的路径
维护一个二维数组dp,dp[i][j]表示从起点到(i,j)的所有路径总数。
采用动态规划的方法。对于如下图所示的点(i,j)。去往(i,j)的所有路径只有从上下左右四个方向出来的,即(i-1,j)、(i+1,j)、(i,j+1)和(i,j-1)四点。那么从起点到该点的路径数肯定是四个方向的路径总数。即,dp[i][j]=dp[i-1][j]+dp[i+1][j]+dp[i][j-1]+dp[i][j+1]。所以,下图所示的dp[i][j]的值为1+3+2+1=7。表示,从起点到(i,j)总共可以有7条路径。
当然,动态规划必不可少的是需要考虑特殊情况和初始情况,即,边界问题。
在横坐标上,由于纵坐标j为0,所以dp[i][j-1]的情况必定会造成数组越界。同样,纵坐标上也会面临相同问题。所以我们需要特殊考虑横纵坐标上的值。
以横坐标为例,由于任务2的要求,横坐标上的路径只能有1条,那就是从起点直接横着出发所以,dp[i][0]必定为1。纵坐标同样。
对于起点来说,从起点到起点肯定只有1条路,那就是原地不动。所以dp[0][0]可以初始化为1。
代码如下所示:
for(int i=0;i
排除向左和向上的路径
其实就是把任务1里的4个方向去掉两个,分别是右边的和上边的方向,即dp[i+1][j]和dp[i][j+1]。
代码修改如下:
dp[i][j]=dp[i-1][j]+dp[i][j-1];
//去掉了两个方向的路径数
排除有障碍物的路径。
如果遇到障碍物,说明该条路不可走。不管该障碍物之前有多少多的路径可以走,一旦遇到障碍物,统统消失。即,从起点到该点的路径数为0。所以只需加一个判断条件即可。
if(obstacleGrid[i][j]==1) //该点上有障碍物
{dp[i][j]=0;}//该路径不可达,路径数为0
else
{dp[i][j]=dp[i-1][j]+dp[i][j-1];}
//否则就按照正常路径计算
同样,我们需要考虑边界问题。当横纵坐标上有障碍物时,因为必须满足任务2,只能向右走和向下的条件,该障碍物之前的点的路径数均为1,该点之后的路径数均为0。纵坐标同理。
for(int i = 0;i
当然,如果起点上有障碍物,则起点到任何位置均不可达。总路径数均为0。
if(obstacleGrid[0][0]==1) //起点上有障碍物
{return 0;}//直接返回0路径数
完整的代码如下所示:
class Solution {
public:
int uniquePathsWithObstacles(vector>& obstacleGrid) {
int m=obstacleGrid.size();//有m行
int n=obstacleGrid[0].size(); //有n列
vector> dp(m,vector(n));
//dp[i][j]表示从起点到(i,j)有多少路径
if(obstacleGrid[0][0]==1) {return 0;}
//如果障碍物在起点,那么从起点到任何点的路径数均为0
else{dp[0][0]=1;}//否则起点到起点的路径数为1
for(int i = 0;i
给你一个链表的头节点 head ,旋转链表,将链表每个节点向右移动 k 个位置。
示例 1:
输入:head = [1,2,3,4,5], k = 2
输出:[4,5,1,2,3]
示例 2:
输入:head = [0,1,2], k = 4
输出:[2,0,1]
提示:
链表中节点的数目在范围 [0, 500] 内
-100 <= Node.val <= 100
0 <= k <= 2 * 109
思路:官方的做法是将链表连成一个环,遍历k个节点,最终停靠的位置上断开,就形成了新的链表。
遍历两次链表,找到重要的三个结点,分别是尾指针jishu、断开的节点first和断开节点的前一个节点pre。其中,first用于作为新链表的头结点,pre作为新链表的尾结点,jish用于首尾连接。
同时,找到特例if (head == nullptr || head->next == nullptr || k == 0)以及if(n-k==n) 该情况无法找到以上三种节点,所以都需要特殊处理,即,返回原链表。
class Solution {
public:
ListNode* rotateRight(ListNode* head, int k) {
if (head == nullptr || head->next == nullptr || k == 0) return head;
int n=1;//记录链表节点个数
ListNode *first = head;
ListNode *jishu = head;//用于记录节点数,顺便找到尾结点
ListNode *pre = head;
while(jishu->next!=nullptr) { jishu=jishu->next;n++;}
k=k%n;//k为实际右移次数
first=head;
pre=head;
if(n-k==n) return head;
for(int i=0;inext;first=first->next;}//找到需要改变的点和他的前一个节点
first=first->next;
ListNode *temphead=new ListNode(0);//创建一个临时头结点,并且他的下一节点为转折点
temphead->next=first;
pre->next=nullptr;
jishu->next=head;
return temphead->next;
}
};