数据结构--环形链表

Posted Yuan_sr

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构--环形链表相关的知识,希望对你有一定的参考价值。

环形链表的一种Go语言实现

package main

import "fmt"

//定义一个环形链表的节点结构体
type circleSingleLink struct {
    id int
    name string
    next *circleSingleLink
}

//插入节点到环形链表
func Insert(head, newnode *circleSingleLink) {
    //判断是否为空链表
    if head.next == nil{
        //如果为空则把添加的第一个元素给头节点,这里和其他链表有些区别,其他链表头结点是空的
        head.id = newnode.id
        head.name = newnode.name
        //重点是要让一个节点也形成一个环形,即收尾相接
        head.next = head
        return
    }
    //添加非头结点到环形链表
    temp := head
    for{
        if temp.next == head {
            //尾部添加
            temp.next = newnode
            newnode.next = head
            break
        }
        temp = temp.next
    }
}

//显示环形链表的所有节点信息
func CircleList(head *circleSingleLink){
    //判断链表是否为空
    if head.next == nil {
        fmt.Println("链表为空")
        return
    }

    temp := head
    for {
        fmt.Printf("[%d  %s] -> ", temp.id, temp.name)
        //注意这里是这样判断终止条件的
        if temp.next == head{
            break
        }
        temp = temp.next
    }
}

//删除环形链表的一个节点(难点)
func CircleDelete(head, node *circleSingleLink) *circleSingleLink { //之所以有返回值是因为头结点的值和指向要发生变化
    //删除思路:
    //先让temp 指向head
    //再让helper指向环形链表的最后
    //让temp和要删除的节点比较,如果相同,则让helper完成删除节点,重点是要考虑是否为头结点,因为环形链表的头结点有值

    temp := head
    if temp.next == nil { //链表为空的时候
        fmt.Println("链表为空")
        return head
    }
    if temp.next == head { //只有一个头结点的环形链表
        temp.next = nil
        return head
    }
    helper := head
    for {
        if helper.next == head {
            break
        }
        helper = helper.next //将指针定位到环形链表的尾节点
    }

    //如果有两个及以上的节点
    for {
        if temp.next == head {
            //说明到最后一个并且还没判断是否是要删除的节点
            if temp.id == node.id {
                helper.next = temp.next
            }else {
                fmt.Println("没有该节点")
            }
            break
        }
        if temp.id == node.id { //找到了要删除的节点
            if temp == head { //如果删除的是头结点
                head = head.next
            }
            //删除非头结点
            helper.next = temp.next //helper始终在temp的后一个
            break
        }
        temp = temp.next //用作比较
        helper = helper.next //用作操作
    }

    return head
}

func main(){
    //定义一个链表的头
    head := &circleSingleLink{}

    //定义第一个节点
    node1 := &circleSingleLink{
        id: 1,
        name : "number1",
    }
    node2 := &circleSingleLink{
        id: 2,
        name : "number2",
    }
    node3 := &circleSingleLink{
        id: 3,
        name : "number3",
    }
    Insert(head, node1)
    Insert(head, node2)
    Insert(head, node3)
    head = CircleDelete(head, node1)
    CircleList(head)
}

约瑟夫问题:

package main

import "fmt"

type Boy struct {
    id int
    next *Boy
}

//创建一个环形链表,并返回头指针
func CreateLink(num int) *Boy {
    first := &Boy{} //头指针不能动,因此需要一个辅助指针进行循环创建
    curBoy := &Boy{}

    if num < 1 {
        return first
    }
    for i := 1; i <= num; i++ {
        boy := &Boy{
            id:   i,
            next: nil,
        }
        if i == 1 { //因为第一个小孩比较特殊
            first = boy
            curBoy = boy
            curBoy.next = first
        }else {
            curBoy.next = boy
            curBoy = boy
            curBoy.next = first //构成环形链表
        }
    }
    return first
}

//显示环形链表
func ShowBoy (first *Boy) {
    if first.next == nil {
        fmt.Println("链表为空")
        return
    }
    curBoy := first
    for {
        fmt.Printf("小孩%d -> ", curBoy.id)
        if curBoy.next == first {
            break
        }
        curBoy = curBoy.next
    }
}

//使用环形链表实现约瑟夫问题
func PlayGame(first *Boy, startNo int, countNum int) {
    //空链表的情况
    if first.next == nil {
        fmt.Println("链表为空,游戏结束")
        return
    }
    //定义两个指针循环,其中first负责比较,tail负责删除
    tail := first
    //将tail指正定位到链表的最后
    for {
        if tail.next == first {
            break
        }
        tail = tail.next
    }
    //开始移动startNo-1步
    for i := 0; i < startNo -1; i++ {
        first = first.next
        tail = tail.next
    }
    fmt.Println()
    //开始循环删除环形链表中第countNum-1个节点
    for {
        for i := 0; i < countNum - 1; i++ {
            first = first.next
            tail = tail.next
        }
        //打印即将出圈的节点信息
        fmt.Printf("编号为%d的节点出圈\\n", first.id)
        //删除此时first指向的当前节点
        first = first.next
        tail.next = first
        //确定结束条件
        if tail == first {
            break
        }
    }
    //打印最后一个出圈的节点
    fmt.Printf("编号为%d的节点出圈\\n", first.id)
}

func main(){
    first := CreateLink(51)
    ShowBoy(first)
    PlayGame(first, 2, 3)
}

以上是关于数据结构--环形链表的主要内容,如果未能解决你的问题,请参考以下文章

Java 数据结构 & 算法宁可累死自己, 也要卷死别人 8 环形链表

Java 数据结构 & 算法宁可累死自己, 也要卷死别人 8 环形链表

HashMap1.8底层数据结构分析源代码解析1.7头插法如何形成环形链表

HashMap1.8底层数据结构分析源代码解析1.7头插法如何形成环形链表

代码题(15)— 环形链表

LeetCode Java刷题笔记—142. 环形链表 II