链表

链表中的数据是以结点来表示的,每个结点的构成:元素(数据元素的映象) + 指针(指示后继元素存储位置),元素就是存储数据的存储单元,指针就是连接每个结点的地址数据

image.png

  1. package main
  2. import (
  3. "errors"
  4. "fmt"
  5. )
  6. type Node struct {
  7. Value int
  8. Next *Node
  9. }
  10. //头节点
  11. type List struct{
  12. Head *Node
  13. }
  14. //是否为空
  15. func (this *List) IsEmpty() bool {
  16. if this.Head == nil{
  17. return true
  18. }
  19. return false
  20. }
  21. //是否最后一个节点
  22. func (this *List) IsLast() bool {
  23. if this.Head.Next == nil{
  24. return true
  25. }
  26. return false
  27. }
  28. // 长度
  29. func (this *List) Len() int{
  30. if this.IsEmpty(){
  31. return 0
  32. }
  33. length := 1
  34. current := this.Head
  35. for current.Next != nil {
  36. length ++
  37. current = current.Next
  38. }
  39. return length
  40. }
  41. //首部插入
  42. func (this *List) Add(v int) {
  43. Head := &Node{Value:v}
  44. Head.Next = this.Head //将新创建的Head的next指向当前Head
  45. this.Head = Head // 将当前Head更新为新创建的Head
  46. }
  47. //尾部插入
  48. func (this *List) Append(v int) {
  49. Head := &Node{Value:v}
  50. if this.IsEmpty(){
  51. // 如果当前链表为空,直接插入首部
  52. this.Add(v)
  53. }else {
  54. last := this.Head
  55. // 遍历到最后一个节点
  56. for last.Next != nil{
  57. last = last.Next
  58. }
  59. last.Next = Head
  60. }
  61. }
  62. // 插入
  63. func (this *List) Insert(index,v int) {
  64. if index<=0{
  65. this.Add(v)
  66. }else if index >= this.Len(){
  67. this.Append(v)
  68. }else {
  69. previous := this.Head
  70. for i:=0;i<index-1;i++{
  71. previous = previous.Next
  72. }
  73. current := previous.Next //index节处现有的Head点
  74. Head := &Node{Value:v}
  75. Head.Next = current //插入的节点的next指向index处现有的Head节点
  76. previous.Next = Head // index处现有Head节点的上一个Head的next指向插入的Head
  77. }
  78. }
  79. // 删除
  80. func (this *List) DelByIndex(index int) error{
  81. current := this.Head
  82. if index <= 0 { //判断删除的是否是首节点
  83. this.Head = current.Next
  84. }else if index>= this.Len(){
  85. return errors.New("index out of range")
  86. }else {
  87. for i:=0;i<index-1;i++{
  88. current = current.Next
  89. }
  90. //将需要被删除的节点的上一个节点的next指向->被删除节点的next指向的Head
  91. current.Next = current.Next.Next
  92. }
  93. return nil
  94. }
  95. // 删除
  96. func (this *List) DelByValue(value int){
  97. current := this.Head
  98. if value == current.Value{ //判断删除的是否是首节点
  99. this.Head = current.Next
  100. }else {
  101. for current.Next != nil {
  102. if current.Next.Value == value{
  103. current.Next = current.Next.Next
  104. }else {
  105. current = current.Next
  106. }
  107. }
  108. }
  109. }
  110. // 查找
  111. func (this *List) Find(index int) (int,error){
  112. current := this.Head
  113. if index <= 0 {
  114. return current.Value,nil
  115. }else if index >= this.Len(){
  116. return 0,errors.New("index out of range")
  117. }
  118. for i:=0;i<index;i++{
  119. current = current.Next
  120. }
  121. return current.Value,nil
  122. }
  123. // 遍历
  124. func (this *List) ForEach(){
  125. current := this.Head
  126. for {
  127. fmt.Println(current.Value)
  128. if current.Next == nil{
  129. break
  130. }
  131. current = current.Next
  132. }
  133. }
  134. func main() {
  135. list := & List{}
  136. list.Append(1)
  137. list.Append(2)
  138. list.Append(3)
  139. list.ForEach()
  140. list.Add(0)
  141. list.ForEach()
  142. list.DelByIndex(3)
  143. list.ForEach()
  144. list.DelByValue(2)
  145. list.ForEach()
  146. v,_ := list.Find(1)
  147. fmt.Println("index=0;value=",v)
  148. }

