Go语言讲述数据结构之单链表

首先要知道为什么引入链表,以及什么情况下使用链表,链表有哪些缺点?

我们使用链表就是为了避免插入和删除数据时带来的开销,同时链表可以不连续(就是在内存中的地址不一定是连续的),所以对于频繁的增加和删除节点,链表是不需要进行大量的数据迁移(相对于数组),但是对于链表的访问,时间复杂度是O(n),不像数组可以根据下表以时间复杂度O(1)来访问。对于时间复杂度,可能leetcode刷的比较多的同学深有体会,时间复杂度往往决定了你的代码运行的快慢,超过了百分之多少的人,这个很有成就感的,哈哈。

我们来看一下单链表长什么样子:

有一个头指针,指向第一个元素,每个节点中包含指向下一个节点的地址(这个很重要),以此类推,最后一个节点指向为空。这就构成了一个单链表,其中节点的地址不一定是连续的。

这里提一下为什么要有头指针(Head),头指针其实也是一个节点,这个节点中可以不包含数据,但是一定要有指向首元节点的指针,试想一下,如果没有头指针,我们在a1前插入节点和删除a1就会比较麻烦,所以引入头指针对我们操作整个单链表会非常的方便。

对于go语言我们应该怎么使用代码描述一个单链表呢?

//定义一个节点
type Node struct {
	e    interface{}  //节点的数据
	next *Node        //下一个节点的地址
}

//定义一个单链表
type LinkList struct {
	head *Node       //链表的头指针
	size int         //链表的长度
}

 

对於单链表,我们来实现下面几种简单的操作(链表复杂的操作,也是以这几种为基础的)

//初始化单链表
func (L *LinkList) Init() *LinkList{}

//获取链表的长度
func (L *LinkList) GetSize() int{}

//判断链表是否为空
func (L *LinkList) IsEmpty() bool{}

//单链表插入数据
func (L *LinkList) Insert(e interface{}, index int){}

//单链表删除数据
func (L *LinkList) Delete(index int){}

//单链表查询元素
func (L *LinkList) FindEle(e interface{}) (index int ){}

下面我们来依次讲解这几种操作:

初始化链表:

初始化链表就是生成头指针,有没有数据都可以,但是一定要有地址能表示这个头指针。

//初始化链表

func (L *LinkList) Init() *LinkList{
	L.size = 0             //生成的单链表没有数据,因此size为0
	L.head = new(Node)     //我们生成一个空Node来表示
	return L
}

 

获取链表的长度:

        直接返回链表的size即可 。

func (L *LinkList) GetSize() int{
	return L.size
}

 

判断链表是否为空:

        这个也是很简单的,就不解释了

func (L *LinkList) IsEmpty() bool{
	return L.size == 0
}

 

单链表插入数据:

        单链表插入数据, 需要经过两步,一:把待插入节点的next地址指向插入位置的后一个节点;二:把待插入位置的前一个节点的next地址指向我们的待插入数据,这样就完成了一个节点的插入,觉得我说的不清楚的可以去看书本的描述。

//单链表插入数据

func (L *LinkList) Insert(e interface{}, index int){
	if index<0 || index>L.size{
		panic("out of range")          //确保插入的位置有效
	}
	preNode := L.head
	for i:=0 ; i<index;i++{
		preNode = preNode.next
	}                                  //移动到待插入位置

	curNode := &Node{e,preNode.next}   //生成插入节点,执行步骤一
	preNode.next = curNode             //执行步骤二
	L.size++                           //链表的大小加一
}

 

单链表的删除:

        我们删除一个节点,只要将这个节点的前一个节点的next地址指向这个节点的next地址,即指向下一个节点。如图所示,直接把a.next指向c即可完成b节点的删除。

func (L *LinkList) Delete(index int){
	if index<0 || index>L.size{
		panic("out of range")                  //确保删除位置有效
	}
	preNode := L.head
	for i:=0;i<index-1;i++{
		preNode = preNode.next                //移动到待删除节点的前一个节点
	}
	preNode.next = preNode.next.next  //将待删除节点的前一个节点的next指向待删除节点的后一个节点
	L.size--
}

查找元素:

        查找元素,我们采用遍历节点并判断是否相等 的方法

func (L *LinkList) FindEle(E interface{}) int{
	var index  = -1      //用来存储返回的位置,没这个数据返回-1
	preNode := L.head
	for i:=0;i<L.size;i++{    //遍历整个单链表
		if preNode.next != nil && preNode.next.e == E{    //判断链表是否遍历完,同时判断节点的值与要查的值是否相等
			index = i
			return index
		}
		preNode = preNode.next
	}
	return index
}

 

到了这里,单链表的基本操作就已经结束了,我们可以结合这些基本的操作来完成复杂的操作,比如,链表的反转,有兴趣的小伙伴可以试试哦, 觉得我写的有问题的小伙伴也可以评论我们来聊聊,共同进步呢。

 

其实在这里写博客,也算是对自己的一个监督吧,加油,争取周更。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章