加强巩固对链表的理解,以及一些操作思路,从网络搜集了一些链表的操作习题,使用Go进行了一些实现。
先初始化一个单向链表:
package linked import "fmt" // 定义节点 type Element struct { Value interface{} Next *Element } // 创建一个单向链表 func New(values ...interface{}) (head *Element) { var prev = &Element{} for k,v := range values{ e := &Element{v, nil} if k == 0 { head = e prev = head continue } prev.Next = e prev = e } return } // 打印单向链表 func (h *Element)Print() { next := h for i:=0; next != nil; i++ { fmt.Printf("第%d元素值:%v 内存地址:%p \n",i, next, next) next = next.Next } } //根据元素位置查找 func (h *Element)Find(pos int) *Element { next := h for i:=0; next != nil; i++ { if i == pos { break } next = next.Next } return next }
package main import ( "learn/linked" ) func main() { head := linked.New(11,2,15,8,9,29,32,43) head.Print() }
输出结果: 第0元素值:&{11 0xc0000a6040} 内存地址:0xc0000a6020 第1元素值:&{2 0xc0000a6060} 内存地址:0xc0000a6040 第2元素值:&{15 0xc0000a6080} 内存地址:0xc0000a6060 第3元素值:&{8 0xc0000a60a0} 内存地址:0xc0000a6080 第4元素值:&{9 0xc0000a60c0} 内存地址:0xc0000a60a0 第5元素值:&{29 0xc0000a60e0} 内存地址:0xc0000a60c0 第6元素值:&{32 0xc0000a6100} 内存地址:0xc0000a60e0 第7元素值:&{43 <nil>} 内存地址:0xc0000a6100
下面的操作都基于初始化的单向链表进行操作
1、删除指定的链表节点
分析:主要思想都是「狸猫换太子」,即用下一个节点数据覆盖要删除的节点,然后删除下一个节点。但是如果节点是尾节点时,该方法就行不通了。
package main import ( "learn/linked" ) func main() { head := linked.New(11,2,15,8,9,29,32,43) head.Print() // 查找待删除的节点 e := head.Find(7) deleteElement(head, e) head.Print() } func deleteElement(head, e *linked.Element) { // 删除末尾节点 if e.Next == nil { current := head for current != nil { if current.Next == e { current.Next = nil break } current = current.Next } return } // 非末尾节点 e.Value = e.Next.Value e.Next = e.Next.Next return }
第0元素值:&{11 0xc00011c040} 内存地址:0xc00011c020 第1元素值:&{2 0xc00011c060} 内存地址:0xc00011c040 第2元素值:&{15 0xc00011c080} 内存地址:0xc00011c060 第3元素值:&{8 0xc00011c0a0} 内存地址:0xc00011c080 第4元素值:&{9 0xc00011c0c0} 内存地址:0xc00011c0a0 第5元素值:&{29 0xc00011c0e0} 内存地址:0xc00011c0c0 第6元素值:&{32 0xc00011c100} 内存地址:0xc00011c0e0 第7元素值:&{43 <nil>} 内存地址:0xc00011c100 第0元素值:&{11 0xc00011c040} 内存地址:0xc00011c020 第1元素值:&{2 0xc00011c060} 内存地址:0xc00011c040 第2元素值:&{15 0xc00011c080} 内存地址:0xc00011c060 第3元素值:&{8 0xc00011c0a0} 内存地址:0xc00011c080 第4元素值:&{9 0xc00011c0c0} 内存地址:0xc00011c0a0 第5元素值:&{29 0xc00011c0e0} 内存地址:0xc00011c0c0 第6元素值:&{32 <nil>} 内存地址:0xc00011c0e0
2、输出一个单链表的逆序反转后的链表
解法一:分析:非递归的算法很简单,用三个临时指针 prev、cur、next 在链表上循环一遍即可。递归算法是先逆转下一个节点,再逆转当前节点。
package main import ( "learn/linked" ) func main() { head := linked.New(11,2,15,8,9,29,32,43) head.Print() // 输出一个单链表的逆序反转后的链表 head = reverseByLoop(head) head.Print() } func reverseByLoop(head *linked.Element) *linked.Element { if head.Next == nil { return head } var prev, current, next *linked.Element = nil, head, nil for current != nil { next = current.Next current.Next = prev prev = current current = next } return prev }
输出结果: 第0元素值:&{11 0xc00011c040} 内存地址:0xc00011c020 第1元素值:&{2 0xc00011c060} 内存地址:0xc00011c040 第2元素值:&{15 0xc00011c080} 内存地址:0xc00011c060 第3元素值:&{8 0xc00011c0a0} 内存地址:0xc00011c080 第4元素值:&{9 0xc00011c0c0} 内存地址:0xc00011c0a0 第5元素值:&{29 0xc00011c0e0} 内存地址:0xc00011c0c0 第6元素值:&{32 0xc00011c100} 内存地址:0xc00011c0e0 第7元素值:&{43 <nil>} 内存地址:0xc00011c100 第0元素值:&{43 0xc00011c0e0} 内存地址:0xc00011c100 第1元素值:&{32 0xc00011c0c0} 内存地址:0xc00011c0e0 第2元素值:&{29 0xc00011c0a0} 内存地址:0xc00011c0c0 第3元素值:&{9 0xc00011c080} 内存地址:0xc00011c0a0 第4元素值:&{8 0xc00011c060} 内存地址:0xc00011c080 第5元素值:&{15 0xc00011c040} 内存地址:0xc00011c060 第6元素值:&{2 0xc00011c020} 内存地址:0xc00011c040 第7元素值:&{11 <nil>} 内存地址:0xc00011c020
解法二:递归至链表的最后一个节点开始,从后往前更改指针
package main import ( "learn/linked" ) func main() { head := linked.New(11,2,15,8,9,29,32,43) head.Print() // 输出一个单链表的逆序反转后的链表 head = reverseByRecursion(head) head.Print() } func reverseByRecursion(head *linked.Element) *linked.Element { // 递归结束条件,至链表末尾 if head.Next == nil { return head } newHead := reverseByRecursion(head.Next) head.Next.Next = head head.Next = nil return newHead }
3、删除单链表倒数第 n 个节点
分析:使用快慢指针;看到题目时的第一想法是先遍历一次计算出单链表的长度 length,然后在遍历第二次删除第 length – n + 1 个节点,但是这需要遍历两次。正常的删除第 n 个节点只需要遍历一次就可以,如何只遍历一次找到倒数第 n 个节点呢?可以设置两个指针 p1、p2,首先 p1 和 p2 都指向 head,p2 移动到第 n 个节点,然后 p1 和 p2 同时向后移动,当 p2 移动到末尾时,p1 刚好指向倒数第 n 个节点。因为最后要删除倒数第 n 个节点,所以可以找到倒数第 n + 1 个节点,方便删除节点。