10.2-1
因为插入操作仅仅需要在链表\(L\) 的头节点插入就好,将当前节点作为新的头节点,链接下一个原来的头节点。这种操作仅需要\(O(1)\) 的时间。但是删除一个节点\(x\) 时,由于\(L\) 是单向链表,因此需要遍历 整个\(L\) 来查找\(x\) 的前驱节点\(y\) ,并且将\(y\) 链接到原来\(x\) 的下一个节点。因此最坏的时间开销为\(\Theta(n)\) ,即\(L\) 的长度。
10.2-2
1 2 3 4 5 6 7 8 9 10 11 12 13 STACK-EMPTY-LIST(L) return L.head == NIL PUSH(L, x) x.next = L.head L.head = x POP(L) if STACK-EMPTY-LIST(L) error "underflow" x = L.head L.head = L.head.next return x
10.2-3
需要给链表\(L\) 需要添加一个属性tail
表示链表的末节点指针。
值得注意的是,这里用tail
指针表示队头,用head
指针表示队尾,链表和队列中指针的头和尾含义完全是“相反”的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 QUEUE-EMPTY-LIST(L) return L.head == NIL ENQUEUE(L, x) if QUEUE-EMPTY-LIST(L) L.head = x else L.tail.next = x x.next = NIL L.tail = x DEQUEUE(L, x) if QUEUE-EMPTY-LIST(L) error "underflow" x = L.head L.head = L.head.next if L.head == NIL L.tail = NIL return x
10.2-4
考虑集合S1, S2
分别使用带哨兵的双向链表L1, L2
来存储。那么合并两个链表的操作非常简单,只需要将L1
的元素放在新链表L
的前面,将L2
的元素放在新链表L
的后面,并且顺序也不会被破坏。
需要注意的是,合并后,需要将L2
的哨兵去掉。
1 2 3 4 5 6 7 8 UNION(L1, L2) //去掉L2的哨兵 L1.nil.prev.next = L2.nil.next L2.nil.next.prev = L1.nil.prev //将L1的哨兵和L2的末节点关联 L1.nil.prev = L2.nil.prev L2.nil.prev.next = L1.nil return L1
完成这\(4\) 个链表操作后,得到的新链表就是集合S1, S2
的并,并且仍然是一个带哨兵的双向链表。
10.2-5
1 2 3 4 5 6 7 8 9 10 11 REVERSE-LIST(L) // a, b, c是链表上3个相邻的节点。 a = NIL b = L.head while b != NIL c = b.next b.next = a a = b b = c L'.head = a return L'
\(\star\)
10.2-6
基本思路:如果\(A\text{ XOR
}B=C\) ,那么\(A\text{ XOR
}C=B\) 。也就是说,对于指针np
,如果知道了目前的其中一个值,那么可以求解出另一个值。
考虑双向链表\(L\) 有头哨兵指针nilhead
,尾哨兵niltail
。
和书上介绍的双向链表有所不同,这个双向链表的头哨兵前驱指针prev
被认为指向NIL
,尾哨兵后继指针next
被认为指向NIL
。
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 37 SEARCH-NP(L, k) prev = L.nilhead p = L.nilhead.np while p != niltail and p.key != k // prev是p前驱节点的指针,next是p的后继节点指针。 next = p.np XOR prev prev = p p = next if p == L.niltail return NIL else return p INSERT-NP(L, x) q = NIL XOR L.nilhead.np // 第一个XOR还原出了q的next地址。 q.np = (q.np XOR L.nilhead) XOR x x.np = L.nilhead XOR q L.nilhead.np = NIL XOR x DELETE-NP(L, x) prev = L.nilhead p = L.nilhead.np while p != niltail // prev是p前驱节点的指针,next是p的后继节点指针。 next = p.np XOR prev if p == x prev.np = (prev.np XOR p) XOR next next.np = (next.np XOR p) XOR prev // 注意,p节点被删除了,那么prev仍然是prev,不需要改变。 p = next else prev = p p = next REVERSE-NP(L) swap L.nilhead with L.niltail