双链表介绍
双链表(Doubly Linked List)是一种链表数据结构,它的每个节点都有两个链接:一个指向前一个节点(如果有的话),另一个指向下一个节点(如果有的话)。这使得双链表在遍历、插入和删除节点时都具有一定的灵活性。
双链表和单链表的基本操作有很多相似的。
因此这里提供一篇单链表基本操作的博客:Golang——实现单链表-优快云博客
双链表与单链表的区别
- 指针数量:
- 单链表(Singly Linked List)中的每个节点只有一个指向下一个节点的指针。
- 双链表中的每个节点有两个指针:一个指向前一个节点,另一个指向下一个节点。
- 遍历方向:
- 单链表只能从头节点开始,沿着指针方向向前遍历到尾节点。
- 双链表可以从头节点开始向前遍历,也可以从尾节点开始向后遍历,或者从任何中间节点开始双向遍历。
- 插入和删除操作:
- 在单链表中,插入和删除节点需要知道前一个节点(除了头节点外)。例如,要删除一个节点,你需要知道该节点的前一个节点,以便更新其
next
指针。 - 在双链表中,插入和删除节点时,你可以直接访问前一个节点和下一个节点,这使得操作更加简单和直接。
- 在单链表中,插入和删除节点需要知道前一个节点(除了头节点外)。例如,要删除一个节点,你需要知道该节点的前一个节点,以便更新其
- 应用场景:
- 单链表通常用于只需要单向遍历的场景,如实现简单的栈或队列。
- 双链表则更适用于需要双向遍历或快速访问前后节点的场景,如实现双向队列(deque)或需要频繁进行节点插入和删除操作的应用。
本文通过定义结构体来实现双链表的操作。id为链表节点的id,name为id对应节点的数据,pre表示前一个节点的数据,next表示下一个节点的数据。双链表是双向连接的因此比单链表多创建一个pre
type Twoline struct {
id int
name string
pre *Twoline
next *Twoline
}
Input方法,实现添加节点操作
这里的操作与单链表添加节点的操作类似,首先定义一个辅助节点tead通过循环找到链表的最后一个节点,然后进行插入操作,将最后一个节点的下一个节点定义为要插入的节点,将要插入的节点的上一个节点定义为最后一个节点。
func Input(head *Twoline, hero *Twoline) {
tead := head
for {
if tead.next == nil {
break
}
tead = tead.next
}
tead.next = hero
hero.pre = tead
}
Input2方法,实现顺序添加节点操作
首先还是定义一个辅助节点,这次还另外定义一个flag为了判断要插入的节点是否已经存在。通过辅助节点进行判断插入的节点的id,并进行插入对应位置。这里将要插入的节点定义为hero,辅助节点为tead。进行插入操作时先将hero的下一个节点定义为tead的下一个节点,然后将hero的上一个节点改为tead节点,然后进行判断辅助节点是否为链表的最后一个节点如果不是就将tead的下一个节点的前一个节点设为hero,并将tead的下一个节点设为hero,如果辅助节点是链表的最后一个节点,那么直接将tead的下一个节点设为hero。
func Input2(head *Twoline, hero *Twoline) {
tead := head
flag := true
for {
if tead.next == nil {
break
} else if tead.next.id > hero.id {
break
} else if tead.next.id == hero.id {
flag = false
break
}
tead = tead.next
}
if !flag {
fmt.Println("已经存在", hero.id)
return
} else {
hero.next = tead.next
hero.pre = tead
if tead.next != nil {
tead.next.pre = hero
}
tead.next = hero
}
}
DelectNode方法,实现链表删除节点操作
通过辅助节点以及flag进行寻找要删除的节点的id,找到后将tead的下一个节点的改为下一个节点的下一个节点,直接将要删除的节点的给忽略掉,go语言的将会将其认定为垃圾从而进行回收,然后进行判断tead的下一个节点是否为空,若是不为空则将tead的下一个节点的前一个节点改为tead。
func DelectNode(head *Twoline, id int) {
tead := head
flag := false
for {
if tead.next == nil {
break
} else if tead.next.id == id {
//说明找到了
flag = true
break
}
tead = tead.next
}
if flag {
tead.next = tead.next.next
if tead.next != nil {
tead.next.pre = tead
}
} else {
fmt.Println("删除id不存在", id)
}
}
Show方法,实现双链表的显示操作
这里的操作与单链表类似,具体可以看单链表的显示操作,这里不做讲解。
func Show(head *Twoline) {
tead := head
if tead.next == nil {
fmt.Println("空链表")
return
}
for {
fmt.Printf("[%d ,%s ]==>", tead.next.id, tead.next.name)
tead = tead.next
if tead.next == nil {
break
}
}
}
Show2方法逆序显示双链表的节点操作。
这里的前期操作和顺序显示一样,但在循环显示时需要注意的是这里tead是前面循环得到的双链表的最后那一个节点因此输出时直接输出他的id和name即可,并将其改为前一个节点,进而继续循环输出。
func Show2(head *Twoline) {
tead := head
// 让tead 定位到双链表的最后节点
if tead.next == nil {
fmt.Println("空链表")
return
}
for {
if tead.next == nil {
break
}
tead = tead.next
}
// 遍历链表
for {
fmt.Printf("[%d , %s]==>", tead.id, tead.name)
tead = tead.pre
if tead.pre == nil {
break
}
}
}
主函数
func main() {
head := &Twoline{}
hero1 := &Twoline{
id: 1,
name: "掌门",
}
hero2 := &Twoline{
id: 2,
name: "花花",
}
hero3 := &Twoline{
id: 3,
name: "明明",
}
hero4 := &Twoline{
id: 4,
name: "东东",
}
Input2(head, hero1)
Input2(head, hero3)
Input2(head, hero4)
Input2(head, hero2)
Show(head)
fmt.Println()
fmt.Println("逆序显示")
Show2(head)
fmt.Println()
fmt.Println("删除节点id为1的节点")
DelectNode(head, 1)
Show(head)
}