go_链表

1、链表

本文讲解了单向链表、双向链表、环形链表以及约瑟夫案例

1.1 链表的介绍

链表是有序的链表,但是它在内存中的存储如下:

1.2 单链表的介绍

​ 一般来说,为了更好的对单链表进行增删改查的操作,我们都会给他设置一个头结点,头结点的作用主要是用来标识链表头,本身这个节点不存放数据。

1.3 单链表的应用示例

  1. 案例说明

    1. 使用带head头的单向链表实现 ---- 水浒英雄排行版管理
    2. 完成对英雄人物的增删改查
  2. 第一种方法,添加英雄时,直接添加到链表尾部

    package main
    
    import "fmt"
    
    type heroNode struct {
    	no   int
    	name string
    	next *heroNode
    }
    
    //增
    //1、在链表末尾添加节点
    func InsertHeroNode(head *heroNode, newHeroNode *heroNode) {
    	//1、找到该链表的最后节点
    	//2、创建一个临时节点
    	temp := head
    	for {
    		if temp.next == nil {
    			break
    		}
    		temp = temp.next
    	}
    	//3、将新节点加到链表的最后
    	temp.next = newHeroNode
    }
    
    //2、按顺序添加节点到链表
    func InsertHeroNode2(head *heroNode, newHeroNode *heroNode) {
    	//1、找到该链表的最后节点
    	//2、创建一个临时节点
    	temp := head
    	for {
    		if temp.next == nil {
    			break
    		}
    		temp = temp.next
    	}
    	//3、将新节点加到链表的最后
    	temp.next = newHeroNode
    }
    
    //删
    
    //改
    
    //显示链表的所有信息
    func listHeroNode(head *heroNode) {
    	temp := head
    	if temp.next == nil {
    		fmt.Println("链表为空")
    		return
    	}
    
    	for {
    		fmt.Printf("[%d %s]====》", temp.next.no, temp.next.name)
    		temp = temp.next
    		//判断是否链表最后节点
    		if temp.next == nil {
    			break
    		}
    	}
    }
    
    //实现一个单向链表
    func main() {
    	//头节点
    	head := &heroNode{}
    	//新节点
    	heronode1 := &heroNode{
    		no:   1,
    		name: "宋江",
    	}
    	heronode2 := &heroNode{
    		no:   2,
    		name: "卢俊义",
    	}
    	heronode3 := &heroNode{
    		no:   2,
    		name: "林冲",
    	}
    
    	//增加节点
    	InsertHeroNode(head, heronode1)
    	InsertHeroNode(head, heronode2)
    	InsertHeroNode(head, heronode3)
    	//显示节点列表
    	listHeroNode(head)
    }
    
  3. 第二种根据英雄排名,排序遍历链表节点

    //给链表插入一个节点
    //2、编写第二种插入方法,根据no的编号从小到大插入【实用】
    func InsertHeroNode2(head *heroNode, newHeroNode *heroNode) {
    	//1、找到适当的节点
    	//2、创建一个临时节点
    	temp := head
    	flag := true
    	//让插入的节点的no和temp的下一个节点的no比较
    	for {
    		if temp.next == nil {
    			break
    		} else if newHeroNode.no < temp.next.no {
    			//newHeroNode.no <= temp.next.no (表示no可以重复)
    			//说明neHeroNode就应该插入到temp后面
    			break
    		} else if temp.next.no == newHeroNode.no {
    			//说明链表中已经有这个no,不允许插入
    			flag = false
    			break
    		}
    		temp = temp.next
    	}
    	if !flag {
    		fmt.Printf("hero no 已经存在,no=[%d]\n", newHeroNode.no)
    		return
    	} else {
    		newHeroNode.next = temp.next
    		temp.next = newHeroNode
    	}
    }
    
    
    

4、单链表删除节点

