• 数据结构之链表



    阅读目录

    一、链表介绍

    二、单链表

    三、双链表

    四、环形链表

    一、链表介绍

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

    二、单链表

    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
    	}
    }
    
  • 相关阅读:
    实验6.1
    SOA
    python的镜像包安装
    中文分词:双向匹配最大算法(BI-MM)
    从github中获取代码
    解决文件冲突
    创建分支
    上传本地文件到github
    mysql事务
    查询练习2
  • 原文地址:https://www.cnblogs.com/zhangliang91/p/11709201.html
Copyright © 2020-2023  润新知