雙向鏈表和普通鏈表的區別在於,在鏈表中,一個節點只有鏈向下一個節點的鏈接,而在雙向鏈表中,鏈接是雙向的:一個鏈向下一個元素,另一個鏈向前一個元素,如下圖所示
雙向鏈表簡介
鏈表結構總結
單向鏈表有head和next兩個屬性,雙向鏈表有head、tail、next、prev四個屬性。處理好它們的指向,相當於將它們正確地連接在一起,這樣就組成了一條鏈,這就是簡單鏈表的實現
刪除節點的原理
只要沒有引用指向該對象,無論該對象是否有引用指向其他對象,該對象都會被回收(刪除)。
雙向鏈表
既可以從頭遍歷到尾,又可以從尾遍歷到頭。也就是說鏈表連接的過程是雙向的,它的實現原理是:一個節點既有向前連接的引用,也有一個向後連接的引用。
雙向鏈表的缺點
每次在插入或刪除某個節點時,都需要處理四個引用,而不是兩個,實現起來會困難些;
相對於單向鏈表,所佔內存空間更大一些;
但是,相對於雙向鏈表的便利性而言,這些缺點微不足道。
雙向鏈表的結構
雙向鏈表不僅有head指針指向第一個節點,而且有tail指針指向最後一個節點;
每一個節點由三部分組成:item儲存數據、prev指向前一個節點、next指向後一個節點;
雙向鏈表的第一個節點的prev指向null;
雙向鏈表的最後一個節點的next指向null;
雙向鏈表常見的操作(方法)
- append(element):向鏈表尾部添加一個新的項;
- inset(position,element):向鏈表的特定位置插入一個新的項;
- get(element):獲取對應位置的元素;
- indexOf(element):返回元素在鏈表中的索引,如果鏈表中沒有元素就返回-1;
- update(position,element):修改某個位置的元素;
- removeAt(position):從鏈表的特定位置移除一項;
- isEmpty():如果鏈表中不包含任何元素,返回trun,如果鏈表長度大於0則返回false;
- size():返回鏈表包含的元素個數,與數組的length屬性類似;
- toString():由於鏈表項使用了Node類,就需要重寫繼承自JavaScript對象默認的toString方法,讓其只輸出元素的值;
- forwardString():返回正向遍歷節點字符串形式;
- backwordString():返回反向遍歷的節點的字符串形式;
參數中凡是有position的都要進行越界判斷。
封裝雙向鏈表
創建雙向鏈表類
//封裝雙向鏈表類
function DoubleLinklist(){
//封裝內部類:節點類
function Node(data){
this.data = data
this.prev = null
this.next = null
}
//屬性
this.head = null
this.tail ==null
this.length = 0
}
append 方法
//append方法
DoubleLinklist.prototype.append = data => {
//1.根據data創建新節點
let newNode = new Node(data)
//2.添加節點
//情況1:添加的是第一個節點
if (this.length == 0) {
this.tail = newNode
this.head = newNode
//情況2:添加的不是第一個節點
}else {
newNode.prev = this.tail
this.tail.next = newNode
this.tail = newNode
}
//3.length+1
this.length += 1
}
toString 方法
//將鏈表轉變爲字符串形式
//一.toString方法
DoubleLinklist.prototype.toString = () => {
return this.backwardString()
}
//二.forwardString方法 —— 從後向前
DoubleLinklist.prototype.forwardString = () => {
//1.定義變量
let current =this.tail
let resultString = ""
//2.依次向前遍歷,獲取每一個節點
while (current) {
resultString += current.data + "--"
current = current.prev
}
return resultString
}
//三.backwardString方法 —— 從前向後
DoubleLinklist.prototype.backwardString = () => {
//1.定義變量
let current = this.head
let resultString = ""
//2.依次向後遍歷,獲取每一個節點
while (current) {
resultString += current.data + "--"
current = current.next
}
return resultString
}
insert 方法
//insert方法
DoubleLinklist.prototype.insert = (position, data) => {
//1.越界判斷
if (position < 0 || position > this.length) return false
//2.根據data創建新的節點
let newNode = new Node(data)
//3.插入新節點
//原鏈表爲空
//情況1:插入的newNode是第一個節點
if (this.length == 0) {
this.head = newNode
this.tail = newNode
//原鏈表不爲空
}else {
//情況2:position == 0
if (position == 0) {
this.head.prev = newNode
newNode.next = this.head
this.head = newNode
//情況3:position == this.length
} else if(position == this.length){
this.tail.next = newNode
newNode.prev = this.tail
this.tail = newNode
//情況4:0 < position < this.length
}else{
let current = this.head
let index = 0
while(index++ < position){
current = current.next
}
//修改pos位置前後節點變量的指向
newNode.next = current
newNode.prev = current.prev
current.prev.next = newNode
current.prev = newNode
}
}
//4.length+1
this.length += 1
return true//返回true表示插入成功
}
get 方法
//get方法
DoubleLinklist.prototype.get = position => {
//1.越界判斷
if (position < 0 || position >= this.length) {//獲取元素時position不能等於length
return null
}
//2.獲取元素 —— 遍歷優化
let current = null
let index = 0
//this.length / 2 > position:從頭開始遍歷
if ((this.length / 2) > position) {
current = this.head
while(index++ < position){
current = current.next
}
//this.length / 2 =< position:從尾開始遍歷
}else{
current = this.tail
index = this.length - 1
while(index-- > position){
current = current.prev
}
}
return current.data
}
indexOf 方法
//indexOf方法
DoubleLinklist.prototype.indexOf = data => {
//1.定義變量
let current = this.head
let index = 0
//2.遍歷鏈表,查找與data相同的節點
while(current){
if (current.data == data) {
return index
}
current = current.next
index += 1
}
return -1
}
update 方法
//update方法
DoubleLinklist.prototype.update = (position, newData) => {
//1.越界判斷
if (position < 0 || position >= this.length) {
return false
}
//2.尋找正確的節點
let current = this.head
let index = 0
//this.length / 2 > position:從頭開始遍歷
if (this.length / 2 > position) {
while(index++ < position){
current = current.next
}
//this.length / 2 =< position:從尾開始遍歷
}else{
current = this.tail
index = this.length - 1
while (index -- > position) {
current = current.prev
}
}
//3.修改找到節點的data
current.data = newData
return true//表示成功修改
}
removeAt 方法
//removeAt方法
DoubleLinklist.prototype.removeAt = position => {
//1.越界判斷
if (position < 0 || position >= this.length) {
return null
}
//2.刪除節點
//當鏈表中length == 1
//情況1:鏈表只有一個節點
let current = this.head//定義在最上面方便以下各種情況返回current.data
if (this.length == 1) {
this.head = null
this.tail = null
//當鏈表中length > 1
} else{
//情況2:刪除第一個節點
if (position == 0) {
this.head.next.prev = null
this.head = this.head.next
//情況3:刪除最後一個節點
}else if(position == this.length - 1){
current = this.tail//該情況下返回被刪除的最後一個節點
this.tail.prev.next = null
this.tail = this.tail.prev
}else{
//情況4:刪除鏈表中間的節點
// 沒有引用指向 current,它會自動銷燬
let index = 0
while(index++ < position){
current = current.next
}
current.next.prev = current.prev
current.prev.next = current.next
}
}
//3.length -= 1
this.length -= 1
return current.data//返回被刪除節點的數據
}
其他方法
/*--------------------其他方法-------------------*/
//八.remove方法
DoubleLinklist.prototype.remove = data => {
//1.根據data獲取下標值
let index = this.indexOf(data)
//2.根據index刪除對應位置的節點
return this.removeAt(index)
}
//九.isEmpty方法
DoubleLinklist.prototype.isEmpty = () => {
return this.length == 0
}
//十.size方法
DoubleLinklist.prototype.size = () => {
return this.length
}
//十一.getHead方法:獲取鏈表的第一個元素
DoubleLinklist.prototype.getHead = () => {
return this.head.data
}
//十二.getTail方法:獲取鏈表的最後一個元素
DoubleLinklist.prototype.getTail = () => {
return this.tail.data
}
完整實現
//封裝雙向鏈表
function DoubleLinklist(){
//封裝內部類:節點類
function Node(data){
this.data = data
this.prev = null
this.next = null
}
//屬性
this.head = null
this.tail ==null
this.length = 0
//常見的操作:方法
//一.append方法
DoubleLinklist.prototype.append = data => {
//1.根據data創建新節點
let newNode = new Node(data)
//2.添加節點
//情況1:添加的是第一個節點
if (this.length == 0) {
this.tail = newNode
this.head = newNode
//情況2:添加的不是第一個節點
}else {
newNode.prev = this.tail
this.tail.next = newNode
this.tail = newNode
}
//3.length+1
this.length += 1
}
//二.將鏈表轉變爲字符串形式
//2.1.toString方法
DoubleLinklist.prototype.toString = () => {
return this.backwardString()
}
//2.2.forwardString方法
DoubleLinklist.prototype.forwardString = () => {
//1.定義變量
let current =this.tail
let resultString = ""
//2.依次向前遍歷,獲取每一個節點
while (current) {
resultString += current.data + "--"
current = current.prev
}
return resultString
}
//2.3.backwardString方法
DoubleLinklist.prototype.backwardString = () => {
//1.定義變量
let current = this.head
let resultString = ""
//2.依次向後遍歷,獲取每一個節點
while (current) {
resultString += current.data + "--"
current = current.next
}
return resultString
}
//三.insert方法
DoubleLinklist.prototype.insert = (position, data) => {
//1.越界判斷
if (position < 0 || position > this.length) return false
//2.根據data創建新的節點
let newNode = new Node(data)
//3.插入新節點
//原鏈表爲空
//情況1:插入的newNode是第一個節點
if (this.length == 0) {
this.head = newNode
this.tail = newNode
//原鏈表不爲空
}else {
//情況2:position == 0
if (position == 0) {
this.head.prev = newNode
newNode.next = this.head
this.head = newNode
//情況3:position == this.length
} else if(position == this.length){
this.tail.next = newNode
newNode.prev = this.tail
this.tail = newNode
//情況4:0 < position < this.length
}else{
let current = this.head
let index = 0
while(index++ < position){
current = current.next
}
//修改pos位置前後節點變量的指向
newNode.next = current
newNode.prev = current.prev
current.prev.next = newNode
current.prev = newNode
}
}
//4.length+1
this.length += 1
return true//返回true表示插入成功
}
//四.get方法
DoubleLinklist.prototype.get = position => {
//1.越界判斷
if (position < 0 || position >= this.length) {//獲取元素時position不能等於length
return null
}
//2.獲取元素
let current = null
let index = 0
//this.length / 2 > position:從頭開始遍歷
if ((this.length / 2) > position) {
current = this.head
while(index++ < position){
current = current.next
}
//this.length / 2 =< position:從尾開始遍歷
}else{
current = this.tail
index = this.length - 1
while(index-- > position){
current = current.prev
}
}
return current.data
}
//五.indexOf方法
DoubleLinklist.prototype.indexOf = data => {
//1.定義變量
let current = this.head
let index = 0
//2.遍歷鏈表,查找與data相同的節點
while(current){
if (current.data == data) {
return index
}
current = current.next
index += 1
}
return -1
}
//六.update方法
DoubleLinklist.prototype.update = (position, newData) => {
//1.越界判斷
if (position < 0 || position >= this.length) {
return false
}
//2.尋找正確的節點
let current = this.head
let index = 0
//this.length / 2 > position:從頭開始遍歷
if (this.length / 2 > position) {
while(index++ < position){
current = current.next
}
//this.length / 2 =< position:從尾開始遍歷
}else{
current = this.tail
index = this.length - 1
while (index -- > position) {
current = current.prev
}
}
//3.修改找到節點的data
current.data = newData
return true//表示成功修改
}
//七.removeAt方法
DoubleLinklist.prototype.removeAt = position => {
//1.越界判斷
if (position < 0 || position >= this.length) {
return null
}
//2.刪除節點
//當鏈表中length == 1
//情況1:鏈表只有一個節點
let current = this.head//定義在最上面方便以下各種情況返回current.data
if (this.length == 1) {
this.head = null
this.tail = null
//當鏈表中length > 1
} else{
//情況2:刪除第一個節點
if (position == 0) {
this.head.next.prev = null
this.head = this.head.next
//情況3:刪除最後一個節點
}else if(position == this.length - 1){
current = this.tail//該情況下返回被刪除的最後一個節點
this.tail.prev.next = null
this.tail = this.tail.prev
}else{
//情況4:刪除鏈表中間的節點
let index = 0
while(index++ < position){
current = current.next
}
current.next.prev = current.prev
current.prev.next = current.next
}
}
//3.length -= 1
this.length -= 1
return current.data//返回被刪除節點的數據
}
/*--------------------其他方法-------------------*/
//八.remove方法
DoubleLinklist.prototype.remove = data => {
//1.根據data獲取下標值
let index = this.indexOf(data)
//2.根據index刪除對應位置的節點
return this.removeAt(index)
}
//九.isEmpty方法
DoubleLinklist.prototype.isEmpty = () => {
return this.length == 0
}
//十.size方法
DoubleLinklist.prototype.size = () => {
return this.length
}
//十一.getHead方法:獲取鏈表的第一個元素
DoubleLinklist.prototype.getHead = () => {
return this.head.data
}
//十二.getTail方法:獲取鏈表的最後一個元素
DoubleLinklist.prototype.getTail = () => {
return this.tail.data
}
}