//删出一个节点
func delNode(head *heroNode, no int) {
	temp := head
	flag := false
	//让要删除的节点的no和temp的下一个节点的no比较
	for {
		if temp.next == nil { //说明找到了链表最后
			break
		} else if temp.next.no == no {
			//说明找到了要删除的节点
			flag = true
			break
		}
		temp = temp.next
	}
	if flag { //找到,删除
		temp.next = temp.next.next
	} else {
		fmt.Println("要删除的id 不存在")
	}
}

1.4 双向链表

单向链表的缺点分析:

1、单向链表查找的方向只能是一个方向,而双向链表可以向前或者向后查找

2、单向链表不能自我删除,需要靠辅助节点,而双向链表可以自我删除,所以前面我们单链表删除时节点总是找到temp的下一个节点来删除的

package main

import "fmt"

type HeroNode struct {
	no   int
	name string
	pre  *HeroNode
	next *HeroNode
}

//在双向链表末尾追加一个节点
func insertNode(head *HeroNode, newHeroNOde *HeroNode) {
	//1、判断链表是否为空
	//创建一个辅助变量
	temp := head
	for {
		if temp.next == nil {
			break
		}
		temp = temp.next //节点后移
	}
	temp.next = newHeroNOde
	newHeroNOde.pre = temp

}

//双向链表,添加的节点,按照no从小到大排序
func insetNode1(head *HeroNode, newHeroNode *HeroNode) {
	temp := head
	flag := true
	for {
		if temp.next == nil {
			break
		} else if temp.next.no > newHeroNode.no {
			break
		} else if temp.next.no == newHeroNode.no {
			flag = false
			break
		}
		temp = temp.next
	}
	if !flag {
		fmt.Println("不允许出现重复的no排名")
		return
	} else {
		newHeroNode.next = temp.next
		newHeroNode.pre = temp
		if temp.next != nil {
			temp.next.pre = newHeroNode
		}
		temp.next = newHeroNode
	}
}

//显示链表的节点信息
func listNode(head *HeroNode) {
	temp := head
	if temp.next == nil {
		fmt.Println("链表为空")
		return
	}
	for {
		fmt.Printf("[%d %s]==>", temp.next.no, temp.next.name)
		temp = temp.next
		if temp.next == nil {
			break
		}
	}
	fmt.Println()
}

//验证双向链表,逆序打印
func listNode2(head *HeroNode) {
	temp := head
	//判断链表是否为空
	if temp.next == nil {
		fmt.Println("链表为空")
		return
	}
	//让temp定位到双向链表的最后节点
	for {
		if temp.next == nil {
			break
		}
		temp = temp.next
	}
	//遍历链表
	for {
		fmt.Printf("[%d %s]==>", temp.no, temp.name)
		temp = temp.pre
		//判断是否链表头
		if temp.pre == nil {
			break
		}
	}
	fmt.Println()
}

//删除节点
func delNode(head *HeroNode, val int) {
	temp := head
	flag := false

	for {
		if temp.next == nil {
			break
		} else if temp.next.no == val {
			flag = true
			break
		}
		temp = temp.next
	}
	if !flag {
		fmt.Println("删除的节点不存在")
		return
	} else {
		temp.next = temp.next.next
		if temp.next != nil {
			temp.next.pre = temp
		}
	}
}

func main() {
	//1、头结点
	head := &HeroNode{}
	//2、新节点
	Node1 := &HeroNode{
		no:   1,
		name: "宋江",
	}
	Node2 := &HeroNode{
		no:   2,
		name: "卢俊义",
	}
	Node3 := &HeroNode{
		no:   3,
		name: "林冲",
	}
	//3、
	insertNode(head, Node1)
	insertNode(head, Node2)
	insertNode(head, Node3)
	listNode(head)
	listNode2(head)
	delNode(head, 2)
	listNode(head)
}

1.5 环形单向链表介绍

1.6 环形单向链表案例

完成对单向链表的添加节点,删除节点和显示节点

删除一个环形单向链表的思路:

1、先让temp指向head

2、让helper指向环形链表最后

