数据结构之链表


阅读目录

一、链表介绍

二、单链表

三、双链表

四、环形链表

一、链表介绍

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

二、单链表

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】小孩出圈
", first.no)
		// 删除first
		first = first.next //走到下一步
		tail.next = first  //断开连接原始first
	}
}
原文地址:https://www.cnblogs.com/zhangliang91/p/11709201.html