数据结构之链表


阅读目录

一、链表介绍

二、单链表

三、双链表

四、环形链表

一、链表介绍

链表是有序的列表,在内存中存储如下:
数据结构之链表_第1张图片

二、单链表

func main() {
    SingleLinkList()
}

// 链表 起名Node结尾
type HeroNode struct {
    no       int
    name     string
    nickname string
    next     *HeroNode // 指向下一个节点
}

// 单链表的第一种方式
func SingleLinkList() {
    // 1.先创建头节点
    head := &HeroNode{}
    // 2. 创建一个新的HeroNode
    hero1 := &HeroNode{
        no:       1,
        name:     "宋江",
        nickname: "及时雨",
        next:     nil,
    }
    hero2 := &HeroNode{
        no:       2,
        name:     "武松",
        nickname: "不知道",
        next:     nil,
    }
    hero3 := &HeroNode{
        no:       3,
        name:     "卢俊义",
        nickname: "玉麒麟",
        next:     nil,
    }
    // 3.加入
    //InsertHeroNode(head, hero1)
    //InsertHeroNode(head, hero2)
    //InsertHeroNode(head, hero3)
    InsertHeroNode2(head, hero1)
    InsertHeroNode2(head, hero2)
    InsertHeroNode2(head, hero3)
    //4.显示
    ListHeroNode(head)

    // 删除
    fmt.Println()
    DelHeroNode(head, 1)
    DelHeroNode(head, 2)
    DelHeroNode(head, 3)
    DelHeroNode(head, 4)
    fmt.Println()
    ListHeroNode(head)

}

// 给列表增加or插入节点
// 第一种插入方式,在单链表最后插入
func InsertHeroNode(headNode *HeroNode, newNode *HeroNode) {
    // 1.先找到该链表最后节点
    // 2. 创建辅助节点[跑龙套]
    tmp := headNode
    for {
        if tmp.next == nil {
            //表示找到最后节点了
            break
        }
        // tmp向下走
        tmp = tmp.next
    }
    //3.加入newNode
    tmp.next = newNode
}

// 给列表增加or插入节点
// 第二种插入方式,找到适当节点,插入,比如根据编号排序
func InsertHeroNode2(headNode *HeroNode, newNode *HeroNode) {
    // 1.先找到该链表最后节点
    // 2. 创建辅助节点[跑龙套]
    tmp := headNode
    for {
        if tmp.next == nil {
            //表示找到最后节点了
            break
        } else if tmp.next.no > newNode.no {
            // newNode就应该插入tmp后面
            break
        } else if tmp.next.no == newNode.no {
            fmt.Println("已存在no,插入错误!")
            return
        }
        // tmp向下
        tmp = tmp.next
    }
    //3.加入newNode
    newNode.next = tmp.next // 先将加入节点的下一个节点指向tmp下一个节点
    tmp.next = newNode      // 在将tmp下一个节点指向newNode
}

// 显示链表的所有节点信息
func ListHeroNode(head *HeroNode) {
    //1.创建辅助节点
    tmp := head
    //2.判断列表是否为空
    if tmp.next == nil {
        fmt.Println("空链表...")
        return
    }
    //3.遍历列表
    for {
        fmt.Printf("节点信息:[%d, %s, %s, ] ->", tmp.next.no, tmp.next.name, tmp.next.nickname)
        // 判断是否到了链表尾部
        tmp = tmp.next
        if tmp.next == nil {
            break
        }
    }
}

// 删除节点
func DelHeroNode(head *HeroNode, id int) {
    tmp := head
    flag := false
    for {
        if tmp.next == nil {
            break
        } else if tmp.next.no == id {
            // 找到了
            flag = true
            break
        }
        tmp = tmp.next
    }
    if flag {
        // 改变连接,垃圾回收会处理单连
        tmp.next = tmp.next.next
    } else {
        fmt.Println("删除的元素不存在...")
    }
}

三、双链表

// 双向链表
func main() {
    DoubleLinkList()
}

// 链表 起名Node结尾
type HeroNode1 struct {
    no       int
    name     string
    nickname string
    pre      *HeroNode1 // 指向前一个节点
    next     *HeroNode1 // 指向下一个节点
}