3、让temp和要删除的id进行比较,如果相同,则同helper完成删除操作【这里必须考虑如果删除的是头结点?】

package main

import "fmt"

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

//增加节点
func addNode(head *Node, newNode *Node) {
	if head.next == nil {
		head.no = newNode.no
		head.name = newNode.name
		head.next = head
		return
	}
	//目前暂定添加到环形链表的最后
	temp := head
	for {
		if temp.next == head {
			break
		}
		temp = temp.next
	}
	//最后加入节点
	temp.next = newNode
	newNode.next = head
}

//显示节点
func listLink(head *Node) {
	fmt.Println("环形链表的情况如下:......")
	temp := head
	if temp.next == nil {
		fmt.Println("链表为空")
		return
	}
	for {
		fmt.Printf("节点信息:【no=%d,name=%s】", temp.no, temp.name)
		if temp.next == head {
			break
		}
		temp = temp.next
	}
}

//删除节点
func deleNode(head *Node, id int) *Node {
	temp := head
	helper := head
	if temp.next == nil {
		fmt.Println("空链表,不能删除")
		return head
	}
	//只有一个节点的环形链表
	if temp.next == head {
		head.next = nil
		return head
	}

	//将helper定位到链表最后
	for {
		if helper.next == head {
			break
		}
		helper = helper.next
	}
	//如果有两个或以上的节点,要先找到最后的节点
	flag := true
	for {
		if temp.next == head { //到这里说明比较到了最后一个,但是最后一个还没比较
			break
		}
		if temp.no == id {
			if temp == head { //说明删除的是头节点
				head = head.next
			}
			//找到要删除的节点
			helper.next = temp.next
			fmt.Println(id, "被删除")
			flag = false
			break
		}
		//temp和helper同步移动
		temp = temp.next
		helper = helper.next //找到要删除的节点,helper来删除
	}
	//出来还要判断最后一次
	if flag { //如果flag为真,则我们上面没有删除
		if temp.no == id { //最后一个节点比较
			helper.next = temp.next
		} else {
			fmt.Println(id, "删除的节点不存在")
		}
	}
	return head
}

func main() {
	head := &Node{}

	head1 := &Node{
		no:   1,
		name: "tom",
	}
	head2 := &Node{
		no:   2,
		name: "jack",
	}
	addNode(head, head1)
	addNode(head, head2)
	listLink(head)
	fmt.Println()
	deleNode(head, 2)
	listLink(head)
}

1.7 约瑟夫小案例

约瑟夫问题是个有名的问题:N个人围成一圈,约定编号为k(1<=k<=N)的人从1开始报数,数到m的那个人出列,它的下一位又从1开始报数,数到m的那个人又出列,依次类推,直到所有人出列为止,由此产生一个出队编号的序列

提示:

用一个不带头结点的循环链表来处理约瑟夫问题:

先构成一个有n个节点的单向循环链表

然后由k节点开始从1开始计数

记到m时,对应节点从链表中删除

然后再从被删除节点的下一个开始计数

直到最后一个节点从链表中删除

算法结束

代码实现:

package main

import "fmt"

type Boy struct {
	num  int
	next *Boy
}

//构建一个单向循环链表
func AddBoy(num int) *Boy {
	first := &Boy{}
	curBoy := &Boy{}
	//判断
	if num < 1 {
		fmt.Println("num 的值不对")
		return first
	}
	//循环的构建这个环形链表
	for i := 0; i <= num; i++ {
		boy := &Boy{
			num: i,
		}
		//分析,构成循环链表需要一个辅助指针
		//1、因为第一个小孩比较特殊
		if i == 1 {
			first = boy //不能动
			curBoy = boy
			curBoy.next = first
		} else {
			curBoy.next = boy
			curBoy = boy
			curBoy.next = first //构成环形链表
		}
	}
	return first
}

