117 填充每个节点的下一个右侧节点指针 II:给定一个包含指针 next
成员的二叉树:填充它的每个 next
指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next
指针设置为 NULL
。初始状态下,所有 next
指针都被设置为 NULL
。
树结构体:
1 2 3 4 5 6
| struct Node { int val; Node *left; Node *right; Node *next; }
|
示例图:

队列 + 层序遍历
根据题目描述,「填充它的每个 next
指针,让这个指针指向(同层的)其下一个右侧节点」,我们很容易想到使用「层序遍历」解决这个问题。而层序遍历通常需要借助 FIFO 队列实现。
关于借助队列的树的层序遍历:首先要入队根节点;然后,记录队列中的节点个数 size
;随后,弹出 size
个节点,在弹出的过程中入队弹出节点的左、右节点;最后,在这 size
个节点弹出后,队列中剩下的节点,就是刚刚入队的树的下一层节点。只要队列不为空,我们就反复进行上述操作,直到队列为空为止(二叉树的叶子层没有左、右节点,便不会再有节点入队,队列也就为空了)。
时间复杂度:O(n)
,一趟遍历,空间复杂度:O(n)
,需要使用队列保存二叉树中的节点。
关于队列,我们可以使用 数据结构之队列(链表实现)中实现的接口。但是,为了简单、清晰,我在这里直接使用数组模拟 FIFO 队列。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| typedef struct Node TreeNode_t; struct Node* connect(struct Node* root) { if (root == NULL) { return NULL; } TreeNode_t *queue[6000]; int front = 0, rear = 0; queue[rear++] = root;
while (rear - front > 0) { int size = rear - front; TreeNode_t *curNode = NULL, *preNode = NULL;
for (int i = 0; i < size; ++i) { curNode = queue[front++]; if (preNode) { preNode->next = curNode; } preNode = curNode; if (curNode->left) { queue[rear++] = curNode->left; } if (curNode->right) { queue[rear++] = curNode->right; } } curNode->next = NULL; } return root; }
|
利用已建立的链表构建下一层链表
根据题目描述,「填充它的每个 next
指针,让这个指针指向(同层的)其下一个右侧节点」。这样填充后,二叉树的每一层都将是一个单链表,链表的头节点是每层最左侧的树节点。这样,我们就可以根据链表中的节点,获得它的下一层的左、右孩子节点。
因此,我们可以利用当前层的链表 currLayer
来构建下一层的链表 nextLayer
:
- 下一层的构建过程就是:不断的访问
currLayer
中的节点,通过它访问下一层的左、右孩子节点,将这些孩子链接起来,就是 nextLayer
;
- 构建的下一层的链表时,要记录下一层的头结点,以便更新当前层为下一层。
- 下一层的头结点,就是
currLayer
中的节点里,第一个有孩子节点(若有左、右孩子,左孩子优先)。
最后,当下一层为叶子结点层时,便不会有下一层链表,当前层链表会更新为 NULL
,标志着每层的链表构建完成。
时间复杂度:O(n)
,一趟遍历,空间复杂度:O(1)
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| struct Node* connect(struct Node* root) { struct Node *currLayer = root;
while (currLayer) { struct Node *node = currLayer; struct Node *nextLayer = NULL; bool isFirst = true;
currLayer = NULL;
while (node) { struct Node *data[2] = {node->left, node->right}; for (int i = 0; i < 2; ++i) { if (data[i]) { if (isFirst) { nextLayer = data[i]; currLayer = nextLayer; isFirst = false; } else { nextLayer->next = data[i]; nextLayer = nextLayer->next; } } } node = node->next; } } return root; }
|
评论区用户 @M9988:方法二确实比方法一巧妙。但是,作为面试准备的话,我更推崇练习方法一。因为方法一才是层序(BFS)遍历的通用模式。把这个 BFS 模板练熟以后,面试方可信手拈来(再在里面做附加逻辑)。方法二,技巧性强,可以提高,但是面试的话还是把场景套上模板的熟练能力。