双向链表
什么是双向链表?
既可以从头遍历到尾,又可以从尾遍历到头,一个节点机油向前链接的引用,也有向后链接的引用
双向链表的缺点:
每次在插入或者删除某个节点的时候,需要处理四个引用,而不是两个,相对于单向链表,占用较多的内存空间,但是在使用层度上这些是微不足道的。
双向链表的操作
<1> append(element):向列表尾部添加一个新的项
<2> insert(position,element):向列表的特定位置插入一个新的项
<3> get(position):获取对应位置的元素
<4> indexOf(element):返回元素在列表中的索引,如果列表中没有该元素则返回-1
<5> update(position,element):修改某个位置的元素
<6> removeAt(position):从列表的特定位置移除一项
<7> remove(element):从列表中移除一项
<8> isEmpty():如果链表中不包含任何元素,返回true,如果链表长度大于0则返回false
<9> size():返回链表中包含的元素个数,与数组的length属性类似
<10> toString():由于列表项使用了Node类,就需要重写继承自JavaScript对象默认的toString方法。让其只输出元素的值
<11> forwardString():返回正向遍历的节点字符串形式
<12> backwardString():返回反向遍历的节点字符串的形式
双向链表方法的实现
1、toString、forwardString、backwardString方法实现
//封装双向链表 function DoublyLinkedList() { this.head = null this.tail = null this.length = 0 //内部类,节点类 function Node(data) { this.data = data this.prev = null this.next = null }//toString方法 DoublyLinkedList.prototype.toString = function () { return this.backwardString() } //forwardString DoublyLinkedList.prototype.forwardString = function () { //定义变量 var current = this.tail var result = '' //依次向前遍历获取节点 while(current){ result += current.data + ' ' current = current.prev } return result } //backwardString DoublyLinkedList.prototype.backwardString = function () { //定义变量 var current = this.head var result = '' //依次向后遍历获取节点 while(current){ result += current.data + ' ' current = current.next } return result } }
测试:
//测试 var list = new DoublyLinkedList() list.append('a') list.append('b') list.append('c') alert(list.backwardString()) alert(list.forwardString())
2、append方法的实现
//append方法 DoublyLinkedList.prototype.append = function (data) { //创建节点 var newNode = new Node(data) //判断添加的节点是否是第一个节点 if(this.length == 0){ this.head = newNode this.tail = newNode } else { newNode.prev=this.tail this.tail.next=newNode this.tail=newNode } //length加一 this.length += 1 }
测试:
var list = new DoublyLinkedList() list.append('a') list.append('b') list.append('c') alert(list)
3、insert方法的实现
//封装双向链表 function DoublyLinkedList() { this.head = null this.tail = null this.length = 0 //内部类,节点类 function Node(data) { this.data = data this.prev = null this.next = null } DoublyLinkedList.prototype.insert = function (position, data) { //越界判断 if (position < 0 || position > this.length) return false //创建新的节点 var newNode = new Node(data) //判断链表是否为空 if (this.length == 0) { this.head = newNode this.tail = newNode } else { //第一种情况,判断prev是否为空。也就是原来有一个节点现在需要插入到节点的这个位置 if (position == 0) { this.head.prev = newNode //原来的节点prev指向新节点 newNode.next = this.head //新节点的next指向原来的节点 this.head = newNode //head指向新节点 } else if (position == this.length) { //第二种情况 newNode.prev = this.tail this.tail.next = newNode this.tail = newNode } else { //第三种情况 var current = this.head var index = 0 while (index++ < position) { current = current.next } //修改指针 newNode.next = current newNode.prev = current.prev current.prev.next = newNode current.prev = newNode } } this.length += 1 return true } }
测试:
//测试 var list = new DoublyLinkedList() list.append('a') list.append('b') list.append('c') list.insert(1,'aaa') list.insert(3,'bbb') list.insert(5,'ccc') alert(list)
4、get方法
//封装双向链表 function DoublyLinkedList() { this.head = null this.tail = null this.length = 0 //内部类,节点类 function Node(data) { this.data = data this.prev = null this.next = null } DoublyLinkedList.prototype.get = function (position) { //越界判断 if (position < 0 || position >= this.length) return null //定义变量 var index = 0 var current = this.head while (index++ < position) { current = current.next } return current.data } }
测试:
//测试 var list = new DoublyLinkedList() list.append('a') list.append('b') list.append('c') alert(list.get(2))
这种方法查找的效率并不是很高,只能从前往后进行查找,如果此时链表的数据过多,那么就会使效率变低,所以进行判断,如果此时的链表长度除于2的结果大于position,就需要从后往前进行查找
所以此时需要这样写
DoublyLinkedList.prototype.get = function (position) { //越界判断 if (position < 0 || position >= this.length) return null if (this.length / 2 > position ) { //定义变量 var index = 0 var current = this.head while (index++ < position) { current = current.next } return current.data } else{ //定义变量 var index = this.length var current = this.tail while (index-- < position) { current = current.prev } return current.data } }
5、indexOf方法
//封装双向链表 function DoublyLinkedList() { this.head = null this.tail = null this.length = 0 //内部类,节点类 function Node(data) { this.data = data this.prev = null this.next = null } DoublyLinkedList.prototype.indexOf = function (data) { var current = this.head var index = 0 while (current) { if (current.data == data) { return index } current = current.next index++ } return -1 } }
测试:
//测试 var list = new DoublyLinkedList() list.append('a') list.append('b') list.append('c') alert(list.indexOf('b')) alert(list.indexOf('d'))
6、update方法
//封装双向链表 function DoublyLinkedList() { this.head = null this.tail = null this.length = 0 //内部类,节点类 function Node(data) { this.data = data this.prev = null this.next = null } DoublyLinkedList.prototype.update = function (position, newData) { //越界判断 if (position < 0 || position >= this.length) return false //定义变量 var curret = this.head var index = 0 while (index++ < position) { current = curret.next } //修改data current.data=newData return true //表示修改成功 } }
测试:
//测试 var list = new DoublyLinkedList() list.append('a') list.append('b') list.append('c') alert(list.update(1,'d')) alert(list)
还有一种思路跟get方法一样,进行判断,是否需要从后往前进行查找修改
DoublyLinkedList.prototype.update = function (position, newData) { //越界判断 if (position < 0 || position >= this.length) return false if (this.length / 2 > position) { //定义变量 var curret = this.head var index = 0 while (index++ < position) { current = curret.next } //修改data current.data=newData return true //表示修改成功 } else { //定义变量 var curret = this.tail var index = this.length while (index-- < position) { current = curret.prev } //修改data current.data=newData return true //表示修改成功 } }
7、removeAt方法
//封装双向链表 function DoublyLinkedList() { this.head = null this.tail = null this.length = 0 //内部类,节点类 function Node(data) { this.data = data this.prev = null this.next = null } DoublyLinkedList.prototype.removeAt = function (position) { //越界判断 if (position < 0 || position >= this.length) return null var current = this.head //提到前面,便于获取删除的节点的data //第一种情况只有一个节点 if (this.length == 1) { this.head = null this.tail = null } else { //第二种情况,长度不为1,删除第一个节点 if (position == 0) { this.head.next.prev = null this.head = this.head.next } else if (position == this.length - 1) { current = this.tail //current指向尾部 //第三种情况,删除最后的节点 this.tail.prev.next = null this.tail = this.tail.prev } else { //第四种情况,在中间删除 var index = 0 while (index++ < position) { current = current.next } current.prev.next = current.next current.next.prev = current.prev } } //length减一 this.length -= 1 return current.data } }
测试:
//测试 var list = new DoublyLinkedList() list.append('a') list.append('b') list.append('c') alert(list.removeAt(1)) alert(list)
8、remove方法
//封装双向链表 function DoublyLinkedList() { this.head = null this.tail = null this.length = 0 //内部类,节点类 function Node(data) { this.data = data this.prev = null this.next = null } DoublyLinkedList.prototype.indexOf = function (data) { var current = this.head var index = 0 while (current) { if (current.data == data) { return index } current = current.next index++ } return -1 } DoublyLinkedList.prototype.removeAt = function (position) { //越界判断 if (position < 0 || position >= this.length) return null var current = this.head //提到前面,便于获取删除的节点的data //第一种情况只有一个节点 if (this.length == 1) { this.head = null this.tail = null } else { //第二种情况,长度不为1,删除第一个节点 if (position == 0) { this.head.next.prev = null this.head = this.head.next } else if (position == this.length - 1) { current = this.tail //current指向尾部 //第三种情况,删除最后的节点 this.tail.prev.next = null this.tail = this.tail.prev } else { //第四种情况,在中间删除 var index = 0 while (index++ < position) { current = current.next } current.prev.next = current.next current.next.prev = current.prev } } //length减一 this.length -= 1 return current.data } DoublyLinkedList.prototype.remove = function (data) { //根据data获取索引 var index = this.indexOf(data) //根据index删除对应位置的节点 return this.removeAt(index) } }
测试:
//测试 var list = new DoublyLinkedList() list.append('a') list.append('b') list.append('c') alert(list.remove('b')) alert(list)
9、isEmpty、size方法
//封装双向链表 function DoublyLinkedList() { this.head = null this.tail = null this.length = 0 //内部类,节点类 function Node(data) { this.data = data this.prev = null this.next = null } DoublyLinkedList.prototype.isEmpty = function () { return this.length == 0 } DoublyLinkedList.prototype.size = function () { return this.length } }
测试:
//测试 var list = new DoublyLinkedList() list.append('a') list.append('b') list.append('c') alert(list.isEmpty()) alert(list.size())
10、获取链表的第一个元素、获取列表的最后一个元素
//封装双向链表 function DoublyLinkedList() { this.head = null this.tail = null this.length = 0 //内部类,节点类 function Node(data) { this.data = data this.prev = null this.next = null } DoublyLinkedList.prototype.getFirst = function () { return this.head.data } DoublyLinkedList.prototype.getLast = function () { return this.tail.data } }
测试:
var list = new DoublyLinkedList() list.append('a') list.append('b') list.append('c') alert(list.getFirst()) alert(list.getLast())