青训营笔记3:Go语言算法基础,实现单链表

56 阅读4分钟

GO数据结构与算法

LinkedList

SingleLinkedList

链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的 。

单链表的特点:

  1. 单链表不要求逻辑上相邻的两个元素在物理位置上也相邻,因此不需要连续的存储空间。
  2. 单链表是非随机的存储结构,即不能直接找到表中某个特定的结点。查找某个特定的结点时,需要从表头开始遍历,依次查找。

//以下是使用GO语言实现单向列表算法的代码

package main
import (
	"fmt"
)
// HeroNode 定义HeroNode , 每个HeroNode 对象就是一个节点
type HeroNode struct {
	no       int
	name     string
	nickname string
	next     *HeroNode
}

// NewHeroNode 构造 // go语言没有自带的构造函数 // 返回指针,以方便调用

func NewHeroNode(no int, name string, nickname string) *HeroNode {
	return &HeroNode{
		no,
		name,
		nickname,
		nil,
	}
}

//重写String方法,以便输出时依照格式

func (node *HeroNode) String() string {
	return fmt.Sprintf("HeroNode [no=%d\t\tname=%s\tnickname=%s]", node.no, node.name, node.nickname)
}

// SingleLinkedList 定义SingleLinkedList 管理
type SingleLinkedList struct {
	head *HeroNode //定义一个头指针
}

// add

func (s *SingleLinkedList) add(heroNode *HeroNode) {
	temp := s.head         //临时指针指向链表头部
	for temp.next != nil { //临时指针没有指向链表尾部时
		temp = temp.next //后移指针
	}
	temp.next = heroNode
}

// addByOrder 按排名添加

func (s *SingleLinkedList) addByOrder(heroNode *HeroNode) {
	temp := s.head
	flag := false
	for {
		if temp.next == nil {
			break // temp在末尾
		}
		if temp.next.no > heroNode.no {
			break //位置找到在temp后面插入
		} else if temp.next.no == heroNode.no {
			flag = true // 编号存在
			break//退出
		}
		temp = temp.next//后移
	}
	if flag {
		fmt.Printf("准备插入的编号 %d 已经存在了,不能加入\n", heroNode.no)
	} else {
		heroNode.next = temp.next
		temp.next = heroNode
	}
}

// reverseStackPrint,运用stack特性 //由于go语言没有stack的官方包,我们可以通过切片模拟 //或者导入外部包,可从github上找到

func (s *SingleLinkedList) reverseStackPrint() {
	if s.head.next == nil {
		fmt.Println("空链表")
		return
	}
   
	stack := make([]*HeroNode, 0)
	cur := s.head.next
	//入栈
	for cur != nil {
		stack = append(stack, cur)
		cur = cur.next
	}
	for len(stack) > 0 {
		top := stack[len(stack)-1] //栈顶
		stack = stack[:len(stack)-1]
		fmt.Println(top)
	}
}

// update 修改节点的信息, 根据no编号来修改

func (s *SingleLinkedList) update(newHeroNode *HeroNode) {
	if s.head.next == nil {
		fmt.Println("链表为空")
		return
	}
	temp := s.head.next
	flag := false
	for temp != nil {
		if temp.no == newHeroNode.no {
			flag = true
			break
		}
		temp = temp.next
	}
	if flag {
		temp.name = newHeroNode.name
		temp.nickname = newHeroNode.nickname
	} else {
		fmt.Println("没有找到节点")
	}
}

// del del单向列表信息的功能

func (s *SingleLinkedList) del(no int) {
	temp := s.head
	flag := false
	for {
		if temp.next == nil {
			break //链表最后
		}
		if temp.next.no == no {
			flag = true//改变flag
			break
		}
		temp = temp.next
	}
	if flag {
		temp.next = temp.next.next
	} else {
		fmt.Printf("没找到要删除的节点%d\n", no)
	}
}

// getLength 获取链表节点个数

func (s *SingleLinkedList) getLength() int {
	if s.head.next == nil {
		return 0
	}
	var length = 0
	cur := s.head.next
	for cur != nil {
		length++
		cur = cur.next
	}
	return length
}

