<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title></title> </head> <body> <script type="text/javascript"> let log = console.log.bind(console); function makeObj(value=0, n=null, p=null) { let obj = { key: value, next: n, prev: p, }; return obj; } function makeLinkedList(arr) { let list = {}; list.head = makeObj(arr[0]); let temp = list.head; for (let i = 1; i != arr.length; ++i) { temp.next = makeObj(arr[i]); temp.next.prev = temp; temp = temp.next; } return list; } function listSearch(list, key) { let temp = list.head; while (temp != null && temp.key != key) { temp = temp.next; } return temp; } function listInsert(list, obj) { // 注意是插入到链表的前面 obj.next = list.head; if (list.head != null) { list.head.prev = obj; } list.head = obj; return list; // 便于打印。。 } function listDelete(list, obj) { // 要拿到相应元素的引用,并且确保obj确实在list里 if (obj.prev != null) { obj.prev.next = obj.next; } else { list.head = obj.next; } if (obj.next != null) { obj.next.prev = obj.prev; } return list; // 便于打印。。 } let list = makeLinkedList([9, 16, 4, 1]); log(list); // 9, 16, 4, 1 log(listSearch(list, 4)); log(listInsert(list, makeObj(3))); // 3, 9, 16, 4, 1 log(listDelete(list, listSearch(list, 4))); // 3, 9, 16, 1 </script> <script type="text/javascript"> /** * 哨兵版本(双向循环列表)。 * 不能降低增长量级,但是可以降低常数因子。 * 假如有多个很短的链表,它们的哨兵所占用的 * 额外存储空间会造成严重的存储浪费。 * 注意:哨兵元素是不存储数据的!!! * 空的双向循环列表中仍有一个哨兵元素 */ function makeLinkedList2(arr) { let list = {}; list.nil = makeObj(null); let temp = list.nil; for (let i = 0; i != arr.length; ++i) { temp.next = makeObj(arr[i]); temp.next.prev = temp; temp = temp.next; } list.nil.prev = temp; temp.next = list.nil; return list; } function listDelete2(list, obj) { // 要拿到相应元素的引用,并且确保obj确实在list里 obj.prev.next = obj.next; obj.next.prev = obj.prev; return list; // 便于打印 } function listSearch2(list, key) { let temp = list.nil.next; // 注意此处的修改 while (temp != list.nil && temp.key != key) { temp = temp.next; } return temp; } function listInsert2(list, obj) { obj.next = list.nil.next; list.nil.next.prev = obj; list.nil.next = obj; obj.prev = list.nil; return list; // 便于打印 } log("=======list 2========="); let list2 = makeLinkedList2([1, 2, 3]); log(list2); // null, 1, 2, 3 log(listSearch2(list2, 3)); log(listInsert2(list2, makeObj(4))); // null, 4, 1, 2, 3 let x = listSearch2(list2, 4); log(listDelete2(list2, x)); // null, 1, 2, 3 </script> </body> </html>
10.2-1
这个得区分往哪insert,以及delete是指定delete还是delete首元素或者尾元素。
如果按照书上的算法:insert可以,delete不行。
10.2-2
略
10.2-3
略
10.2-4
该检查的目的就在于防止死循环,解决这个问题,只要把k作为哨兵关键字key的value就可以了。在搜索结束后再把空值赋还给哨兵。
10.2-5
实现略。
按照书上的算法:INSERT是O(1),DELETE是O(n),SEARCH也是O(n)
10.2-6
双向循环链表。直接把一个链表接到另外一个链表的首尾都可以。如果有中间元素的引用,直接往中间插也是O(1)的。
10.2-7
扫描一遍list,把next反向指一下就行了。需要一个额外的temp临时存储下一个元素。