重学数据结构与算法(7)- Golang双向链表的增删改查

232 阅读3分钟

这是我参与8月更文挑战的第7天,活动详情查看:8月更文挑战

双链表的插入

上篇文章展示了链表的结构,和简单的链表头部和尾部的插入,链表从头部与从尾部打印链表,今天就就用golang来实现,固定位置的节点前后方向插入,节点删除以及索引删除,还有节点的改和查。

双向链表固定节点位置插入

//向某个节点前插入节点
func (dlist *DoubleLinkList) InsertValueHead(dest *DoubleLinkNode, node *DoubleLinkNode) bool {
	phead := dlist.head
	//循环查找
	for phead.next != nil && phead.next != dest {
		phead = phead.next
	}
	if phead.next == dest {

		if phead.next != nil {
			//假如要插入的节点的下一个节点还有节点
			phead.next.prev = node
		}
		node.next = phead.next //插入的节点的下一个节点为头节点的下一个的下一个节点
		//
		node.prev = phead //插入节点的前一个节点为头节点的下一个节点
		phead.prev = phead
		phead.next = node
		dlist.head = phead
		dlist.length++
		return true
	} else {
		return false
	}
}

//向某个节点后插入节点
func (dlist *DoubleLinkList) InsertValueTail(dest *DoubleLinkNode, node *DoubleLinkNode) bool {
	phead := dlist.head
	//循环查找
	for phead.next != nil && phead.next != dest {
		phead = phead.next
	}
	if phead.next == dest {
		if phead.next.next != nil {
			//假如要插入的节点的下一个节点还有节点
			phead.next.next.prev = node //在头节点和该头节点的下个节点之间插入当前节点
		}
		node.next = phead.next.next //插入的节点的下一个节点为头节点的下一个的下一个节点
		phead.next.next = node      //头节点的下一个节点为当前节点
		node.prev = phead.next      //插入节点的前一个节点为头节点的下一个节点
		dlist.head = phead
		dlist.length++
		return true
	} else {
		return false
	}
}

双向链表的节点删除与索引删除

//删除某个节点
func (dlist *DoubleLinkList) DeleteNode(node *DoubleLinkNode) bool {
	//没找到直接报错
	if node == nil {
		return false
	} else {
		phead := dlist.head
		for phead.next != nil && phead.next != node {
			phead = phead.next
		}
		if phead.next == node {
			//在这里删除
			if phead.next.next != nil {
				//删掉要删除节点的前后关系就删除了
				phead.next.next.prev = phead
			}
			phead.next = phead.next.next //跳过删除的节点就是删除了
			dlist.length--
			return true
		} else {
			return false
		}
	}
}

//索引删除
func (dlist *DoubleLinkList) DeleteNodeAtindex(index int) bool {
	//索引越界就报错
	if index > dlist.length-1 || index < 0 {
		return false
	}
	phead := dlist.head
	//依次遍历,从头开始
	for index > 0 {
		phead = phead.next
		index--
	}
	if phead.next.next != nil {
		//找到了,删掉要删除节点的前后关系就删除了
		phead.next.next.prev = phead
		phead.next = phead.next.next //跳过删除的节点就是删除了
		dlist.length--
		return true
	} else {
		//遍历到没找到这个index报错
		return false
	}

}

双向连表的改

//修改节点的值
func (dlist *DoubleLinkList) ModifyNode(node *DoubleLinkNode, value DoubleObject) bool {
	//从头开始遍历
	phead := dlist.head
	//循环查找
	for phead.next != nil && phead.next != node {
		phead = phead.next
	}
	if phead.next == node {
		//找到了
		node.value = value
		return true
	} else {
		//没找到
		return false
	}
}

双向链表的查

//通过索引找节点
func (dlist *DoubleLinkList) GetNodeValueAtindex(index int) *DoubleLinkNode {
	//索引越界就报错
	if index > dlist.length-1 || index < 0 {
		return nil
	}
	phead := dlist.head
	for index > -1 {
		phead = phead.next
		index--
	}
	return phead
}

整体代码所有功能测试

//测试节点头插与尾插和打印
func main() {
	dlist := NewDoubleLinkList()
	node1 := NewNode(1)
	node2 := NewNode(2)
	node3 := NewNode(3)
	node4 := NewNode(4)
	node5 := NewNode(5)
	node6 := NewNode(6)
	node7 := NewNode(7)
	dlist.InsertHead(node1)                         //尾部插入node1,data:1
	dlist.InsertHead(node2)                         //尾部插入node2,data:2
	dlist.InsertHead(node3)                         //尾部插入node3,data:3
	dlist.InsertTail(node4)                         //尾部插入node4,data:4
	dlist.InsertTail(node5)                         //尾部插入node5,data:5
	dlist.InsertValueTail(node3, node6)             //node3的尾部插node6,data:6
	dlist.InsertValueHead(node3, node7)             //node3的头部插node7,data:7
	dlist.DeleteNode(node2)                         //删除node2  ,没有了2
	fmt.Println(dlist.String())                     //打印双向链表
	dlist.DeleteNodeAtindex(0)                      //删除链表第一个节点 ,没有第一个节点就是7,没有了7
	fmt.Println(dlist.String())                     //打印双向链表
	dlist.ModifyNode(node1, 99)                     //把node1的1改成了99
	fmt.Println(dlist.String())                     //打印双向链表
	fmt.Println(dlist.GetNodeValueAtindex(0).value) //打印双向链表的第一个元素

}

打印内容(如需测试需要将上篇文章的代码一起复制到一个文件中一起运行):

7-->3-->6-->1-->4-->5-->nil
<--5<--4<--1<--6<--3<--7nil


3-->6-->1-->4-->5-->nil
<--5<--4<--1<--6<--3nil


3-->6-->99-->4-->5-->nil
<--5<--4<--99<--6<--3nil


3

总结

双向链表的增删改查到此基本上完成,分成了两篇文章,没有画图,画图应该是最直观的,仔细研究代码逻辑还是能看懂的,哈哈,没有涉及到乱七八糟的语法,都是一些循环,赋值操作,要弄清楚什么时候变量的值是什么。
在双向链表中跟单向链表一样,它不适合查找操作,只适合频繁的修改或者删除操作。我这里在根据索引查找节点的时候没有判断所以处于链表的位置来决定从头部还是从尾部开始,这是一个优化的方案。