《大話數據結構》學習筆記 —— 03 線性表之循環鏈表(golang實現)


循環鏈表


前言

普通的單鏈表有一個明顯的缺點,如果不從頭結點出發,就無法訪問到全部結點。


定義

將單鏈表中終端結點的指針由空指針改爲指向頭結點,就使整個單鏈表形成一個環,這種頭尾相接的單鏈表稱爲單循環鏈表,簡稱循環鏈表。


與單鏈表的區別

其實循環鏈表和單鏈表的主要區別在於循環的判斷條件上,原來是判斷p->next是否爲空,現在是判斷p->next是否等於頭結點。


代碼實現

package CricleLinkling

import "fmt"

// 循環表結點
type CricleLinkNode struct {
	value interface{}
	pNext *CricleLinkNode
}

// 構造結點
func NewCricleLinkNode(data interface{}) *CricleLinkNode {
	return &CricleLinkNode{
		value: data,
		pNext: nil,
	}
}

// 返回數據
func (node *CricleLinkNode) Value() interface{} {
	return node.value
}

// 返回下一個結點
func (node *CricleLinkNode) PNext() *CricleLinkNode {
	return node.pNext
}

// 定義循環鏈表接口
type CricleLink interface {

	// 返回第一個數據結點
	GetFirstNode() *CricleLinkNode

	// 插入結點(頭插法)
	InsertNodeFront(node *CricleLinkNode)

	// 插入結點(尾插法)
	InsertNodeBack(node *CricleLinkNode)

	// 插入某結點前
	InsertNodeValueFront(dest interface{}, node *CricleLinkNode) bool

	// 插入某結點後
	InsertNodeValueBack(dest interface{}, node *CricleLinkNode) bool

	// 獲取指定索引上的結點
	GetNodeAtIndex(index int) *CricleLinkNode

	// 刪除結點
	DeleteNode(dest *CricleLinkNode) bool

	// 刪除指定索引上的結點
	DeleteAtIndex(index int) bool

	// 返回字符串
	String() string
}

// 循環鏈表
type CricleLinkList struct {

	// 頭指針
	head *CricleLinkNode

	// 長度
	length int
}

// 初始化循環鏈表
func NewCricleLinkList() *CricleLinkList {

	// 初始化頭結點
	head := NewCricleLinkNode(nil)

	return &CricleLinkList{
		head:   head,
		length: 0,
	}
}

// 獲取第一個數據結點
func (list *CricleLinkList) GetFirstNode() *CricleLinkNode {
	return list.head.pNext
}

// 獲取指定索引上的結點
func (list *CricleLinkList) GetNodeAtIndex(index int) *CricleLinkNode {

	// 索引越界
	if index > list.length-1 || index < 0 {
		return nil
	}

	// 備份頭結點
	bak := list.head

	// 向後循環
	for index > -1 {
		bak = bak.pNext
		index--
	}

	return bak
}

// 頭部插入
func (list *CricleLinkList) InsertNodeFront(node *CricleLinkNode) {

	// 備份頭結點
	bak := list.head

	// 空鏈表
	if bak.pNext == nil {

		// 新結點賦值爲第一個數據結點
		bak.pNext = node

		// 新結點的指針指向頭結點
		node.pNext = bak
		list.length++
		return
	}

	// 新結點的指針,指向原來的第一個數據結點
	node.pNext = bak.pNext

	// 頭結點指向新結點
	bak.pNext = node

	list.length++
	return
}

// 尾部插入
func (list *CricleLinkList) InsertNodeBack(node *CricleLinkNode) {

	// 備份頭結點
	bak := list.head
	bak2 := list.head

	// 空鏈表
	if bak.pNext == nil {

		// 新結點賦值爲第一個數據結點
		bak.pNext = node

		// 新結點的指針指向頭結點
		node.pNext = bak

		list.length++
		return
	}

	// 循環到鏈表末尾
	for bak.pNext != list.head {
		bak = bak.pNext
	}

	// 終端結點的指針指向新結點
	bak.pNext = node

	// 新結點的指針指向頭結點
	node.pNext = bak2

	list.length++
	return
}

// 插入某結點前
func (list *CricleLinkList) InsertNodeValueFront(dest interface{}, node *CricleLinkNode) bool {

	// 備份頭結點
	bak := list.head

	// 循環到末尾,直至找到目標結點
	for bak.pNext != bak && bak.pNext.value != dest {
		bak = bak.pNext
	}

	// 找到
	if bak.pNext.value == dest {

		// 目標結點的上一個數據結點的指針,賦值給新結點的指針
		node.pNext = bak.pNext

		// 目標結點的上一個數據結點的指針,指向新結點
		bak.pNext = node

		list.length++
		return true
	}

	return false

}

// 插入某結點後
func (list *CricleLinkList) InsertNodeValueBack(dest interface{}, node *CricleLinkNode) bool {

	// 備份頭結點
	bak := list.head

	// 循環到末尾,直至找到目標結點
	for bak.pNext != bak && bak.pNext.value != dest {
		bak = bak.pNext
	}

	// 找到
	if bak.pNext.value == dest {

		// 目標結點的指針賦值給新結點的指針
		node.pNext = bak.pNext.pNext

		// 目標結點的指針指向新結點
		bak.pNext.pNext = node

		list.length++
		return true
	}

	return false

}

// 刪除結點
func (list *CricleLinkList) DeleteNode(dest *CricleLinkNode) bool {

	// 備份頭結點
	bak := list.head

	// 結點爲空
	if dest == nil {
		return false
	}

	// 循環到末尾,直至找到目標結點
	for bak.pNext != bak && bak.pNext != dest {
		bak = bak.pNext
	}

	// 找到
	if bak.pNext == dest {

		// 目前結點的指針賦值給目標結點的上一個數據結點的指針
		bak.pNext = bak.pNext.pNext

		list.length--
		return true
	}

	return false
}

// 刪除指定索引上的結點
func (list *CricleLinkList) DeleteAtIndex(index int) bool {

	// 索引越界
	if index > list.length-1 || index < 0 {
		return false
	}

	// 備份頭結點
	bak := list.head

	// 向後循環,找到前一個結點
	for index > 0 {
		bak = bak.pNext
		index--
	}

	// 目前結點的指針賦值給目標結點的上一個數據結點的指針
	bak.pNext = bak.pNext.pNext
	list.length--

	return true
}

// 返回字符串
func (list *CricleLinkList) String() string {

	var listString string

	// 頭結點
	p := list.head

	// 循環輸出
	for p.pNext != list.head {
		listString += fmt.Sprintf("%v-->", p.pNext.value)
		p = p.pNext
	}

	listString += fmt.Sprintf("nil")

	return listString
}


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