数组和链表

数组

在golang中已经封装成了高级结构,array和slice
golang实现数组
golang实现切片

数组在内存中是一片连续的空间,每一个元素都有自己的索引对应

数组因为索引的存在,所以查找和更新的时间复杂度都是O(1)的

因为连续内存,中间不能断,所以增加和删除都可能涉及到所有元素的移动所以时间复杂度是O(n)的

数组常常和链表来比较,很多人都回答说,“链表适合插入、删除,时间复杂度 O(1);数组适合查找,查找时间复杂度为 O(1)”。。
实际上,这种表述是不准确的。数组是适合查找操作,但是查找的时间复杂度并不为 O(1)。即便是排好序的数组,你用二分查找,时间复杂度也是 O(logn)。所以,正确的表述应该是,数组支持随机访问,根据下标随机访问的时间复杂度为 O(1)。

golang中数组知识补充

1.数组是如何快速寻址的?

前面说到数组是具有连续内存地址的存储结构,每个元素都有自己的索引,所以它的寻址过程是:

	arr[i] = &arr[0]+i*size
	注:size是指的每一个的数组的size,占用了几个位,例如普通的int就是4个字节。

2.为什么大多数编程语言中,数组要从0开始编号。而不是从1开始呢?

从数组存储的内存模型上来看,“下标”最确切的定义应该是“偏移(offset)”。如果用 arr 来表示数组的首地址,arr[0]就是偏移为 0 的位置,也就是首地址,arr[k]就表示偏移 k 个 size的位置,所以计算 arr[k]的内存地址只需要用这个公式:


arr[k] = &arr[0]+k*size

但是,如果数组从 1 开始计数,那我们计算数组元素 a[k]的内存地址就会变为:

arr[k] = &arr[0]+(k-1)*size

对比两个公式,我们不难发现,从 1 开始编号,每次随机访问数组元素都多了一次减法运算,对于 CPU 来说,就是多了一次减法指令。数组作为非常基础的数据结构,通过下标随机访问数组元素又是其非常基础的编程操作,效率的优化就要尽可能做到极致。所以为了减少一次减法操作,数组选择了从 0 开始编号,而不是从 1 开始。当然还可能有一些历史原因,这里就不考虑了

3.字符串和数组的关系

字符串可以理解为一个由字符组成的数组,只不过会多一个只读的标志,从而限制字符串不能被修改

链表

在golang中没有提供现成的高级结构

链表在内存中是随机存储的,可以有效利用零散的碎片空间,每一个节点通过指针相链接。

链表元素的增加,删除是O(1)的,但查找是O(n)的。
更新操作,在不考虑查找节点的过程,是O(1)的,否则是O(n)的

链表的几种类别:

1.单向链表:每个节点有两个部分,一个存放数据变量data,另一个存放指向下一个节点的指针next
2.双向链表:每个节点有三个部分。一个存放数据变量data,一个存放指向下一个节点的指针next,多的一个是指向前置节点的prev指针
3.循环链表:它跟单链表唯一的区别就在尾结点。我们知道,单链表的尾结点指针指向空地址,表示这就是最后的结点了。而循环链表的尾结点指针是指向链表的头结点。它像一个环一样首尾相连,所以叫作“循环”链表。

golang实现单向链表(其他种类类比可得)

package main

import "fmt"

/*
 使用golang实现单链表
 add 操作:在链表首部添加
 append 操作:在链表尾部添加,从头部找到最后一个元素,把最后一个元素指针指向需要添加的元素
 insert 操作:在指定位置添加,往中间插入元素,需要调整插入该位置的元素之前的指针,还有该元素的指针需要指向下一位
 delete 操作:删除指定的元素,删除任意位置的元素,需要调整指针
 */


type Linklist interface {
	IsEmpty() bool
	Length() int
	Add(value int32)
	Append(value int32)
	Insert(index int, value int32)
	Delete(value int32)
	forEachLink() 


}

// 链表
type LinkList struct {
	HeadNode *LinkNode
}

//单向链表节点
type LinkNode struct {
	Data int32 // 链表上的数据
	Next *LinkNode // 指针指向下一个数据
}



// 判断链表是否为空,只需要判断第一个元素是否是 nil
func (self *LinkList) IsEmpty() bool  {
	if self.HeadNode == nil {
		return true
	} else {
		return false
	}
}
// 获取链表的长度,从头部循环链表,直到元素是 nil 即可
func (self *LinkList) Length() int  {
	count := 0
	node := self.HeadNode
	for node != nil {
		count += 1
		node = node.Next
	}
	return count
}
// 在头部添加数据,把原来的头部替换掉,还需要把现在的元素指向原来的头部
func (self *LinkList) Add(value int32)  {
	node := &LinkNode{Data:value,}
	node.Next=self.HeadNode
	self.HeadNode = node
}
// 在尾部添加数据,需要从头部开始遍历,直到nil
func (self *LinkList)Append(value int32)  {
	newNode := &LinkNode{Data:value}
	node := self.HeadNode
	// 首部是空
	if node == nil {
		self.HeadNode = newNode
	} else {
		for node.Next != nil {
			node = node.Next
		}
		// 已经得到最后的元素
		node.Next = newNode
	}
}
// 在指定位置insert
func (self *LinkList) Insert(index int, value int32)  {
	newNode := &LinkNode{Data:value}
	node := self.HeadNode
	if index < 0 {
		// index 小于 0 就放在首部吧
		self.Add(value)
	} else if index > self.Length() {
		// index 大于 长度 就放在尾部部吧
		self.Append(value)
	} else {
		count := 0
		// 找到 index 之前的元素
		for count < (index - 1) {
			node = node.Next
			count += 1
		}
		// 已经找到index之前的元素
		newNode.Next = node.Next
		node.Next = newNode
	}
}
// 删除指定元素,从首部遍历,找到该元素删除,并且需要维护指针
func (self *LinkList) Delete(value int32)  {
	node := self.HeadNode
	// 如果是首部
	if node.Data == value {
		self.HeadNode = node.Next
	} else {
		for node.Next != nil {
			// 找到了,改指针
			if node.Next.Data == value {
				node.Next = node.Next.Next
			} else {
				node = node.Next
			}
		}
	}
}
// 循环链表输出元素
func (self *LinkList)forEachLink()  {
	node := self.HeadNode
	for node != nil {
		fmt.Printf("当前元素 :%v\n", node.Data)
		node = node.Next
	}
}

func NewLinkList() LinkList {
	linkList := LinkList{HeadNode:nil}
	return linkList
}

func main()  {
	linkList :=NewLinkList()
	linkList.forEachLink()
	fmt.Println("=========Insert/Append后========")
	linkList.Add(0)
	linkList.Add(1)
	linkList.Append(2)
	linkList.Append(3)
	linkList.Append(5)
	linkList.Append(4)
	linkList.Append(6)
	linkList.forEachLink()
	linkList.Delete(4)
	fmt.Println("=============Delete后============")
	linkList.forEachLink()
	fmt.Println("=============Insert后============")
	linkList.Insert(5, 4)
	linkList.forEachLink()
	fmt.Println("=============链表长度============")
	fmt.Println(linkList.Length())

}


参考:
Go 数据结构和算法篇(一):链表

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值