链表反转

这里引发一个关于指针的问题
两个指针类型互指,结果会如何?

func main() {
    var a *int              // a为nil
    var b = new (int)       // b为0xc00000a110
    var c = new (int)       // c为0xc00000a118
    a = b                     // 此时a为0xc00000a110
    *b = 2                  // 将地址0xc00000a110 指向值为2的一块内存
    b = c                   // 此时b为0xc00000a118,注意这里不会改变a
    *b = 1                  // 将地址0xc00000a118 指向值为1的一块内存
    fmt.Println(*a,*b,*c)   // 2 1 1
}

链表反转的思想:

位置调换次数 pre temp whole
0 nil 1->2->3->4->5 1->2->3->4->5
1 1->nil 2->-3>->4->5 2->3->4->5->1->nil
2 2->1->nil 3->4->5 3->4->5->2->1->nil
3 3->2->1->nil 4->5 4->5->3->2->1->nil
4 4->3->2->1->nil 5 5->4->3->2->1->nil

可以看出来

  • pre是cur的最前面那位(pre = cur)
  • cur就是当前位的后面链表元素(cur = cur.Next)
  • cur.Next肯定是接pre(cur.Next = pre)
    // 单链反转
    func (this *List) Reversr(){
      Head := this.node  // 头节点
      var pre *Node // 定一个空node
      var temp *Node //临时node
      for current != nil{
          temp = current.Next  //让临时node保存头得下一个节点
          current.Next = pre   //将头节点指向前一个节点
          pre = current        //更新前一个节点
          current = temp       //更新头节点
      }
      this.Head = pre
    }
    

判断链表有无环

对于这个问题我们可以采用“快慢指针”的方法。就是有两个指针fast和slow,开始的时候两个指针都指向链表头head,然后在每一步

操作中slow向前走一步即:slow = slow->next,而fast每一步向前两步即:fast = fast->next->next。

由于fast要比slow移动的快,如果有环,fast一定会先进入环,而slow后进入环。当两个指针都进入环之后,经过一定步的操作之后

二者一定能够在环上相遇,并且此时slow还没有绕环一圈,也就是说一定是在slow走完第一圈之前相遇。证明可以看下图:
image.png

func (this *List) HasLoop() bool {
    var (
        res        bool
        slow, fast = this.Head, this.Head
    )

    for slow != nil && fast.Next != nil {
        slow = slow.Next
        fast = fast.Next
        if slow == fast {
            res = true
            break
        }
    }

    return res
}

删除倒数第n个节点

快慢指针解法

func removeNthFromEnd(head *ListNode, n int) *ListNode {
    dummy := &ListNode{Next: head} // 辅助头节点,它的好处是处理边界问题
    slow, fast := dummy, dummy

    // fast先走n步
    for n > 0 && fast != nil {
        fast = fast.Next
        n--
    }

    // 当fast走到最后一个有效节点,那么slow就走到了待删除节点的上一个节点
    for fast.Next != nil {
        fast = fast.Next
        slow = slow.Next
    }

    slow.Next = slow.Next.Next

    return dummy.Next
}

结构优化

做任何事情至少都会有两个步骤,实现与优化

type Node struct { 
    Value int
    Next *Node
}

type List struct{
    // 1.很显然这种结构体在并发中是不安全的,可以加读写锁
    // mutex *sync.RWMutex
    // 2.如果经常有判断长度的操作,可以加个size字段,将时间复杂度从O(n)变为O(1)
    // size uint
    Head *Node
}