430. 扁平化多级双向链表
思路:多级双向链表中,除了指向下一个节点和前一个节点指针之外,它还有一个子链表指针,可能指向单独的双向链表。这些子列表也可能会有一个或多个自己的子项,依此类推,生成多级数据结构,如下面的示例所示。
给你位于列表第一级的头节点,请你扁平化列表,使所有结点出现在单级双链表中。
我们观察这个链表,把链表顺时针旋转90度,多级链表变成了二叉树,输出先序即可。
Stack<Node> stack = new Stack<>();
List<Node> res = new ArrayList<>();
stack.add(head);
while(!stack.isEmpty()){
Node node = stack.pop();
if(node == null) continue;
res.add(node);
stack.add(node.next);
stack.add(node.child);
}
然后再将这个节点串起来,注意将child置空:
for(int i=0;i<res.size()-1;i++){
Node node = res.get(i);
Node node2 = res.get(i+1);
node.next = node2;
node2.prev = node;
node.child = null;
}
Node node3 = res.get(res.size()-1);
node3.next = null;
node3.child = null;
return head;
完整代码如下:
public Node flatten(Node head){//前序遍历+串联
if(head == null) return head;
Stack<Node> stack = new Stack<>();
List<Node> res = new ArrayList<>();
stack.add(head);
while(!stack.isEmpty()){
Node node = stack.pop();
if(node == null) continue;
res.add(node);
stack.add(node.next);
stack.add(node.child);
}
for(int i=0;i<res.size()-1;i++){
Node node = res.get(i);
Node node2 = res.get(i+1);
node.next = node2;
node2.prev = node;
node.child = null;
}
Node node3 = res.get(res.size()-1);
node3.next = null;
node3.child = null;
return head;
}
但是如果一开始很难想到将其转换成树的这个思路呢?
那我们也可以采用最容易理解的方法,按照题目的描述,我们先顺着链表遍历,直到遇到child不为空的节点,将这个节点的下一个节点next存起来,然后遍历其child节点,得到整个child链表后,我们将next节点串在child链表最后一个节点后面即可,同样的,扁平化后所有child节点置空,我们采用递归来完成这个操作:
public Node flatten(Node head) {//递归
if (head == null) return null;
Node cur = head;
// 向后遍历
while (cur != null) {
// 存在子链表,进行递归
if (cur.child != null) {
// 保留 next 节点
Node next = cur.next;
// 得到扁平化后的子链表,与之相连
Node child = flatten(cur.child);
cur.next = child;
child.prev = cur;
cur.child = null;
// 连接原 next
if (next != null) {//child中间是已经被打平的链表,这里可以放心的不断next,不用担心child的出现
while (cur.next != null) {
cur = cur.next;
}
cur.next = next;
next.prev = cur;
}
}
cur = cur.next;
}
return head;
}
但是递归可能不太容易理解Node child = flatten(cur.child);这部分,我们也可以采用迭代,如果采用迭代就需要借用stack来保存next节点了,细节部分看注释:
public Node flatten(Node head) {//迭代
if (head == null) return null;
LinkedList<Node> stack = new LinkedList<>();
Node cur = head;
while (true) {
if (cur.child != null) {
// 将 next 节点入栈
if (cur.next != null) {
stack.push(cur.next);
}
// 将子链表扁平化
cur.next = cur.child;
cur.child.prev = cur;
cur.child = null;
}
// 遍历子链表的下一个节点或是从栈中弹出 next 节点
if (cur.next != null) {//结束本次循环,下次循环再次判断cur.child != null。。。
cur = cur.next;
} else if (!stack.isEmpty()) {//没有child了,也就是child链表遍历完了,直接接上stack中存的next即可
Node next = stack.pop();
cur.next = next;//这里的cur本身就没有child的,所以不用 cur.child = null;
next.prev = cur;
cur = next;
} else {
return head;//所有节点遍历结束
}
}
}