// findLastIndexNode 找倒数的index

func (s *SingleLinkedList) findLastIndexNode(index int) *HeroNode {
	//链表空返回nil
	if s.head.next == nil {
		return nil
	}
	size := s.getLength()
	if index <= 0 || index > size {
		return nil
	}
	cur := s.head.next
	for i := 0; i < size-index; i++ {
		cur = cur.next
	}
	return cur
}

// reverseList 链表倒置 //1. next = cur.next: 我们先暂时保存当前节点 cur 的下一个节点, // 因为在修改 cur.next 指针之后,会失去对下一个节点的引用。 //2. cur.next = reverseHead.next: 将当前节点 cur 的下一个节点指向新链表的最前端。 // 也就是将 curnext 指针指向 reverseHead.next,这样当前节点就成为了新链表的第一个节点。 //3. reverseHead.next = cur: 将当前节点 cur 连接到新链表上。 // 我们将 cur 插入到 reverseHead 的后面,使其成为新链表的头部。 //4. cur = next: 将 cur 后移,指向原链表中保存的下一个节点。 // 这样我们可以继续处理下一个节点,重复以上步骤,直到链表全部反转完成。

func (s *SingleLinkedList) reverseList() {
	if s.head.next == nil || s.head.next.next == nil {
		return
	}
	cur := s.head.next
	var next *HeroNode                    //指向cur当前节点的下一个节点
	reverseHead := NewHeroNode(0, "", "") //新链表
	
	for cur != nil {
		next = cur.next             //保存当前节点下一个节点
		cur.next = reverseHead.next //cur下一个节点指向新链表最前端
		reverseHead.next = cur      //cur链接在新链表上
		cur = next                  //cur后移
	}
	//head.next指向reverseHead.next从而反转
	s.head.next = reverseHead.next
}

// list 显示链表[遍历]

func (s *SingleLinkedList) list() {
	if s.head.next == nil {
		fmt.Println("链表为空")
		return
	}
	temp := s.head.next
	for temp != nil {
		fmt.Println(temp)
		temp = temp.next
	}
}
func main() {
	// 初始化一个头节点
	head := &HeroNode{}
	singleLinkedList := &SingleLinkedList{
		head: head,
	}

	hero1 := NewHeroNode(1, "钟离", "盐王")
	hero2 := NewHeroNode(2, "windy", "卖唱的")
	hero3 := NewHeroNode(3, "雷军", "不会做饭的")
	hero4 := NewHeroNode(4, "小狗", "小猫")

	singleLinkedList.addByOrder(hero1)
	singleLinkedList.addByOrder(hero3)
	singleLinkedList.addByOrder(hero2)
	singleLinkedList.addByOrder(hero4)
	singleLinkedList.list()

	fmt.Println("--------------------------update修改后--------------------------")
	heroupdate := NewHeroNode(4, "小草神", "你好")
	singleLinkedList.update(heroupdate)
	singleLinkedList.list()

	fmt.Println("--------------------------del删除后------------------------------")
	singleLinkedList.del(2)
	singleLinkedList.list()

	fmt.Println("--------------------------节点长度--------------------······------")
	singleLinkedList.addByOrder(NewHeroNode(2, "温迪", "卖唱的"))
	singleLinkedList.list()
	num := singleLinkedList.getLength()
	fmt.Println("链表节点个数", num)

	fmt.Println("------------------------倒序索引实例-------------------------------")
	fmt.Println(singleLinkedList.findLastIndexNode(1))
	fmt.Println(singleLinkedList.findLastIndexNode(2))
	fmt.Println(singleLinkedList.findLastIndexNode(3))
	fmt.Println(singleLinkedList.findLastIndexNode(4))

	fmt.Println("------------------------单链表的反转-------------------------------")
	singleLinkedList.reverseList()
	singleLinkedList.list()

	fmt.Println("------------------------栈的反转-----------------------------------")
	singleLinkedList.reverseStackPrint()
}


image.png