//显示单向的环形链表
func listBoy(first *Boy) {
	if first.next == nil {
		fmt.Println("链表为空")
		return
	}
	//创建一个指针,帮助遍历
	curBoy := first
	for {
		fmt.Printf("小孩编号=%d ->", curBoy.num)
		//退出的条件 curBoy.next ==first
		if curBoy.next == first {
			break
		}
		//curBoy移动到下一个位置
		curBoy = curBoy.next
	}
}

/*N个人围成一圈,约定编号为k(1<=k<=N)的人从1开始报数,数到m的那个人出列,
它的下一位又从1开始报数,数到m的那个人又出列,依次类推,直到所有人出列为止,由此产生一个出队编号的序列*/

//分析思路
//1、编写一个函数,playGame(first *Boy,startNo int,countNum int)
//2、最后我们使用一个算法,按照要求,在环形链表中留下最后一个
func playGame(first *Boy, startNo int, countNum int) {
	if first.next == nil {
		fmt.Println("链表为空,说明没人")
		return
	}
	//2、定义个辅助指针,帮助删除节点
	tail := first
	//3、让tail执行环形链表到最后一个节点,这个非常重要
	//tail在删除节点的时候会用到
	for {
		if tail.next == first { //说明tail到了最后的小孩
			break
		}
		tail = tail.next
	}
	//4、让first移动到startNo【后面删除小孩就以first为准】
	for i := 1; i <= startNo; i++ {
		first = first.next
		tail = tail.next
	}
	//5、开始数countNum,删除first指向的节点
	for {
		//开始数count-1次
		for i := 1; i < countNum-1; i++ {
			first = first.next
			tail = tail.next
		}
		fmt.Printf("删除节点【%d】", first.num)
		//删除first指向的节点
		first = first.next
		tail.next = first
		//如果tail ==first,圈中只有一个节点
		if tail == first {
			break
		}
	}
	fmt.Printf("最后留下的小孩节点为【%d】", first.num)
}

func main() {
	first := AddBoy(5)
	listBoy(first)
	fmt.Println()
	playGame(first, 2, 3)
}

参考网站:### 6、链表

6.1 链表的介绍

链表是有序的链表,但是它在内存中的存储如下:

1613316590800

6.2 单链表的介绍

​ 一般来说,为了更好的对单链表进行增删改查的操作,我们都会给他设置一个头结点,头结点的作用主要是用来标识链表头,本身这个节点不存放数据。

1613317157853

6.3 单链表的应用示例

  1. 案例说明

    1. 使用带head头的单向链表实现 ---- 水浒英雄排行版管理
    2. 完成对英雄人物的增删改查
  2. 第一种方法,添加英雄时,直接添加到链表尾部

    package main
    
    import "fmt"
    
    type heroNode struct {
    	no   int
    	name string
    	next *heroNode
    }
    
    //增
    //1、在链表末尾添加节点
    func InsertHeroNode(head *heroNode, newHeroNode *heroNode) {
    	//1、找到该链表的最后节点
    	//2、创建一个临时节点
    	temp := head
    	for {
    		if temp.next == nil {
    			break
    		}
    		temp = temp.next
    	}
    	//3、将新节点加到链表的最后
    	temp.next = newHeroNode
    }
    
    //2、按顺序添加节点到链表
    func InsertHeroNode2(head *heroNode, newHeroNode *heroNode) {
    	//1、找到该链表的最后节点
    	//2、创建一个临时节点
    	temp := head
    	for {
    		if temp.next == nil {
    			break
    		}
    		temp = temp.next
    	}
    	//3、将新节点加到链表的最后
    	temp.next = newHeroNode
    }
    
    //删
    
    //改
    
    //显示链表的所有信息
    func listHeroNode(head *heroNode) {
    	temp := head
    	if temp.next == nil {
    		fmt.Println("链表为空")
    		return
    	}
    
    	for {
    		fmt.Printf("[%d %s]====》", temp.next.no, temp.next.name)
    		temp = temp.next
    		//判断是否链表最后节点
    		if temp.next == nil {
    			break
    		}
    	}
    }
    
    //实现一个单向链表
    func main() {
    	//头节点
    	head := &heroNode{}
    	//新节点
    	heronode1 := &heroNode{
    		no:   1,
    		name: "宋江",
    	}
    	heronode2 := &heroNode{
    		no:   2,
    		name: "卢俊义",
    	}
    	heronode3 := &heroNode{
    		no:   2,
    		name: "林冲",
    	}
    
    	//增加节点
    	InsertHeroNode(head, heronode1)
    	InsertHeroNode(head, heronode2)
    	InsertHeroNode(head, heronode3)
    	//显示节点列表
    	listHeroNode(head)
    }
    
  3. 第二种根据英雄排名,排序遍历链表节点

    //给链表插入一个节点
    //2、编写第二种插入方法,根据no的编号从小到大插入【实用】
    func InsertHeroNode2(head *heroNode, newHeroNode *heroNode) {
    	//1、找到适当的节点
    	//2、创建一个临时节点
    	temp := head
    	flag := true
    	//让插入的节点的no和temp的下一个节点的no比较
    	for {
    		if temp.next == nil {
    			break
    		} else if newHeroNode.no < temp.next.no {
    			//newHeroNode.no <= temp.next.no (表示no可以重复)
    			//说明neHeroNode就应该插入到temp后面
    			break
    		} else if temp.next.no == newHeroNode.no {
    			//说明链表中已经有这个no,不允许插入
    			flag = false
    			break
    		}
    		temp = temp.next
    	}
    	if !flag {
    		fmt.Printf("hero no 已经存在,no=[%d]\n", newHeroNode.no)
    		return
    	} else {
    		newHeroNode.next = temp.next
    		temp.next = newHeroNode
    	}
    }
    
    
    

