4.5 双向链表和循环链表
...大约 5 分钟
双向链表中每个节点包含指向下一节点和上一节点的指针, 可以从两个方向遍历.
循环链表的尾节点和头节点相连形成环. 因为可以从任意节点出发到达任意节点, 所以循环链表的任意节点都可以是头节点.
4.5.1 问题28: 展平多级双向链表
多级双向链表中,除了指向下一个节点和前一个节点指针之外,它还有一个子链表指针,可能指向单独的双向链表。这些子列表也可能会有一个或多个自己的子项,依此类推,生成多级数据结构,如下面的示例所示。
给定位于列表第一级的头节点,请扁平化列表,即将这样的多级双向链表展平成普通的双向链表,使所有结点出现在单级双链表中。
示例 1:
输入:head = [1,2,3,4,5,6,null,null,null,7,8,9,10,null,null,11,12] 输出:[1,2,3,7,8,11,12,9,10,4,5,6] 解释: 输入的多级列表如下图所示: 扁平化后的链表如下图:
示例 2:
输入:head = [1,2,null,3] 输出:[1,3,2] 解释: 输入的多级列表如下图所示: 1---2---NULL | 3---NULL
示例 3:
输入:head = [] 输出:[]
如何表示测试用例中的多级链表?
以 示例 1 为例:
1---2---3---4---5---6--NULL | 7---8---9---10--NULL | 11--12--NULL
序列化其中的每一级之后:
[1,2,3,4,5,6,null] [7,8,9,10,null] [11,12,null]
为了将每一级都序列化到一起,我们需要每一级中添加值为 null 的元素,以表示没有节点连接到上一级的上级节点。
[1,2,3,4,5,6,null] [null,null,7,8,9,10,null] [null,11,12,null]
合并所有序列化结果,并去除末尾的 null 。
[1,2,3,4,5,6,null,null,null,7,8,9,10,null,null,11,12]
提示:
- 节点数目不超过
1000
1 <= Node.val <= 10^5
4.5.1.1 分析
首先理清展开的逻辑:
- 将子链表展开, 形成不含子链表的链表
- 将已展开的子链表插入回原链表中
由于子链表也可包含子链表, 所以操作是递归的.
4.5.1.2 题解
时间复杂度: , 遍历每个节点
空间复杂度: , k 为子链表的层数
type Node struct {
Val int
Next *Node
Prev *Node
Child *Node
}
func flatten(root *Node) *Node {
flattenGetTail(root)
return root
}
func flattenGetTail(head *Node) *Node {
node := head
var tail *Node // 展开后的尾节点
// 遍历链表
for node != nil {
next := node.Next
// 展开子链表
if node.Child != nil {
child := node.Child // 子链表头节点
childTail := flattenGetTail(child) // 递归展开子链表, 返回展开后的尾节点
// 插入原链表
node.Child = nil // 断开子链表
node.Next = child
child.Prev = node
childTail.Next = next
if next != nil { // 注意双向链表的指针有两个
next.Prev = childTail
}
// 更新当前展开的尾节点
tail = childTail
} else { // 递归出口, 无子链表
tail = node
}
// 更新当前节点
node = next
}
return tail
}
4.5.2 问题29: 排序的循环链表
给定循环单调非递减列表中的一个点,写一个函数向这个列表中插入一个新元素
insertVal
,使这个列表仍然是循环升序的。给定的可以是这个列表中任意一个顶点的指针,并不一定是这个列表中最小元素的指针。
如果有多个满足条件的插入位置,可以选择任意一个位置插入新的值,插入后整个列表仍然保持有序。
如果列表为空(给定的节点是
null
),需要创建一个循环有序列表并返回这个节点。否则。请返回原先给定的节点。示例 1:
输入:head = [3,4,1], insertVal = 2 输出:[3,4,1,2] 解释:在上图中,有一个包含三个元素的循环有序列表,你获得值为 3 的节点的指针,我们需要向表中插入元素 2 。新插入的节点应该在 1 和 3 之间,插入之后,整个列表如上图所示,最后返回节点 3 。
示例 2:
输入:head = [], insertVal = 1 输出:[1] 解释:列表为空(给定的节点是 null),创建一个循环有序列表并返回这个节点。
示例 3:
输入:head = [1], insertVal = 0 输出:[1,0]
提示:
0 <= Number of Nodes <= 5 * 10^4
-10^6 <= Node.val <= 10^6
-10^6 <= insertVal <= 10^6
4.5.2.1 分析
找出规则
要插入一个节点 , 那么这个节点值一定满足: . 那么问题就转化成在链表中寻找满足这种条件的节点.
边界条件
- 空链表: 此时插入的节点就是头节点
- 仅有一个节点: 将插入节点与其链接即可
- 最大(小)值: 若插入的节点大于(小于)最大(小)值, 则插入到头节点和尾节点之间.
4.5.2.2 题解
type Node29 struct {
Val int
Next *Node29
}
func insert(aNode *Node29, x int) *Node29 {
newNode := &Node29{Val: x}
// 边界条件
if aNode == nil { // 空链表
newNode.Next = newNode
return newNode
} else if aNode.Next == nil { // 只有一个节点
aNode.Next = newNode
newNode.Next = aNode
} else {
insertCore(aNode, newNode)
}
return aNode
}
func insertCore(head, node *Node29) {
cur := head
next := cur.Next
biggest := cur
// 寻找插入位置
for !(cur.Val <= node.Val && next.Val >= node.Val) && next != head {
cur = next
next = cur.Next
if cur.Val >= biggest.Val {
biggest = cur
}
}
// 插入
if cur.Val <= node.Val && next.Val >= node.Val { // 插入位置在中间
cur.Next = node
node.Next = next
} else { // 插入位置为末尾
node.Next = biggest.Next
biggest.Next = node
}
}
Reference
Powered by Waline v2.15.2