【Go資料結構】連結串列 Linked List
阿新 • • 發佈:2022-05-18
連結串列 Linked List
元素在記憶體中不是連續存放。
元素間通過指向指標聯絡在一起,訪問元素必須從第一個元素開始遍歷查詢。
- 優點:插入、刪除元素只需改變指標,快 O(1)
- 缺點:隨機訪問慢 O(N)
- 場景:經常插入、刪除元素
分類
- 單向連結串列:節點僅指向下一節點,最後一個節點指向 nil
- 雙向連結串列:每個節點有 2 個指標 pre 和 next,最後一個節點的 next 指向 nil
- 迴圈連結串列:單鏈表 + 最後一個節點指向第一個節點
時間複雜度
- 查詢:O(N)
- 插入、移除:O(1)
實現細節
不帶頭結點:
帶頭結點:
頭指標:
- 頭指標是指向第一個結點的指標,若是連結串列中只有頭結點,則是指向頭結點的指標
- 頭指標具有標識作用,所以常常以頭指標冠以連結串列的名字(指標變數名)
- 無論連結串列是否為空,頭結點均不為空
- 頭指標是連結串列必要元素
頭結點:
- 頭結點是為了操作統一和方便而設立的,放在第一個元素結點之前,其資料域一般無意義(也可以存放表長度)
- 有了頭結點,對第一個元素結點的插入,刪除,其操作和其他結點操作統一了
- 頭結點不一定是連結串列的必要元素
Go實現
package list const ( MAXSIZE = 20 ) type ElemType int //元素型別 const EmptyElement = 0 //空值 /** 線性表的抽象資料型別定義 定義:由零個(空表)或多個數據元素組成的序列 ADT: 線性表List Data: 線性表的資料物件集合為{a1,a2,...,an},每個元素型別為DataType。除了第一個無前驅,最後一個無後繼, 其他每個元素都有一個位元組前驅和直接後繼結點。資料元素間關係一對一。 Operation: InitList(*L);//初始線性表,建立空表 ClearList(*L);//清空線性表資料 ListEmpty(L);//判斷列表是否為空 ListLength(L);//獲取線性表的長度 GetElem(L,i,* e);//獲取指定位置的元素,返回在指標元素中 LocateElem(L,e);//查詢元素線上性表中的位置 ListInsert(*L,i,e);//向線性表中指定位置插入元素 ListDelete(*L, i, *e);//刪除指定位置處的元素 */ //注意線性表中的位置不是按照陣列一樣從0開始,而是按照我們正常習慣1開始的 type MyList interface { //四個基本操作,初始,清空,判斷是否為空,獲取長度 InitList() ClearList() ListEmpty() bool ListLength() int //四個元素操作,插入,刪除,兩種查詢 GetElem(index int, e *ElemType) bool LocateElem(value ElemType) int ListInsert(index int, value ElemType) bool ListDelete(index int, e *ElemType) bool Echo() //Test() }
package my_chain import ( "fmt" "my_go/dataStructure/list" ) //鏈式儲存 //可以用頭指標用來儲存長度 也就是第0個 //但是這個有個弊端就是當儲存的不是int型別時會有問題 //所有還是新增一個欄位表示長度 //為了保持從1開始,頭指標還是不放東西 type LinkList struct { Head *Node Length int } type Node struct { Data list.ElemType Next *Node } //初始化列表 func (l *LinkList) InitList() { l.Head = new(Node) l.Length = 0 } //清空列表(不會清除頭結點) func (l *LinkList) ClearList() { p := new(Node) q := l.Head //q指向第一個結點 //釋放記憶體,其實go可以不需要這個迴圈 for q != nil { p = q q = p.Next p = nil } l.Head.Next = nil l.Length = 0 } //判斷是否為空 func (l *LinkList) ListEmpty() bool { if l.Length == 0 { return true } return false } //獲取長度 func (l *LinkList) ListLength() int { return l.Length } //查 func (l *LinkList) GetElem(index int, e *list.ElemType) bool { if l.Length == 0 { fmt.Println("獲取失敗,佇列為空") return false } if index < 1 || index > l.Length { fmt.Println("獲取失敗,位置錯誤") return false } j := 1 q := l.Head.Next for q != nil && j < index { q = q.Next j++ } //有了這一步其實開頭的判斷可以去掉 if q == nil || j > index { return false } *e = q.Data return true } //按照元素進行查詢,獲取索引 func (l *LinkList) LocateElem(value list.ElemType) int { if l.Length == 0 { fmt.Println("獲取失敗,佇列為空") return 0 } j := 0 q := l.Head.Next for q != nil { j++ if q.Data == value { break } q = q.Next } if j >= list.MAXSIZE { return 0 } return j } //按照索引進行插入資料 func (l *LinkList) ListInsert(index int, value list.ElemType) bool { if l.Length == list.MAXSIZE { //滿了 fmt.Println("插入失敗,佇列已滿") return false } if index < 1 || index > l.Length+1 { fmt.Println(fmt.Sprintf("插入失敗,位置錯誤:%d", index)) return false } front := l.Head //找到插入位置的前驅 for j := 1; j < index; j++ { front = front.Next } //新建節點,加入連結串列 n := new(Node) n.Next = front.Next n.Data = value front.Next = n l.Length++ return true } //刪 func (l *LinkList) ListDelete(index int, e *list.ElemType) bool { if l.Length == 0 { fmt.Println("獲取失敗,佇列為空") return false } if index < 1 || index > l.Length { fmt.Println("獲取失敗,位置錯誤") return false } j := 1 front := l.Head //找到索引的直接前驅 for front.Next != nil && j < index { front = front.Next j++ } if front.Next == nil || j > index { return false } //開始刪除 tmp := front.Next //記錄要刪除的 *e = tmp.Data //返回刪除節點的資料 front.Next = tmp.Next //前驅節點直接指向後繼節點,就跳過了要刪除的節點 //free(tmp) l.Length-- return true } //輸出 func (l *LinkList) Echo() { //遍歷的寫法 curItem := l.Head.Next for i := 0; i < l.Length; i++ { fmt.Print(curItem.Data, " ") curItem = curItem.Next } fmt.Println() }
測試
package my_chain
import (
"fmt"
"my_go/dataStructure/list"
"testing"
)
func Test(t *testing.T) {
fmt.Println("測試開始")
my_list := new(LinkList)
my_list.InitList()
for i := 1; i <= 10; i++ {
my_list.ListInsert(i, list.ElemType(i*i+1))
my_list.Echo()
}
fmt.Println("第5個這裡插入256")
my_list.ListInsert(5, 256)
my_list.Echo()
my_list.ListInsert(199, 99)
var e list.ElemType
my_list.ListDelete(1, &e)
fmt.Println("刪除頭元素:", e)
my_list.Echo()
my_list.ListDelete(my_list.ListLength(), &e)
fmt.Println("刪除尾元素:", e)
my_list.Echo()
my_list.GetElem(6, &e)
fmt.Println("獲取第6個:", e)
fmt.Println("256的位置:", my_list.LocateElem(256))
fmt.Println("長度:", my_list.ListLength())
fmt.Println("開始清空")
my_list.ClearList()
if my_list.ListEmpty() {
fmt.Println("已清空")
my_list.Echo()
}
fmt.Println("測試完成")
}