4、单链表删除节点

//删出一个节点
func delNode(head *heroNode, no int) {
	temp := head
	flag := false
	//让要删除的节点的no和temp的下一个节点的no比较
	for {
		if temp.next == nil { //说明找到了链表最后
			break
		} else if temp.next.no == no {
			//说明找到了要删除的节点
			flag = true
			break
		}
		temp = temp.next
	}
	if flag { //找到,删除
		temp.next = temp.next.next
	} else {
		fmt.Println("要删除的id 不存在")
	}
}

6.4 双向链表

单向链表的缺点分析:

1、单向链表查找的方向只能是一个方向,而双向链表可以向前或者向后查找

2、单向链表不能自我删除,需要靠辅助节点,而双向链表可以自我删除,所以前面我们单链表删除时节点总是找到temp的下一个节点来删除的

package main

import "fmt"

type HeroNode struct {
	no   int
	name string
	pre  *HeroNode
	next *HeroNode
}

//在双向链表末尾追加一个节点
func insertNode(head *HeroNode, newHeroNOde *HeroNode) {
	//1、判断链表是否为空
	//创建一个辅助变量
	temp := head
	for {
		if temp.next == nil {
			break
		}
		temp = temp.next //节点后移
	}
	temp.next = newHeroNOde
	newHeroNOde.pre = temp

}

//双向链表,添加的节点,按照no从小到大排序
func insetNode1(head *HeroNode, newHeroNode *HeroNode) {
	temp := head
	flag := true
	for {
		if temp.next == nil {
			break
		} else if temp.next.no > newHeroNode.no {
			break
		} else if temp.next.no == newHeroNode.no {
			flag = false
			break
		}
		temp = temp.next
	}
	if !flag {
		fmt.Println("不允许出现重复的no排名")
		return
	} else {
		newHeroNode.next = temp.next
		newHeroNode.pre = temp
		if temp.next != nil {
			temp.next.pre = newHeroNode
		}
		temp.next = newHeroNode
	}
}

//显示链表的节点信息
func listNode(head *HeroNode) {
	temp := head
	if temp.next == nil {
		fmt.Println("链表为空")
		return
	}
	for {
		fmt.Printf("[%d %s]==>", temp.next.no, temp.next.name)
		temp = temp.next
		if temp.next == nil {
			break
		}
	}
	fmt.Println()
}

