思路:
用pre、cur、next三个指针通过不断后移来遍历链表,遍历时逐个完成每个结点的逆序。
遍历链表时,先保存当前结点的后继结点信息,再修改当前结点指针域的指向,改为指向前驱结点;
需要用一个指针变量(即pre)保存前驱结点的地址(用于上一条的修改),
需要用一个指针变量(即next)保存后继结点的地址(为了还能找到后继结点)。
实现代码+运行结果:
package main
import (
"fmt"
. "github.com/isdamir/gotype" //引入定义的数据结构
)
func Reverse(node *LNode) {
if node == nil || node.Next == nil {
return
}
var pre *LNode //前驱结点
var cur *LNode //当前结点
next := node.Next //后继结点
for next != nil {
cur = next.Next
next.Next = pre
pre = next
next = cur
}
node.Next = pre
}
func main() {
head := &LNode{}
fmt.Println("就地逆序")
CreateNode(head, 8)
PrintNode("逆序前:", head)
Reverse(head)
PrintNode("逆序后:", head)
}
就地逆序
逆序前:1 2 3 4 5 6 7
逆序后:7 6 5 4 3 2 1
算法性能
时间复杂度:O(n),因为需要对链表进行一次遍历,n为链表长度。
空间复杂度:O(1),因为需要常数个额外的变量来保存当前结点的前驱结点与后继结点。
补充说明:
这些定义在引入的包中
//链表定义
type LNode struct {
Data interface{}
Next *LNode
}
//创建链表
func CreateNode(node *LNode, max int) {
cur := node
for i := 1; i < max; i++ {
cur.Next = &LNode{}
cur.Next.Data = i
cur = cur.Next
}
}
next := node.Next //后继结点
for next != nil {
cur = next.Next
next.Next = pre
pre = next
next = cur
}
node.Next = pre
关于这一小段代码,下述分别逐行说明:我是真的菜晕鸭子怎么感觉又晕了
话说怎么感觉怪怪的,这个代码里next和cur是不是写反了,怎么感觉理解上有点别扭,换一下试试
cur := node.Next //当前结点从1开始
for cur != nil {
next = cur.Next //这多合理
cur.Next = pre //这我不就不晕了
pre = cur //太合理了吧
cur = next
}
node.Next = pre
吼吼,虽然本质没变,但是这样看真的顺眼一些嗨!嗨嗨嗨!
让晕鸭子不晕的代码:(好耶好耶)
package main
import (
"fmt"
. "github.com/isdamir/gotype" //引入定义的数据结构
)
func Reverse(node *LNode) {
if node == nil || node.Next == nil {
return
}
var pre *LNode //前驱结点
var next *LNode //后继结点
cur := node.Next //当前结点从1开始
for cur != nil {
next = cur.Next //这多合理
cur.Next = pre //这我不就不晕了
pre = cur //太合理了吧
cur = next
}
node.Next = pre
}
func main() {
head := &LNode{}
fmt.Println("就地逆序")
CreateNode(head, 8)
PrintNode("逆序前:", head)
Reverse(head)
PrintNode("逆序后:", head)
}
package main
import (
"fmt"
. "github.com/isdamir/gotype" //引入定义的数据结构
)
func RecursiveReverseChild(node *LNode) *LNode {
if node == nil || node.Next == nil {
return node
}
newHead := RecursiveReverseChild(node.Next)
node.Next.Next = node
node.Next = nil
return newHead
}
func RecursiveReverse(node *LNode) {
firstNode := node.Next
//递归调用
newHead := RecursiveReverseChild(firstNode)
node.Next = newHead
}
func main() {
head := &LNode{}
fmt.Println("递归法")
CreateNode(head, 8)
PrintNode("逆序前:", head)
RecursiveReverse(head)
PrintNode("逆序后:", head)
}
递归法
逆序前:1 2 3 4 5 6 7
逆序后:7 6 5 4 3 2 1
算法性能
时间复杂度:O(n),因为需要对链表进行一次遍历,n为链表长度。
优点:思路比较直观,容易理解,不需要保存前驱结点的地址;
缺点:算法实现难度较大,且由于递归需要不断调用自己,需要额外的压栈与弹栈操作,因此相比方法一性能有所下降。
晕鸭子笔记:
哎 方法二递归。。。晕死我算了。。zhu脑过载555,天知道我啥时候能学会自己写递归
按照思路我理一下,
要逆序 1->2->3->4->5->6->7 ,则要先逆 2->3->4->5->6->7 ,
要逆序 2->3->4->5->6->7 ,则要先逆 3->4->5->6->7 ,
要逆序 3->4->5->6->7,则要先逆 4->5->6->7 ,
要逆序 4->5->6->7 ,则要先逆 5->6->7 ,
要逆序 5->6->7 ,则要先逆 6->7 ,
……
麻了不懂,打出来看下
func RecursiveReverseChild(node *LNode) *LNode {
fmt.Println("node:", node)
if node == nil || node.Next == nil {
return node
}
newHead := RecursiveReverseChild(node.Next)
fmt.Println("newHead:", newHead, "node:", node)
node.Next.Next = node
node.Next = nil
return newHead
}
func RecursiveReverse(node *LNode) {
firstNode := node.Next
//递归调用
newHead := RecursiveReverseChild(firstNode)
node.Next = newHead
}
递归法
逆序前:1 2 3 4 5 6 7
node: &{1 0xc000004090}
node: &{2 0xc0000040a8}
node: &{3 0xc0000040c0}
node: &{4 0xc0000040d8}
node: &{5 0xc0000040f0}
node: &{6 0xc000004108}
node: &{7 <nil>}
newHead: &{7 <nil>} node: &{6 0xc000004108}
newHead: &{7 0xc0000040f0} node: &{5 0xc0000040f0}
newHead: &{7 0xc0000040f0} node: &{4 0xc0000040d8}
newHead: &{7 0xc0000040f0} node: &{3 0xc0000040c0}
newHead: &{7 0xc0000040f0} node: &{2 0xc0000040a8}
newHead: &{7 0xc0000040f0} node: &{1 0xc000004090}
逆序后:7 6 5 4 3 2 1
emmm,从头结点的下一个结点,也就是1开始,1~7都进了一次RecursiveReverseChild,所以返回值也有七次,最先返回的应该是最后调用的,也就是7;
这个时候因为7的指针域为空,函数直接return了没有打印newHead;
下一个返回的是6,即node为6,此时newHead为刚刚return过来的7,
那node.Next.Next是修改了7的指针域,改为指向6,(啊哈!)
node.Next是修改了6的指针域,改为指向空;
那么此时的状态是7->6了。(逆了哎)
下一个返回的是5,即node为5,此时newHead为刚刚6那边return过来的7,
那node.Next.Next是修改了6的指针域,改为指向5,
node.Next是修改了5的指针域,改为指向空;
那么此时的状态是7->6->5了。(!)
下一个返回的是4,即node为4,此时newHead为刚刚5那边return过来的7,
那node.Next.Next是修改了5的指针域,改为指向4,
node.Next是修改了4的指针域,改为指向空;
那么此时的状态是7->6->5->4了。
我不知道我在干嘛我好像在瞎说八道但是我好像不晕了?
……
那最后返回的是1,即node为1,此时newHead为刚刚2那边return过来的7,
那node.Next.Next是修改了2的指针域,改为指向1,
node.Next是修改了1的指针域,改为指向空;
那么此时的状态是7->6->5->4->3->2->1了。
在RecursiveReverse中node.Next = newHead再最后修改头指针的指针域,即 head->7->6->5->4->3->2->1 了!
麻了,我看懂了,但是这让我自己写出来的话我感觉还是不会啊55555
思路:
从链表的第二个结点开始,将遍历到的结点插入到头结点的后面,直到遍历结束。
原链表为 head->1->2->3->4->5->6->7 时,
在遍历到2时,将其插入到头结点后,链表变为 head->2->1->3->4->5->6->7 ;之后同理。
代码实现:
package main
import (
"fmt"
. "github.com/isdamir/gotype" //引入定义的数据结构
)
func InsertReverse(node *LNode) {
if node == nil || node.Next == nil {
return
}
var cur *LNode //当前结点
var next *LNode //后继结点
cur = node.Next.Next
node.Next.Next = nil //设置链表第一个结点为尾结点
//把遍历到的结点插入到头结点的后面
for cur != nil {
next = cur.Next
cur.Next = node.Next
node.Next = cur
cur = next
}
}
func main() {
head := &LNode{}
fmt.Println("插入法")
CreateNode(head, 8)
PrintNode("逆序前:", head)
InsertReverse(head)
PrintNode("逆序后:", head)
}
插入法
逆序前:1 2 3 4 5 6 7
逆序后:7 6 5 4 3 2 1
提示:方法二已经实现了递归的方法
package main
import (
"fmt"
//. "github.com/isdamir/gotype" //引入定义的数据结构
)
//LNode 链表定义
type LNode struct {
Data interface{}
Next *LNode
}
//CreateNode 创建不带头结点的单链表
func CreateNode(node *LNode, max int) {
cur := node
cur.Data = 1
for i := 2; i < max; i++ {
cur.Next = &LNode{}
cur.Next.Data = i
//fmt.Println("cur:", cur)
cur = cur.Next
}
}
//PrintNode 打印不带头结点的单链表
func PrintNode(node *LNode) {
for cur := node; cur != nil; cur = cur.Next {
//fmt.Print(cur.Data, " ")
fmt.Println("cur_node:", cur)
}
fmt.Println()
}
func main() {
head := &LNode{}
CreateNode(head, 8)
PrintNode(head)
}
运行结果:创建的不带头结点的单链表
cur_node: &{1 0xc000096078}
cur_node: &{2 0xc000096090}
cur_node: &{3 0xc0000960a8}
cur_node: &{4 0xc0000960c0}
cur_node: &{5 0xc0000960d8}
cur_node: &{6 0xc0000960f0}
cur_node: &{7 <nil>}
这里可以直观的看下有无头结点的区别,下面是之前有头结点的单链表:
cur_node: &{<nil> 0xc000004090}
cur_node: &{1 0xc0000040c0}
cur_node: &{2 0xc0000040f0}
cur_node: &{3 0xc000004120}
cur_node: &{4 0xc000004150}
cur_node: &{5 0xc000004180}
cur_node: &{6 0xc0000041b0}
cur_node: &{7 <nil>}
得到了不带头结点的单链表后,现在要实现将它逆序,用它提示的方法二递归法
emm试了下,没有头结点之后递归完head指针还是指的1,但是1结点的指针域已经为空了……
要打印输出的话得是从结点7开始才得行……怎么改一下我
package main
import (
"fmt"
//. "github.com/isdamir/gotype" //引入定义的数据结构
)
//LNode 链表定义
type LNode struct {
Data interface{}
Next *LNode
}
//CreateNode 创建不带头结点的单链表
func CreateNode(node *LNode, max int) {
cur := node
cur.Data = 1
for i := 2; i < max; i++ {
cur.Next = &LNode{}
cur.Next.Data = i
cur = cur.Next
}
}
//PrintNode 打印不带头结点的单链表
func PrintNode(info string, node *LNode) {
fmt.Print(info)
for cur := node; cur != nil; cur = cur.Next {
fmt.Print(cur.Data, " ")
//fmt.Println("cur_node:", cur)
}
fmt.Println()
}
func RecursiveReverseChild(node *LNode) *LNode {
if node == nil || node.Next == nil {
return node
}
newHead := RecursiveReverseChild(node.Next)
node.Next.Next = node
node.Next = nil
return newHead
}
func main() {
head := &LNode{}
CreateNode(head, 8)
fmt.Println("对不带头结点的单链表进行逆序-递归法")
PrintNode("逆序前:", head)
newHead := RecursiveReverseChild(head)
PrintNode("逆序后:", newHead)
}
对不带头结点的单链表进行逆序-递归法
逆序前:1 2 3 4 5 6 7
逆序后:7 6 5 4 3 2 1
呜呜呜,好耶,终于好了,在创建不带头结点的单链表那里卡了很久,老笨蛋了555;
最后直接先把第一个结点数据域赋值,循环里的代码就不用动了,最开始把 cur.Next.Data = i 改为了 cur.Data = i,然后引发了一系列晕鸭子反应。。。
package main
import (
"fmt"
. "github.com/isdamir/gotype" //引入定义的数据结构
)
//ReversePrint 从尾到头输出链表-递归
func ReversePrint(node *LNode) {
if node == nil {
return
}
ReversePrint(node.Next)
fmt.Print(node.Data, " ")
}
func main() {
head := &LNode{}
fmt.Println("从尾到头输出链表")
CreateNode(head, 8)
PrintNode("顺序输出:", head)
fmt.Print("逆序输出:")
ReversePrint(head.Next)
}
从尾到头输出链表
顺序输出:1 2 3 4 5 6 7
逆序输出:7 6 5 4 3 2 1
我麻了三天就刚看完第一章第一小节我。。。以后不知道哪个大冤种公司会收我。。。
package main
import (
"fmt"
. "github.com/isdamir/gotype" //引入定义的数据结构
)
//RemoveDup 顺序删除
func RemoveDup(head *LNode) {
if head == nil || head.Next == nil {
return
}
outerCur := head.Next //用于外层循环,指向链表第一个结点
var innerCur *LNode //用于内层循环遍历outerCur后面的结点
var innerPre *LNode //innerCur的前驱结点
for ; outerCur != nil; outerCur = outerCur.Next {
for innerCur, innerPre = outerCur.Next, outerCur; innerCur != nil; innerPre, innerCur = innerCur, innerCur.Next {
if innerCur.Data == outerCur.Data {
innerCur = innerCur.Next
innerPre.Next = innerCur
}
}
}
}
func main() {
head := &LNode{}
fmt.Println("删除重复结点-顺序删除")
CreateNodeT(head)
PrintNode("删除重复结点前:", head)
RemoveDup(head)
PrintNode("删除重复结点后:", head)
}
func CreateNodeT(node *LNode) {
CreateNode(node, 7)
node.Next.Next.Data = 3
node.Next.Next.Next.Data = 1
node.Next.Next.Next.Next.Data = 5
node.Next.Next.Next.Next.Next.Next.Data = 7
}
删除重复结点-顺序删除
删除重复结点前:1 3 1 5 5 7
删除重复结点后:1 3 5 7
算法性能
时间复杂度:O(n^2) ,因为采用双重循环对链表进行遍历,n为链表长度。
空间复杂度:O(1),使用了常量个额外的指针变量。
晕鸭子笔记
这个代码跟给的不太一样,因为引入的包里没有CreateNodeT,不造CreateNodeT是个啥啊,就自己乱整了。。
内层循环按书上写else的话就不会拖那么长,但是因为晕鸭子是直肠子所以就这样写了
反正样例过了嘛嘻嘻 (还能指望晕鸭子写什么高级代码呢,不晕就万幸了)
package main
import (
"fmt"
. "github.com/isdamir/gotype" //引入定义的数据结构
)
//removeDupRecursionChild 递归法
func removeDupRecursionChild(head *LNode) *LNode {
if head == nil || head.Next == nil {
return head
}
var pointer *LNode
cur := head
//对以head.Next为首的子链表删除重复的结点
head.Next = removeDupRecursionChild(head.Next)
pointer = head.Next
//找出以head.Next为首的子链表中与head结点相同的结点并删除
for pointer != nil {
if head.Data == pointer.Data {
cur.Next = pointer.Next
pointer = cur.Next
} else {
pointer = pointer.Next
cur = cur.Next
}
}
return head
}
func RemoveDupRecursion(head *LNode) {
if head == nil {
return
}
head.Next = removeDupRecursionChild(head.Next)
}
func main() {
head := &LNode{}
fmt.Println("删除重复结点-递归法")
CreateNodeT(head)
PrintNode("删除重复结点前:", head)
removeDupRecursionChild(head)
PrintNode("删除重复结点后:", head)
}
func CreateNodeT(node *LNode) {
CreateNode(node, 10)
node.Next.Next.Data = 3
node.Next.Next.Next.Data = 1
node.Next.Next.Next.Next.Data = 5
node.Next.Next.Next.Next.Next.Next.Data = 7
}
删除重复结点-递归法
删除重复结点前:1 3 1 5 5 7 7 8 9
删除重复结点后:1 3 5 7 8 9
算法性能
时间复杂度:O(n^2) ,该方法与方法一类似,本质上需要对链表进行双重遍历,n为链表长度。
由于递归法会增加许多额外的函数调用,因此,理论上该方法效率比方法一低。
晕鸭子笔记
额我怎么好像……怎么每次感觉递归调用那句都有点懵 head.Next = removeDupRecursionChild(head.Next)
呃也还是打出来看看吧
//removeDupRecursionChild 递归法
func removeDupRecursionChild(head *LNode) *LNode {
fmt.Println("head:", head)
if head == nil || head.Next == nil {
return head
}
var pointer *LNode
cur := head
head.Next = removeDupRecursionChild(head.Next) //对以head.Next为首的子链表删除重复的结点
pointer = head.Next
fmt.Println("head:", head, "pointer:", pointer)
//找出以head.Next为首的子链表中与head结点相同的结点并删除
for pointer != nil {
if head.Data == pointer.Data {
cur.Next = pointer.Next
pointer = cur.Next
} else {
pointer = pointer.Next
cur = cur.Next
}
}
return head
}
删除重复结点-递归法
删除重复结点前:1 3 1 5 5 7
head: &{<nil> 0xc000004090}
head: &{1 0xc0000040a8}
head: &{3 0xc0000040c0}
head: &{1 0xc0000040d8}
head: &{5 0xc0000040f0}
head: &{5 0xc000004108}
head: &{7 <nil>}
head: &{5 0xc000004108} pointer: &{7 <nil>}
head: &{5 0xc0000040f0} pointer: &{5 0xc000004108}
head: &{1 0xc0000040d8} pointer: &{5 0xc000004108}
head: &{3 0xc0000040c0} pointer: &{1 0xc0000040d8}
head: &{1 0xc0000040a8} pointer: &{3 0xc0000040c0}
head: &{<nil> 0xc000004090} pointer: &{1 0xc0000040a8}
删除重复结点后:1 3 5 7
对不起我又开始最笨的手推了
还是先从头指针head进了七次递归的函数,没猫饼,然后倒着返回
最先返回的是最后一次调用,即结点7,由于head.Next为nil所以直接return了当前head给上一次调用的head.Next
倒数第二次的head是5因此head.Next = removeDupRecursionChild(head.Next)这个相当于没有变还是5->7,
此时head指向5,pointer指向7,判断这两个数据域是否相同,由于不相同所以cur和pointer一起后移,由于7已经是最后一个结点,pointer再后移会为空,因此结束for循环,return了指向倒数第二个5的head;
倒数第三个数还是5,此时head指向5(倒数第三个数),pointer指向5(倒数第二个),判断这两个数据域是否相同,由于相同,此时cur指向与head相同,修改cur所指结点的指针域让它指向pointer的下一个结点(即结点7),pointer再后移一个,因此,此时倒数第二个结点5已经被从链表中删除了;
至此,cur指向5,pointer指向7,由于7已经是最后一个结点,pointer再后移会为空,因此结束for循环,return了指向5的head;
倒数第四个数为1,此时head指向1,pointer指向5,判断这两个数据域是否相同,由于不相同所以cur和pointer一起后移,此时cur指向5,pointer指向7;
由于pointer不为空,因此循环继续判断head与pointer所指数据域是否相同,由于不相同所以cur和pointer一起后移,由于7已经是最后一个结点,pointer再后移会为空,因此结束for循环,return了指向1的head;
倒数第五个数为3,此时head指向3,pointer指向1,判断这两个数据域是否相同,由于不相同所以cur和pointer一起后移,此时cur指向1,pointer指向5;
由于pointer不为空,因此循环继续判断head与pointer所指数据域是否相同,由于不相同所以cur和pointer一起后移,此时cur指向5,pointer指向7;
由于7已经是最后一个结点,pointer再后移会为空,因此结束for循环,return了指向3的head;
倒数第六个数为1,此时head指向1,pointer指向3,判断这两个数据域是否相同,由于不相同所以cur和pointer一起后移,此时cur指向3,pointer指向1;
由于pointer不为空,因此循环继续判断head与pointer所指数据域是否相同,由于相同,修改cur所指结点(3)的指针域让它指向pointer的下一个结点(即结点5),pointer再后移一个,因此,此时原本在倒数第四个的结点1已经被从链表中删除了;
至此,cur指向3,pointer指向5;
由于pointer不为空,因此循环继续判断head与pointer所指数据域是否相同,由于1和5不相同所以cur和pointer一起后移,此时cur指向5,pointer指向7;
由于7已经是最后一个结点,pointer再后移会为空,因此结束for循环,return了指向1的head;
此时重复的已经删完了,只剩一个头结点;头结点也要走一遍。
emmmm怎么感觉跟方法一相比好像差不多,就是倒过来了呢?
package main
import (
"fmt"
. "github.com/isdamir/gotype" //引入定义的数据结构
)
func removeDuplicateNodes(head *LNode) *LNode {
if head == nil {
return head
}
hashSet := make(map[interface{}]bool)
cur := head
for cur.Next != nil {
if hashSet[cur.Next.Data] {
cur.Next = cur.Next.Next
} else {
hashSet[cur.Next.Data] = true
cur = cur.Next
}
//cur = cur.Next 一开始放这里不对
}
return head
}
func main() {
head := &LNode{}
fmt.Println("删除重复结点-空间换时间")
CreateNodeT(head)
PrintNode("删除重复结点前:", head)
removeDuplicateNodes(head)
PrintNode("删除重复结点后:", head)
}
func CreateNodeT(node *LNode) {
CreateNode(node, 7)
node.Next.Next.Data = 3
node.Next.Next.Next.Data = 1
node.Next.Next.Next.Next.Data = 5
node.Next.Next.Next.Next.Next.Next.Data = 7
}
删除重复结点-空间换时间
删除重复结点前:1 3 1 5 5 7
删除重复结点后:1 3 5 7
有序链表:链表中的各个结点按照结点数据域中的数据递增/递减有序连接。
上述介绍的从无序链表中移除重复项的方法,也适用于链表有序的情况,但是以上方法没有充分利用到链表有序这个条件,因此,算法的性能肯定不是最优的。
对于有序链表,由于链表具有有序性,因此,不需要对链表进行两次遍历。(如112334,543321,重复项只会挨在一起)
思路:
用cur指向链表第一个结点,此时需要分为以下两种情况讨论:
如果 cur.Data == cur.Next.Data,那么删除cur.Next结点;
如果 cur.Data != cur.Next.Data,那么cur=cur.Next,继续遍历其余结点。
代码实现
package main
import (
"fmt"
. "github.com/isdamir/gotype" //引入定义的数据结构
)
func RemoveDup(head *LNode) {
if head == nil {
return
}
cur := head.Next //cur := head
for cur.Next != nil {
if cur.Data == cur.Next.Data {
cur.Next = cur.Next.Next
} else {
cur = cur.Next
}
}
}
func main() {
head := &LNode{}
fmt.Println("删除有序链表的重复结点-顺序删除")
CreateNodeT(head)
PrintNode("删除重复结点前:", head)
RemoveDup(head)
PrintNode("删除重复结点后:", head)
}
func CreateNodeT(node *LNode) {
CreateNode(node, 7)
node.Next.Next.Data = 1
node.Next.Next.Next.Data = 2
node.Next.Next.Next.Next.Data = 3
node.Next.Next.Next.Next.Next.Data = 3
node.Next.Next.Next.Next.Next.Next.Data = 4
}
删除有序链表的重复结点-顺序删除
删除重复结点前:1 1 2 3 3 4
删除重复结点后:1 2 3 4
思路
对链表中的结点直接进行相加操作,把相加的和存储到新的链表中对应的结点中,同时还要记录结点相加后的进位。
该方法需要注意的问题:
代码实现
package main
import (
. "github.com/isdamir/gotype" //引入定义的数据结构
)
func Add(h1 *LNode, h2 *LNode) *LNode {
if h1 == nil || h1.Next == nil {
return h2
}
if h2 == nil || h2.Next == nil {
return h1
}
c := 0 //记录进位
sum := 0 //记录两个结点相加的值
p1 := h1.Next //遍历h1
p2 := h2.Next //遍历h2
resultHead := &LNode{} //相加后链表头结点
p := resultHead //指向链表resultHead最后一个结点
for p1 != nil && p2 != nil {
p.Next = &LNode{} //指向新创建的存储相加和的结点
sum = p1.Data.(int) + p2.Data.(int) + c
p.Next.Data = sum % 10
c = sum / 10 //进位
p = p.Next
p1 = p1.Next
p2 = p2.Next
}
//链表h2比h1长,接下来只需要考虑h2剩余结点的值
if p1 == nil {
for p2 != nil {
p.Next = &LNode{} //指向新创建的存储相加和的结点
sum = p2.Data.(int) + c
p.Next.Data = sum % 10 //两结点相加和
c = sum / 10 //进位
p = p.Next
p2 = p2.Next
}
}
//链表h1比h2长,接下来只需要考虑h1剩余结点的值
if p2 == nil {
for p1 != nil {
p.Next = &LNode{} //指向新创建的存储相加和的结点
sum = p1.Data.(int) + c
p.Next.Data = sum % 10 //两结点相加和
c = sum / 10 //进位
p = p.Next
p1 = p1.Next
}
}
if c == 1 {
p.Next = &LNode{}
p.Next.Data = 1
}
return resultHead
}
//CreateNodeT 创建链表
func CreateNodeT(l1 *LNode, l2 *LNode) {
cur := l1
for i := 1; i < 7; i++ {
cur.Next = &LNode{}
cur.Next.Data = i + 2
cur = cur.Next
}
cur = l2
for i := 9; i > 4; i-- {
cur.Next = &LNode{}
cur.Next.Data = i
cur = cur.Next
}
}
func main() {
head1 := &LNode{}
head2 := &LNode{}
CreateNodeT(head1, head2)
PrintNode("Head1:", head1)
PrintNode("Head2:", head2)
PrintNode("相加后:", Add(head1, head2))
}
Head1:3 4 5 6 7 8
Head2:9 8 7 6 5
相加后:2 3 3 3 3 9
算法性能
时间复杂度:O(n) ,因为需要对两个链表都进行遍历,n为较长的链表的长度。
空间复杂度:O(n),因为计算结果保存在一个新的链表中。
晕鸭子笔记
微信读书的想法中有网友给出了另一思路,
书中的这个是根据哪个链表更长后续又分开写了两个,这个代码是将先结束的链表的加数赋值为0。
代码实现:
package main
import (
. "github.com/isdamir/gotype" //引入定义的数据结构
)
func Add(h1 *LNode, h2 *LNode) *LNode {
resultHead := &LNode{} //保存相加结果的链表的头结点
p := resultHead //遍历保存结果的链表
var d1, d2, sum, c int
p1 := h1.Next //遍历h1
p2 := h2.Next //遍历h2
for p1 != nil || p2 != nil {
if p1 == nil {
d1 = 0
} else {
d1 = p1.Data.(int)
}
if p2 == nil {
d2 = 0
} else {
d2 = p2.Data.(int)
}
sum = d1 + d2 + c
c = sum / 10 //进位
p.Next = &LNode{}
p.Next.Data = sum % 10
p = p.Next
if p1 != nil {
p1 = p1.Next
}
if p2 != nil {
p2 = p2.Next
}
}
if c == 1 {
p.Next = &LNode{}
p.Next.Data = 1
}
return resultHead
}
//CreateNodeT 创建链表
func CreateNodeT(l1 *LNode, l2 *LNode) {
cur := l1
for i := 1; i < 7; i++ {
cur.Next = &LNode{}
cur.Next.Data = i + 2
cur = cur.Next
}
cur = l2
for i := 9; i > 4; i-- {
cur.Next = &LNode{}
cur.Next.Data = i
cur = cur.Next
}
}
func main() {
head1 := &LNode{}
head2 := &LNode{}
CreateNodeT(head1, head2)
PrintNode("Head1:", head1)
PrintNode("Head2:", head2)
PrintNode("相加后:", Add(head1, head2))
}
Head1:3 4 5 6 7 8
Head2:9 8 7 6 5
相加后:2 3 3 3 3 9
题目描述
给定链表 L0->L1->L2…Ln-1->Ln,将链表重新排序为 L0->Ln->L1->Ln-1->L2->Ln-2…
要求:①在原来链表的基础上进行排序,即不能申请新的结点; ②只能修改结点的next域,不能修改数据域。
思路
代码实现
package main
import (
. "github.com/isdamir/gotype" //引入定义的数据结构
)
//findMiddleNode 找出链表的中心结点,把链表从中间断成两个子链表
func findMiddleNode(head *LNode) *LNode {
if head == nil || head.Next == nil {
return head
}
fast := head //遍历链表的每次向前走两步
slow := head //遍历链表的每次向前走一步
slowPre := head
for fast != nil && fast.Next != nil {
slowPre = slow
slow = slow.Next
fast = fast.Next.Next
}
slowPre.Next = nil
return slow
}
//reverse 逆序不带头结点的单链表
func reverse(head *LNode) *LNode {
if head == nil || head.Next == nil {
return head
}
var pre *LNode
var next *LNode
for head != nil {
next = head.Next
head.Next = pre
pre = head
head = next
}
return pre
}
func Recorder(head *LNode) {
if head == nil || head.Next == nil {
return
}
cur1 := head.Next //前半部分的链表第一个结点
mid := findMiddleNode(head.Next)
cur2 := reverse(mid) //后半部分链表逆序后的第一个结点
var tmp *LNode
//合并链表
for cur1.Next != nil {
tmp = cur1.Next
cur1.Next = cur2
cur1 = tmp
tmp = cur2.Next
cur2.Next = cur1
cur2 = tmp
}
cur1.Next = cur2
}
func main() {
head := &LNode{}
CreateNode(head, 8)
PrintNode("排序前:", head)
Recorder(head)
PrintNode("排序后:", head)
}
排序前:1 2 3 4 5 6 7
排序后:1 7 2 6 3 5 4