当前位置:   article > 正文

链表系列之经典问题_链表的参考文献

链表的参考文献

前文介绍了一些链表的基础知识,不熟悉的同学可以去看看,有了知识后,没有操作怎么行,所以今天给大家分析几个链表的经典问题,面试也是经常出现,废话不多说,我们一个个讲,重点补充一下,以下例子都是有链表头的,并都是用Golang

一、链表倒置

这里介绍两种方法,头插入法和递归发。

1、head插入发

基本思想就是从头到尾遍历列表,每遍历一个节点,都插入到head后,直到链尾。看图:

代码:

  1. type ListNode struct {
  2. value interface{}
  3. next *ListNode
  4. }
  5. type SingleLinkList struct {
  6. head *ListNode
  7. len uint
  8. }
  9. // 含链表头
  10. func (s *SingleLinkList) Reverse() bool {
  11. if s.head.next == nil {
  12. return true
  13. }
  14. cur := s.head.next
  15. p := cur.next
  16. cur.next = nil
  17. s.head.next = cur
  18. for nil != p {
  19. tmp := p.next
  20. tmp1 := s.head.next
  21. s.head.next = p
  22. p.next = tmp1
  23. p = tmp
  24. }
  25. return true
  26. }

2、递归法

既然可以使用递归法,那我们根据递归法的条件来分析这个题目。

1、是否存在终止条件

如果链表无节点或者只有一个节点,则无需反转,直接返回当前节点

  1. if head == nil || head.next == nil {
  2. return head
  3. }

2、是否能分为多个规模更小的子问题,并且子问题的解法一致

有一个链表: A->B->C, 将B->C看成一个已经反转后的整体K, 即A->K,那么问题就变为 A 和 K之间的反转,代码如下:

  1. A = head.next
  2. A.next = K
  3. head.next = nil

所以问题就转化成B->C之间的反转,这也是两个节点的反转,与A->K的解法是相同的,而且从三个节点的问题 转化为两个节点问题,规模更小了。所以我们可以总结出递归公式如下:

  1. head.next.next = head
  2. head.next = nil

通过以上分析,代码如下:

  1. func Reverse2(head *ListNode) *ListNode {
  2. if head == nil || head.next == nil {
  3. return head
  4. }
  5. newHead := Reverse2(head.next)
  6. head.next.next = head
  7. head.next = nil
  8. return newHead
  9. }

二、链表中环的检测

环的检测很简单,我们设计两个指针,一个快指针,一个慢指针,如果链表中有环,那么快慢指针肯定会相遇,反之则无环。另外注意链尾的判断,如果有遇到,则表示没有环。

代码:

  1. type ListNode struct {
  2. value interface{}
  3. next *ListNode
  4. }
  5. type SingleLinkList struct {
  6. head *ListNode
  7. len uint
  8. }
  9. func (s *SingleLinkList) HasLoop() bool {
  10. if s.head == nil || s.head.next == nil {
  11. return false
  12. }
  13. pFast := s.head.next
  14. pSlow := s.head.next
  15. for pFast.next != nil {
  16. pFast = pFast.next.next
  17. if pFast == nil {
  18. break
  19. }
  20. pSlow = pSlow.next
  21. if pSlow == nil {
  22. break
  23. }
  24. if pSlow == pFast {
  25. return true
  26. }
  27. }
  28. return false
  29. }

三、两个有序的链表合并

1. 非递归法

从图可得,合并的基本思想是,遍历链表,并一一比较节点值,哪个小,指针就指向哪个,直到某个链表达到链尾。

代码:

  1. type ListNode struct {
  2. value interface{}
  3. next *ListNode
  4. }
  5. type SingleLinkList struct {
  6. head *ListNode
  7. len uint
  8. }
  9. func MergeTwoLinkList(l1 *SingleLinkList, l2 *SingleLinkList) *SingleLinkList {
  10. if l1.head == nil || l1.head.next == nil {
  11. return l2
  12. }
  13. if l2.head == nil || l2.head.next == nil {
  14. return l1
  15. }
  16. cur1 := l1.head.next
  17. cur2 := l2.head.next
  18. result := NewSingleLink()
  19. n := result.head
  20. for cur1 != nil && cur2 != nil {
  21. if cur1.value.(int) <= cur2.value.(int) {
  22. n.next = cur1
  23. cur1 = cur1.next
  24. } else {
  25. n.next = cur2
  26. cur2 = cur2.next
  27. }
  28. result.len++
  29. n = n.next
  30. }
  31. if cur1 == nil {
  32. n.next = cur2
  33. }
  34. if cur2 == nil {
  35. n.next = cur1
  36. }
  37. return result
  38. }

 2、递归法

我们还是按照递归的条件来对问题进行分析:

1、是否有终止条件

如果两个合并链表都无节点,则不需要合并,任意返回一个即可。如果一个链表是空节点,则直接返回另外一个,终止条件如下:

  1. if n1 == nil {
  2. return n2
  3. }
  4. if n2 == nil {
  5. return n1
  6. }

2、是否能分为多个规模更小的子问题,并且子问题的解法一致

我们假设两个链表只有一个节点,那么问题就简化为,比较两个节点的大小,小的先连,大得后连,那么扩展到N个节点,我们是否也可以将问题简化到只比较两个节点问题呢? 当然可以,我们可以假设除了两个节点之外,其他节点已经合并,这样就问题的解法是一致。问题化解过程,请具体看图:

从图中可以看到,问题最后就是值为4的两个节点在比较。由此可以得出递推公式:

  1. if n1.value.(int) <= n2.value.(int) {
  2. return n1
  3. } else {
  4. return n2
  5. }

所以根据终止条件和递推公式,代码如下:

  1. func MergeTwoLinkList2(n1 *ListNode, n2 *ListNode) *ListNode {
  2. if n1 == nil {
  3. return n2
  4. }
  5. if n2 == nil {
  6. return n1
  7. }
  8. if n1.value.(int) <= n2.value.(int) {
  9. n1.next = MergeTwoLinkList2(n1.next, n2)
  10. return n1
  11. } else {
  12. n2.next = MergeTwoLinkList2(n1, n2.next)
  13. return n2
  14. }
  15. }

四、总结

最后还是强调,写链表还是得多写,写多了自然而然就熟悉了。以上三个例子,抛砖引玉,进一步提升大家可以去leetcode上练习。另外此文也讲了一些递归算法的分析方法,具有一定的普世性,大家可以参考分析,以免落入脑容量不够的境地。最后留个作业:如果去掉链表头,如何写上面上个例子。

 

五、参考文献

1. Finding the Start of a Loop in a Circular Linked List

2. https://en.wikipedia.org/wiki/Linked_list

欢迎关注公众号:

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/weixin_40725706/article/detail/64525
推荐阅读
相关标签
  

闽ICP备14008679号