一、什麼是鏈表
鏈表是有序的列表,但是它在內存中是存儲如下:
總結:
- 鏈表是一個有序列表
- 鏈表的數據,在內存空間不一定是連續分佈的
- 鏈表又分單向、雙向或者循環
區別:
1、單向鏈表
2、雙向鏈表
3、循環鏈表(單向循環,雙向循環)
如下圖
使用場景及優缺點:
單向鏈表:
1、單向鏈表需要實現隊列,建設頭結點是尾部,入隊速度快,但是每次出隊需要遍歷到最後一個節點
2、實現棧則比較完美
3、鏈表移除數據需要臨時節點來記錄待刪除節點的前一個節點
雙向鏈表:
1、頭尾節點都有並且是雙向,則實現隊列不用遍歷數據,頭尾一出一進,完美配合
2、刪除數據時不需要臨時節點,只需將刪除節點的前一個節點的向後指向刪除節點的後一個,刪除節點的後一個節點指向前一個節點即可
循環鏈表:解決特殊的算法問題,後續案例介紹
二、代碼實操
1、單向鏈表代碼
object DanxLink {
def main(args: Array[String]): Unit = {
val link = new DanxLink()
// (0 to 10).foreach(link.addFirstNode(_))
(0 to 10).foreach(link.addLastNode(_))
link.showData()
println()
println(link.size())
println(link.get(3))
println(link.remove(3))
println(link.get(3))
println(link.size())
link.showData()
}
}
/**
* 單向鏈表只能從前頭結點向後查找
* head-->1-->2-->3-->4-->null
*/
class DanxLink {
//頭結點
val head = new Node
//長度
private var length = 0
//添加在首位
def addFirstNode(value: Int): Unit = {
add(value, 0, 0, head)
}
//末尾添加
def addLastNode(value: Int): Unit = {
add(value, length, 0, head)
}
/**
* 插入結點
*
* @param value 結點值
* @param index 座標
* @param len 當前所在深度
* @param cur 當前所在節點
*/
def add(value: Int, index: Int, len: Int, cur: Node): Unit = {
if (index > length) {
println("現有 " + length + "個元素" + ",無法插入座標" + index)
return
}
if (index == len) {
val newNode = new Node //創建新節點
newNode.value = value //賦值
newNode.next = cur.next //掛載到鏈表上
cur.next = newNode
length += 1
} else {
add(value, index, len + 1, cur.next) //遞歸調用-處理下一個節點
}
}
/**
* 根據座標查詢數據
*
* @param index
* @return
*/
def get(index: Int): Int = {
get(index, 0, head)
}
/**
* 根據座標查詢數據
*
* @param index 座標
* @param len 當前深度
* @param cur 當前節點
* @return
*/
def get(index: Int, len: Int, cur: Node): Int = {
if (index > length - 1) {
throw new IllegalArgumentException("查詢座標存在")
}
if (index == len) {
//以頭結點開始,所以下個節點纔是數據節點
return cur.next.value
} else {
get(index, len + 1, cur.next)
}
}
/**
* 根據座標刪除節點
* @param index
* @return
*/
def remove(index: Int): Int = {
remove(index,0,head)
}
/**
* 根據座標刪除節點
* @param index 座標
* @param len 深度
* @param cur 當前節點
* @return 刪除值
*/
def remove(index: Int, len: Int, cur: Node): Int = {
//座標大於當前鏈表長度
if (index > length - 1) {
throw new IllegalArgumentException("刪除座標存在")
}
//待刪除節點位置
if (index == len) {
val delNode = cur.next //待刪除節點
cur.next = delNode.next //重新掛載節點
delNode.next = null //節點刪除
length -= 1
delNode.value
} else {
//尋找下一個節點
remove(index, len + 1, cur.next)
}
}
def size() = {
length
}
def isEmpty() = {
length == 0
}
def showData(): Unit = {
var curNode = head.next
for (i <- 0 to length - 1) {
print(curNode.value + "-->")
curNode = curNode.next
}
}
}
class Node {
var value: Int = _
var next: Node = null
var prev:Node = null
}
2、雙向鏈表代碼
object ShuangxLink{
def main(args: Array[String]): Unit = {
val link = new ShuangxLink()
// (0 to 10).foreach(link.addFirstNode(_))
(0 to 10).foreach(link.addLastNode(_))
link.showData()
println()
println(link.size())
println(link.get(3))
println(link.remove(3))
println(link.get(3))
println(link.size())
link.showData()
}
}
/**
*
* 雙向鏈表:需要頭尾節點,並且可以從尾部向頭結點查找
* head <--> 1 <--> 4 <--> tail
*
* 優點:使用雙向鏈表來完成棧、隊列,則從頭尾節點來操作,省略遍歷鏈表的時間了
*/
class ShuangxLink {
val head = new Node //頭結點
val tail = new Node //尾結點
head.next = tail
tail.prev = head
//長度
private var length = 0
//添加在首位
def addFirstNode(value: Int): Unit = {
add(value, 0, 0, head)
}
//末尾添加
def addLastNode(value: Int): Unit = {
add(value, length, 0, head)
}
/**
* 插入結點
*
* @param value 結點值
* @param index 座標
* @param len 當前所在深度
* @param cur 當前所在節點
*/
def add(value: Int, index: Int, len: Int, cur: Node): Unit = {
if (index > length) {
println("現有 " + length + "個元素" + ",無法插入座標" + index)
return
}
if (index == len) {
val newNode = new Node //創建新節點
newNode.value = value //賦值
newNode.next = cur.next //掛載到鏈表上
newNode.prev = cur
cur.next.prev = newNode
cur.next = newNode
length += 1
} else {
add(value, index, len + 1, cur.next) //遞歸調用-處理下一個節點
}
}
/**
* 根據座標查詢數據
*
* @param index
* @return
*/
def get(index: Int): Int = {
get(index, 0, head)
}
/**
* 根據座標查詢數據
*
* @param index 座標
* @param len 當前深度
* @param cur 當前節點
* @return
*/
def get(index: Int, len: Int, cur: Node): Int = {
if (index > length - 1) {
throw new IllegalArgumentException("查詢座標存在")
}
if (index == len) {
//以頭結點開始,所以下個節點纔是數據節點
return cur.next.value
} else {
get(index, len + 1, cur.next)
}
}
/**
* 根據座標刪除節點
* @param index
* @return
*/
def remove(index: Int): Int = {
remove(index,0,head)
}
/**
* 根據座標刪除節點
* @param index 座標
* @param len 深度
* @param cur 當前節點
* @return 刪除值
*/
def remove(index: Int, len: Int, cur: Node): Int = {
//座標大於當前鏈表長度
if (index > length - 1) {
throw new IllegalArgumentException("刪除座標存在")
}
//待刪除節點位置
if (index == len) {
val delNode = cur.next //待刪除節點
cur.next = delNode.next //它的前一個節點指向它的後一個節點
delNode.next.prev = cur //它的後一個節點的前一個節點指向它的前一個節點
delNode.next = null //節點刪除
delNode.prev = null
length -= 1
delNode.value
} else {
//尋找下一個節點
remove(index, len + 1, cur.next)
}
}
def size() = {
length
}
def isEmpty() = {
length == 0
}
def showData(): Unit = {
var curNode = head.next
for (i <- 0 to length - 1) {
print(curNode.value + "-->")
curNode = curNode.next
}
}
}