Go語言講述數據結構之單鏈表

首先要知道爲什麼引入鏈表,以及什麼情況下使用鏈表,鏈表有哪些缺點?

我們使用鏈表就是爲了避免插入和刪除數據時帶來的開銷,同時鏈表可以不連續(就是在內存中的地址不一定是連續的),所以對於頻繁的增加和刪除節點,鏈表是不需要進行大量的數據遷移(相對於數組),但是對於鏈表的訪問,時間複雜度是O(n),不像數組可以根據下表以時間複雜度O(1)來訪問。對於時間複雜度,可能leetcode刷的比較多的同學深有體會,時間複雜度往往決定了你的代碼運行的快慢,超過了百分之多少的人,這個很有成就感的,哈哈。

我們來看一下單鏈表長什麼樣子:

有一個頭指針,指向第一個元素,每個節點中包含指向下一個節點的地址(這個很重要),以此類推,最後一個節點指向爲空。這就構成了一個單鏈表,其中節點的地址不一定是連續的。

這裏提一下爲什麼要有頭指針(Head),頭指針其實也是一個節點,這個節點中可以不包含數據,但是一定要有指向首元節點的指針,試想一下,如果沒有頭指針,我們在a1前插入節點和刪除a1就會比較麻煩,所以引入頭指針對我們操作整個單鏈表會非常的方便。

對於go語言我們應該怎麼使用代碼描述一個單鏈表呢?

//定義一個節點
type Node struct {
	e    interface{}  //節點的數據
	next *Node        //下一個節點的地址
}

//定義一個單鏈表
type LinkList struct {
	head *Node       //鏈表的頭指針
	size int         //鏈表的長度
}

 

對於單鏈表,我們來實現下面幾種簡單的操作(鏈表複雜的操作,也是以這幾種爲基礎的)

//初始化單鏈表
func (L *LinkList) Init() *LinkList{}

//獲取鏈表的長度
func (L *LinkList) GetSize() int{}

//判斷鏈表是否爲空
func (L *LinkList) IsEmpty() bool{}

//單鏈表插入數據
func (L *LinkList) Insert(e interface{}, index int){}

//單鏈表刪除數據
func (L *LinkList) Delete(index int){}

//單鏈表查詢元素
func (L *LinkList) FindEle(e interface{}) (index int ){}

下面我們來依次講解這幾種操作:

初始化鏈表:

初始化鏈表就是生成頭指針,有沒有數據都可以,但是一定要有地址能表示這個頭指針。

//初始化鏈表

func (L *LinkList) Init() *LinkList{
	L.size = 0             //生成的單鏈表沒有數據,因此size爲0
	L.head = new(Node)     //我們生成一個空Node來表示
	return L
}

 

獲取鏈表的長度:

        直接返回鏈表的size即可 。

func (L *LinkList) GetSize() int{
	return L.size
}

 

判斷鏈表是否爲空:

        這個也是很簡單的,就不解釋了

func (L *LinkList) IsEmpty() bool{
	return L.size == 0
}

 

單鏈表插入數據:

        單鏈表插入數據, 需要經過兩步,一:把待插入節點的next地址指向插入位置的後一個節點;二:把待插入位置的前一個節點的next地址指向我們的待插入數據,這樣就完成了一個節點的插入,覺得我說的不清楚的可以去看書本的描述。

//單鏈表插入數據

func (L *LinkList) Insert(e interface{}, index int){
	if index<0 || index>L.size{
		panic("out of range")          //確保插入的位置有效
	}
	preNode := L.head
	for i:=0 ; i<index;i++{
		preNode = preNode.next
	}                                  //移動到待插入位置

	curNode := &Node{e,preNode.next}   //生成插入節點,執行步驟一
	preNode.next = curNode             //執行步驟二
	L.size++                           //鏈表的大小加一
}

 

單鏈表的刪除:

        我們刪除一個節點,只要將這個節點的前一個節點的next地址指向這個節點的next地址,即指向下一個節點。如圖所示,直接把a.next指向c即可完成b節點的刪除。

func (L *LinkList) Delete(index int){
	if index<0 || index>L.size{
		panic("out of range")                  //確保刪除位置有效
	}
	preNode := L.head
	for i:=0;i<index-1;i++{
		preNode = preNode.next                //移動到待刪除節點的前一個節點
	}
	preNode.next = preNode.next.next  //將待刪除節點的前一個節點的next指向待刪除節點的後一個節點
	L.size--
}

查找元素:

        查找元素,我們採用遍歷節點並判斷是否相等 的方法

func (L *LinkList) FindEle(E interface{}) int{
	var index  = -1      //用來存儲返回的位置,沒這個數據返回-1
	preNode := L.head
	for i:=0;i<L.size;i++{    //遍歷整個單鏈表
		if preNode.next != nil && preNode.next.e == E{    //判斷鏈表是否遍歷完,同時判斷節點的值與要查的值是否相等
			index = i
			return index
		}
		preNode = preNode.next
	}
	return index
}

 

到了這裏,單鏈表的基本操作就已經結束了,我們可以結合這些基本的操作來完成複雜的操作,比如,鏈表的反轉,有興趣的小夥伴可以試試哦, 覺得我寫的有問題的小夥伴也可以評論我們來聊聊,共同進步呢。

 

其實在這裏寫博客,也算是對自己的一個監督吧,加油,爭取周更。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章