//验证双向链表,逆序打印
func listNode2(head *HeroNode) {
	temp := head
	//判断链表是否为空
	if temp.next == nil {
		fmt.Println("链表为空")
		return
	}
	//让temp定位到双向链表的最后节点
	for {
		if temp.next == nil {
			break
		}
		temp = temp.next
	}
	//遍历链表
	for {
		fmt.Printf("[%d %s]==>", temp.no, temp.name)
		temp = temp.pre
		//判断是否链表头
		if temp.pre == nil {
			break
		}
	}
	fmt.Println()
}

//删除节点
func delNode(head *HeroNode, val int) {
	temp := head
	flag := false

	for {
		if temp.next == nil {
			break
		} else if temp.next.no == val {
			flag = true
			break
		}
		temp = temp.next
	}
	if !flag {
		fmt.Println("删除的节点不存在")
		return
	} else {
		temp.next = temp.next.next
		if temp.next != nil {
			temp.next.pre = temp
		}
	}
}

func main() {
	//1、头结点
	head := &HeroNode{}
	//2、新节点
	Node1 := &HeroNode{
		no:   1,
		name: "宋江",
	}
	Node2 := &HeroNode{
		no:   2,
		name: "卢俊义",
	}
	Node3 := &HeroNode{
		no:   3,
		name: "林冲",
	}
	//3、
	insertNode(head, Node1)
	insertNode(head, Node2)
	insertNode(head, Node3)
	listNode(head)
	listNode2(head)
	delNode(head, 2)
	listNode(head)
}

6.5 环形单向链表介绍

1613449768422

6.6 环形单向链表案例

完成对单向链表的添加节点,删除节点和显示节点

1613490886878

删除一个环形单向链表的思路:

1、先让temp指向head

2、让helper指向环形链表最后

3、让temp和要删除的id进行比较,如果相同,则同helper完成删除操作【这里必须考虑如果删除的是头结点?】

package main

import "fmt"

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

//增加节点
func addNode(head *Node, newNode *Node) {
	if head.next == nil {
		head.no = newNode.no
		head.name = newNode.name
		head.next = head
		return
	}
	//目前暂定添加到环形链表的最后
	temp := head
	for {
		if temp.next == head {
			break
		}
		temp = temp.next
	}
	//最后加入节点
	temp.next = newNode
	newNode.next = head
}

//显示节点
func listLink(head *Node) {
	fmt.Println("环形链表的情况如下:......")
	temp := head
	if temp.next == nil {
		fmt.Println("链表为空")
		return
	}
	for {
		fmt.Printf("节点信息:【no=%d,name=%s】", temp.no, temp.name)
		if temp.next == head {
			break
		}
		temp = temp.next
	}
}

//删除节点
func deleNode(head *Node, id int) *Node {
	temp := head
	helper := head
	if temp.next == nil {
		fmt.Println("空链表,不能删除")
		return head
	}
	//只有一个节点的环形链表
	if temp.next == head {
		head.next = nil
		return head
	}

	//将helper定位到链表最后
	for {
		if helper.next == head {
			break
		}
		helper = helper.next
	}
	//如果有两个或以上的节点,要先找到最后的节点
	flag := true
	for {
		if temp.next == head { //到这里说明比较到了最后一个,但是最后一个还没比较
			break
		}
		if temp.no == id {
			if temp == head { //说明删除的是头节点
				head = head.next
			}
			//找到要删除的节点
			helper.next = temp.next
			fmt.Println(id, "被删除")
			flag = false
			break
		}
		//temp和helper同步移动
		temp = temp.next
		helper = helper.next //找到要删除的节点,helper来删除
	}
	//出来还要判断最后一次
	if flag { //如果flag为真,则我们上面没有删除
		if temp.no == id { //最后一个节点比较
			helper.next = temp.next
		} else {
			fmt.Println(id, "删除的节点不存在")
		}
	}
	return head
}