// 单链表的第一种方式
func DoubleLinkList() {
    // 1.先创建头节点
    head := &HeroNode1{}
    // 2. 创建一个新的HeroNode
    hero1 := &HeroNode1{
        no:       1,
        name:     "宋江",
        nickname: "及时雨",
        next:     nil,
        pre:      nil,
    }
    hero2 := &HeroNode1{
        no:       2,
        name:     "武松",
        nickname: "不知道",
        next:     nil,
        pre:      nil,
    }
    hero3 := &HeroNode1{
        no:       3,
        name:     "卢俊义",
        nickname: "玉麒麟",
        next:     nil,
        pre:      nil,
    }
    // 3.加入
    InsertHeroNode1(head, hero1)
    InsertHeroNode1(head, hero2)
    InsertHeroNode1(head, hero3)
    //4.显示
    ListHeroNode1(head)

    // 删除
    fmt.Println()
    ListHeroNode2(head)
    //DelHeroNode(head, 1)
    //DelHeroNode(head, 2)
    //DelHeroNode(head, 3)
    //DelHeroNode(head, 4)
    //fmt.Println()
    //ListHeroNode(head)

}

// 给列表增加or插入节点
// 第一种插入方式,在单链表最后插入
func InsertHeroNode1(headNode *HeroNode1, newNode *HeroNode1) {
    // 1.先找到该链表最后节点
    // 2.创建辅助节点[跑龙套]
    tmp := headNode
    for {
        if tmp.next == nil {
            //表示找到最后节点了
            break
        }
        // tmp向下走
        tmp = tmp.next
    }
    //3.加入newNode
    tmp.next = newNode
    newNode.pre = tmp
}

// 给列表增加or插入节点
// 第二种插入方式,找到适当节点,插入,比如根据编号排序
func InsertHeroNode3(headNode *HeroNode1, newNode *HeroNode1) {
    // 1.先找到该链表最后节点
    // 2. 创建辅助节点[跑龙套]
    tmp := headNode
    for {
        if tmp.next == nil {
            //表示找到最后节点了
            break
        } else if tmp.next.no > newNode.no {
            // newNode就应该插入tmp后面
            break
        } else if tmp.next.no == newNode.no {
            fmt.Println("已存在no,插入错误!")
            return
        }
        // tmp向下
        tmp = tmp.next
    }
    //3.加入newNode
    newNode.next = tmp.next // 先将加入节点的下一个节点指向tmp下一个节点
    newNode.pre = tmp
    if tmp.next != nil {
        tmp.next.pre = newNode
    }
    tmp.next = newNode // 在将tmp下一个节点指向newNode
}

// 显示链表的所有节点信息
func ListHeroNode1(head *HeroNode1) {
    //1.创建辅助节点
    tmp := head
    //2.判断列表是否为空
    if tmp.next == nil {
        fmt.Println("空链表...")
        return
    }
    //3.遍历列表
    for {
        fmt.Printf("节点信息:[%d, %s, %s, ] ->", tmp.next.no, tmp.next.name, tmp.next.nickname)
        // 判断是否到了链表尾部
        tmp = tmp.next
        if tmp.next == nil {
            break
        }
    }
}

// 删除节点
func DelHeroNode1(head *HeroNode1, id int) {
    tmp := head
    flag := false
    for {
        if tmp.next == nil {
            break
        } else if tmp.next.no == id {
            // 找到了
            flag = true
            break
        }
        tmp = tmp.next
    }
    if flag {
        // 改变连接,垃圾回收会处理单连
        tmp.next = tmp.next.next
        if tmp.next != nil {
            tmp.next.pre = tmp
        }
    } else {
        fmt.Println("删除的元素不存在...")
    }

}

// 倒序打印
// 显示链表的所有节点信息
func ListHeroNode2(head *HeroNode1) {
    //1.创建辅助节点
    tmp := head
    //2.判断列表是否为空
    if tmp.next == nil {
        fmt.Println("空链表...")
        return
    }
    for {
        if tmp.next == nil {
            break
        }
        // 走到最后
        tmp = tmp.next
    }

    //3.遍历列表
    for {
        fmt.Printf("节点信息:[%d, %s, %s, ] ->", tmp.no, tmp.name, tmp.nickname)
        // 判断是否到了链表尾部
        tmp = tmp.pre
        if tmp.pre == nil {
            break
        }
    }
}

四、环形链表

单向环形链表

type CatNode struct {
    no   int
    name string
    next *CatNode
}

//插入环形链表
func InsertCatNode(head *CatNode, newNode *CatNode) {
    if head.next == nil {
        head.no = newNode.no
        head.name = newNode.name
        head.next = head // 构成环形
        return
    }
    tmp := head
    for {
        if tmp.next == head {
            break
        }
        tmp = tmp.next
    }
    tmp.next = newNode
    newNode.next = head
}

