数据结构 - 循环双向链表(JavaScript实现)
源码地址
双向循环链表(CircularDoublyLinkList) 双向循环链表 双向循环链表是在双向链表的基础上,将尾节点的 next 指向 头节点,形成了一个环。
初始化 1 2 3 4 5 6 7 8 9 10 11 12 13 class CircularDoublyLinkList { constructor (...arg ){ this .head = null this .rear = null } node (data ){ return { data, next : null , prev : null } } }
基本操作 插入 头部插入
如果头节点为空,head 和 rear 都指向新节点node
头节点不为空,取出头节点 temp = head
node.next 指向 temp
设置头指针指向新节点node head = node,
设置尾部节点的 next 指向新节点 rear.next = node
node.prev 指向 rear
1 2 3 4 1 ->2 ->3 ->1 0 ->1 ->2 ->3 0 ->1 ->2 ->3 ->0
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 prepend (data ){ let node = this .node (data) if (!this .head ){ this .head = node this .rear = node node.next = this .head node.prev = this .rear return node.data } let temp = this .head node.next = temp temp.prev = node this .head = node node.prev = this .rear this .rear .next = node return node.data }
尾部插入
取出头尾节点 head,rear,temp = rear
新节点 node.next 指向 head
temp.next 指向新节点 node
rear 指针指向新节点 node
head.prev 指向 head
1 2 3 4 1 ->2 ->3 ->1 1 ->2 ->3 ->4 1 ->2 ->3 ->4 ->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 append (data ){ let node = this .node (data) if (!this .rear ){ this .head = node this .rear = node node.next = this .head node.prev = this .rear return node.data } let temp = this .rear temp.next = node node.prev = temp this .rear = node node.next = this .head this .head .prev = node return node.data }
指定位置插入
找到插入的位置 curr,创建新节点 node,temp = curr.next
把 curr.next 指向 node
把 node.next 指向 temp
把 node.prev 指向 curr
把 temp.prev 指向 node
1 2 3 4 1 ->2 ->4 ->1 取出 2 创建 3 1 ->2 ->3 1 ->2 ->3 ->4 ->1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 add (el, data ){ let curr = this .contains (el) if (!curr) throw null if (curr == this .rear ) return this .append (data) let node = this .node (data) let temp = curr.next curr.next = node node.prev = curr node.next = temp return node.data }
删除 头部删除
取出头节点 temp = head,如果节点不存在,返回 null。
把 head 的指针 指向原 head.next
把 rear.next 指向新的 head
把 head.prev 指向 rear
1 2 3 1 ->2 ->3 ->1 2 ->3 ->1 2 ->3 ->2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 removeHead ( ){ if (!this .head ) return null let temp = this .head if (!temp) return null if (temp == this .rear ) { this .rear = null this .head = null return temp.data } temp.next .prev = null this .head = temp.next this .rear .next = this .head this .head .prev = this .rear temp.next = null return temp.data }
尾部删除
取出头节点 head 尾节点 rear ,找出 rear 的前置节点 curr
将 curr.next 指向 rear.next
把尾指针指向 curr
把 head.prev 指向 curr
1 2 3 1 ->2 ->3 ->1 取出 2 1 ->2 ->1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 removeRear ( ){ if (!this .rear ) return null let curr = this .rear .prev let rear = this .rear if (rear == this .head ) { this .rear = null this .head = null return rear.data } rear.prev = null if (curr) curr.next = null this .rear = curr this .rear .next = this .head this .head .prev = this .rear return rear.data }
指定位置删除
判断删除位置是否头节点,还是尾节点,还是中间节点
取出 前置节点 curr,通过 curr.next 取出目标节点 node
curr.next 指向 node.next
node.next.prev 指向 curr
1 2 3 1 ->2 ->3 -> 取出 1 2 1 ->3 ->1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 remove (el ){ if (!this .head ) return null if (this .head .data === el) return this .removeHead () let curr = this .contains (el) if (!curr) return null if (curr == this .rear ) return this .removeRear () let nextNode = curr.next let prevNode = curr.prev prevNode.next = nextNode nextNode.prev = prevNode return curr.data }
查找
循环查找,直到 curr 再次等于 head 的时候,跳出循环
1 2 3 4 5 6 7 8 9 10 11 12 13 contains (el ){ if (!this .head ) return null let curr = this .head do { if (curr.data === el) break curr = curr.next ; } while (curr != this .head ); if (curr.data != el) return null return curr }
遍历 正序遍历
从头节点开始循环,直到 curr 再次等于 head 的时候,跳出循环
1 2 3 4 5 6 7 8 9 10 11 12 13 14 traverse (callback=(item)=>item ){ let res = [] if (!this .head ) return res let curr = this .head do { res.push (callback (curr.data )) curr = curr.next ; } while (curr != this .head ); return res }
反序遍历
取出尾节点,通过前置指针不停往前,直到 curr 再次等于 rear
1 2 3 4 5 6 7 8 9 10 11 12 13 reverseTraversal (callback=(item)=>item ){ let res = [] if (!this .head ) return res let curr = this .rear do { res.push (callback (curr.data )); curr = curr.prev ; } while (curr != this .rear ); return res }
反转
步骤跟单链表差不多,但是需要在后面处理尾指针的指向
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 38 39 40 41 42 43 44 45 46 47 48 49 50 reverse ( ) { if (!this .head ) return null let curr = this .head let next = null let prev = null while (curr){ next = curr.next curr.next = prev if (prev) prev.prev = curr prev = curr curr = next } let rear = this .rear this .rear = this .head this .head = rear return this }
取出中间节点 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 findMid ( ){ if (!this .head ) return null let search = this .head let mid = this .head while (search.next != this .head ){ if (search.next .next != this .head ){ search = search.next .next mid = mid.next }else { search = search.next } } return mid }