func main() {
	head := &Node{}

	head1 := &Node{
		no:   1,
		name: "tom",
	}
	head2 := &Node{
		no:   2,
		name: "jack",
	}
	addNode(head, head1)
	addNode(head, head2)
	listLink(head)
	fmt.Println()
	deleNode(head, 2)
	listLink(head)
}

6.7 约瑟夫小案例

约瑟夫问题是个有名的问题:N个人围成一圈,约定编号为k(1<=k<=N)的人从1开始报数,数到m的那个人出列,它的下一位又从1开始报数,数到m的那个人又出列,依次类推,直到所有人出列为止,由此产生一个出队编号的序列

提示:

用一个不带头结点的循环链表来处理约瑟夫问题:

先构成一个有n个节点的单向循环链表

然后由k节点开始从1开始计数

记到m时,对应节点从链表中删除

然后再从被删除节点的下一个开始计数

直到最后一个节点从链表中删除

算法结束

代码实现:

package main

import "fmt"

type Boy struct {
	num  int
	next *Boy
}

//构建一个单向循环链表
func AddBoy(num int) *Boy {
	first := &Boy{}
	curBoy := &Boy{}
	//判断
	if num < 1 {
		fmt.Println("num 的值不对")
		return first
	}
	//循环的构建这个环形链表
	for i := 0; i <= num; i++ {
		boy := &Boy{
			num: i,
		}
		//分析,构成循环链表需要一个辅助指针
		//1、因为第一个小孩比较特殊
		if i == 1 {
			first = boy //不能动
			curBoy = boy
			curBoy.next = first
		} else {
			curBoy.next = boy
			curBoy = boy
			curBoy.next = first //构成环形链表
		}
	}
	return first
}

//显示单向的环形链表
func listBoy(first *Boy) {
	if first.next == nil {
		fmt.Println("链表为空")
		return
	}
	//创建一个指针,帮助遍历
	curBoy := first
	for {
		fmt.Printf("小孩编号=%d ->", curBoy.num)
		//退出的条件 curBoy.next ==first
		if curBoy.next == first {
			break
		}
		//curBoy移动到下一个位置
		curBoy = curBoy.next
	}
}

/*N个人围成一圈,约定编号为k(1<=k<=N)的人从1开始报数,数到m的那个人出列,
它的下一位又从1开始报数,数到m的那个人又出列,依次类推,直到所有人出列为止,由此产生一个出队编号的序列*/

//分析思路
//1、编写一个函数,playGame(first *Boy,startNo int,countNum int)
//2、最后我们使用一个算法,按照要求,在环形链表中留下最后一个
func playGame(first *Boy, startNo int, countNum int) {
	if first.next == nil {
		fmt.Println("链表为空,说明没人")
		return
	}
	//2、定义个辅助指针,帮助删除节点
	tail := first
	//3、让tail执行环形链表到最后一个节点,这个非常重要
	//tail在删除节点的时候会用到
	for {
		if tail.next == first { //说明tail到了最后的小孩
			break
		}
		tail = tail.next
	}
	//4、让first移动到startNo【后面删除小孩就以first为准】
	for i := 1; i <= startNo; i++ {
		first = first.next
		tail = tail.next
	}
	//5、开始数countNum,删除first指向的节点
	for {
		//开始数count-1次
		for i := 1; i < countNum-1; i++ {
			first = first.next
			tail = tail.next
		}
		fmt.Printf("删除节点【%d】", first.num)
		//删除first指向的节点
		first = first.next
		tail.next = first
		//如果tail ==first,圈中只有一个节点
		if tail == first {
			break
		}
	}
	fmt.Printf("最后留下的小孩节点为【%d】", first.num)
}

func main() {
	first := AddBoy(5)
	listBoy(first)
	fmt.Println()
	playGame(first, 2, 3)
}

参考网站:https://www.bilibili.com/video/BV114411D768

posted @ 2021-03-05 09:43  天下医者不自医  阅读(93)  评论(0编辑  收藏  举报