func ListCircleLink(head *CatNode) {
    tmp := head
    if tmp.next == nil {
        fmt.Println("空的环形链表...")
        return
    }
    for {
        fmt.Println("cat info:", tmp.name, "->")
        if tmp.next == head {
            break
        }
        tmp = tmp.next
    }
}

func main() {
    head := &CatNode{}
    cat1 := &CatNode{
        no:   1,
        name: "tom1",
        next: nil,
    }
    cat2 := &CatNode{
        no:   2,
        name: "tom2",
        next: nil,
    }
    cat3 := &CatNode{
        no:   3,
        name: "tom3",
        next: nil,
    }
    InsertCatNode(head, cat1)
    InsertCatNode(head, cat2)
    InsertCatNode(head, cat3)
    ListCircleLink(head)
    head = DelCatNode(head, 1)
    fmt.Println()
    ListCircleLink(head)
    head = DelCatNode(head, 30)
    fmt.Println()
    ListCircleLink(head)
    head = DelCatNode(head, 3)
    fmt.Println()
    ListCircleLink(head)
}

func DelCatNode(head *CatNode, no int) *CatNode {
    tmp := head    //辅助查找 no
    helper := head // 标志最后位置
    if tmp.next == nil {
        fmt.Println("空链表...")
        return head
    }
    // 如果只有一个节点
    if tmp.next == head {
        tmp.next = nil
        return head
    }
    // 将helper指向最后
    for {
        if helper.next == head {
            break
        }
        helper = helper.next
    }

    flag := true
    //如果有两个及以上
    for {
        if tmp.next == head {
            // 已经到最后一个  还没有比较/还没找到
            break
        }
        if tmp.no == no {
            if tmp == head {
                // 删除头节点
                head = head.next
            }
            // 正好匹配到
            helper.next = tmp.next
            flag = false
            break
        }
        tmp = tmp.next       //比较
        helper = helper.next //一旦找到,helper辅助删除
    }
    if flag {
        //整个循环中没有删除过,比较最后一次
        if tmp.no == no {
            // 比较最后一个
            helper.next = tmp.next
        } else {
            fmt.Println("没有此节点...")
        }
    }
    return head
}

单向环形链表应用 - josephu(丢手帕)

func main() {
    first := AddBoy(500)
    //显示
    ShowBoy(first)
    fmt.Println()
    PlayGame(first, 3, 20)
}

type Boy struct {
    no   int  //编号
    next *Boy //指向下一个小孩指针
}

func AddBoy(num int) *Boy {
    // 传入创建小孩的个数   返回第一个小孩的指针
    first := &Boy{}  //空节点 头
    curBoy := &Boy{} //辅助节点
    if num < 1 {
        fmt.Println("不支持构建数据")
        return first // 返回空指针
    }
    for i := 1; i <= num; i++ {
        boy := &Boy{
            no: i,
        }
        //1.因为第一个小孩比较特殊
        if i == 1 {
            first = boy
            curBoy = boy
            curBoy.next = first // 构成循环
        } else {
            curBoy.next = boy // 指针移位
            curBoy = boy
            curBoy.next = first //构成环形
        }
    }
    return first
}

// 显示单向环形链表
func ShowBoy(head *Boy) {
    //如果环形列表为空
    if head.next == nil {
        fmt.Println("空链表...")
    }

    // 辅助指针
    curBoy := head
    for {
        fmt.Printf("小孩编号【%d】->", curBoy.no)
        if curBoy.next == head {
            break // 遍历完了
        }
        curBoy = curBoy.next // 下移
    }
}

func PlayGame(first *Boy, startNo int, countNum int) {
    //1.空的链表单独处理
    if first.next == nil {
        fmt.Println("空链表...")
        return
    }
    // 2.start不能大于小孩总数
    if startNo > 500 {
        fmt.Println("不支持的开始节点")
        return
    }
    // 3.需要定义赋值指针
    tail := first //帮助删除
    for {
        if tail.next == first {
            break // 指向最后一个节点
        }
        tail = tail.next
    }
    // 4.先让first移动到startNo
    for i := 1; i <= startNo-1; i++ {
        first = first.next
        tail = tail.next //一起移动
    }
    // 5.开始数countNum,删除first指向的小孩
    for {

        if first == tail {
            //判断退出条件
            fmt.Println("最后出圈小孩:", first.no)
            break
        }
        //数countNum-1
        for i := 1; i <= countNum-1; i++ {
            first = first.next
            tail = tail.next //一起移动
        }
        fmt.Printf("第【%d】小孩出圈\n", first.no)
        // 删除first
        first = first.next //走到下一步
        tail.next = first  //断开连接原始first
    }
}

你可能感兴趣的:(数据结构之链表)