golang剑指offer-005.从尾到头打印链表

题目描述

输入一个链表,从尾到头打印链表每个节点的值。

输入描述

链表表头

输出描述

需要打印的链表的表头

解题思路

这种题目一般情况下会使用反转链表的方法解决。
对于单链表来说,其逆序方法和普通的切片/数组逆序方法是有一定区别的,在逆序过程中有一个很重要的点:在修改节点指针域的时候,一定要记录下后继节点的地址,否则将丢失后继节点。
关于反转链表,一般有三种解决方法。

就地逆序

主要思路是:在遍历链表时,修改当前节点指针域的指向,让其指向它的前驱节点。这样,如果需要修改一个节点的后继指针,应涉及到三个点:前驱,当前,后继三个节点。

// 我们考虑链表带有头结点的情况
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 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 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 ReversePrint(node *LNode) {
	if node == nil {
		return
	}
	ReversePrint(node.Next)
	fmt.Println(node.Data, " ")
}

这里要注意,在剑指Offer中为我们在面试中提出了如下小提示:

在面试时候,如果我们打算修改输入的数据,最好先问问面试官是不是允许修改

如果要求修改,那么头插法最合适不过了,但如果不让修改的话,直接递归输出即可。

发布了16 篇原创文章 · 获赞 0 · 访问量 1306

猜你喜欢

转载自blog.csdn.net/weixin_41036574/article/details/103885568