当前位置: 首页 > news >正文

海西高端网站建设公司下载ps软件免费版

海西高端网站建设公司,下载ps软件免费版,小红书推广群,深圳做网站-龙华信科基础介绍 单向链表中的每个节点包含数据和指向下一个节点的指针。其特点是每个节点只知道下一个节点的位置#xff0c;使得数据只能单向遍历。 示意图如下#xff1a; 双向链表中的每个节点都包含指向前一个节点和后一个节点的指针。这使得在双向链表中可以从前向后或从后…基础介绍 单向链表中的每个节点包含数据和指向下一个节点的指针。其特点是每个节点只知道下一个节点的位置使得数据只能单向遍历。 示意图如下 双向链表中的每个节点都包含指向前一个节点和后一个节点的指针。这使得在双向链表中可以从前向后或从后向前遍历。 示意图如下 结合上面的图就很容易明白单、双链表的定义。其中双向链表可以从前向后也可以从后向前遍历操作起来也更加方便。 接下来我们看看官方给的例子 import (container/listfmt )func Example() {// Create a new list and put some numbers in it.l : list.New()e4 : l.PushBack(4)e1 : l.PushFront(1)l.InsertBefore(3, e4)l.InsertAfter(2, e1)// Iterate through list and print its contents.for e : l.Front(); e ! nil; e e.Next() {fmt.Println(e.Value)}// Output:// 1// 2// 3// 4 }首先调用list.New()创建一个双向链表然后添加元素Element最后从头遍历链表打印每个元素的值。 从上可以看出container/list提供了两个结构 List、Element。 ListElement 在Java中实现的双向链表也是这样做的只是元素一般命名为Node而已。 container/list源码分析 Element // Element is an element of a linked list. type Element struct {// Next and previous pointers in the doubly-linked list of elements.// To simplify the implementation, internally a list l is implemented// as a ring, such that l.root is both the next element of the last// list element (l.Back()) and the previous element of the first list// element (l.Front()).next, prev *Element// The list to which this element belongs.list *List// The value stored with this element.Value any }Element 一共定义了四个字段分别是指向前一个节点的 prev指向下一个节点的 next存储值的 Value以及 此元素属于哪个list。 平常自己在定义双向链表 Node 的结构的时候一般是不会有 list 这个元素的为什么官方给的有这个元素呢 Element 的 list 字段是小写的那意味着外部使用者是无法获取和定义此字段的也就是说外部使用者无法通过 Element 来操作 链表。在通篇读过源码后发现 Element.list 是用于判断插入、移动、删除等操作的元素是否属于此链表所以我认为增加 list 字段的原因主要是安全性。 比如防止在多维链表操作的时候错误的加入了不属于此链表的节点有了 list 字段后就可以做判断防止这类情况产生。对于Element的扩展字段list可以将其理解为与链表的跟节点root绑定的一个值通过Element.list可以获取这个链表从而获取root值用于一些判断主要在源码中使用有链表基础的建议查阅源码学习 Element 只有两个方法即 Next()、Prev()源代码如下 // Next returns the next list element or nil. func (e *Element) Next() *Element {if p : e.next; e.list ! nil p ! e.list.root {return p}return nil }// Prev returns the previous list element or nil. func (e *Element) Prev() *Element {if p : e.prev; e.list ! nil p ! e.list.root {return p}return nil }看到这里官方给的实现方式并不是简单的 e.prev、e.next而是多了p ! e.list.root的判断为什么会有这个判断呢 因为container/list起始是一个环形链表那么就需要有一个特殊的节点切断这种环形关系root就是用来做这个标识的节点。 这样做有什么好处呢 root 字段是链表的根节点它并不直接存储数据而是一个空节点Element 类型。这个空节点被用作链表的哨兵节点Sentinel Node或者叫做标志节点Dummy Node。 这个哨兵节点的作用是为了简化链表的操作。通过将哨兵节点作为链表的根节点在实际的链表操作中就无需考虑头节点为空的情况即空链表和非空链表的操作逻辑变得更加统一和简化。 简化逻辑: 哨兵节点的引入避免了对空链表的特殊处理。无论链表是否为空头节点哨兵节点之后的第一个节点始终存在这样在操作链表时就无需针对空链表做额外的判断。边界条件更清晰: 有了哨兵节点链表的头部和尾部都有了固定的节点作为标志使得链表操作时边界条件更加清晰。提高代码的一致性: 通过哨兵节点链表的操作逻辑更加统一减少了特殊情况下的代码分支提高了代码的一致性和可读性。 List List结构 // List represents a doubly linked list. // The zero value for List is an empty list ready to use. type List struct {root Element // sentinel list element, only root, root.prev, and root.next are usedlen int // current list length excluding (this) sentinel element }// Init initializes or clears list l. func (l *List) Init() *List {l.root.next l.rootl.root.prev l.rootl.len 0return l }// New returns an initialized list. func New() *List { return new(List).Init() }// Len returns the number of elements of list l. // The complexity is O(1). func (l *List) Len() int { return l.len }因为container/list 是一个环形链表所以只用提供一个节点就可以了。 注意刚初始化时即调用New生成的链表对象此时的 root.next、root.prev 都是指向root 自己的 。当使用 PushBack或者PushFront方法后root.next 表示 Head Noderoot.prev 表示 Tail Node。注意 List.len 的长度是不包含 root 节点的。方法后root.next 表示 Head Noderoot.prev 表示 Tail Node。注意 List.len 的长度是不包含 root 节点的。 获取头尾节点 // Front returns the first element of list l or nil if the list is empty. func (l *List) Front() *Element {if l.len 0 {return nil}return l.root.next }// Back returns the last element of list l or nil if the list is empty. func (l *List) Back() *Element {if l.len 0 {return nil}return l.root.prev }root.next 表示 Head Noderoot.prev 表示 Tail Node。 Go中链表的延迟初始化机制 示例代码 // 声明一个链表但未初始化随后使用链表的延迟初始化技术在插入元素时进行初始化lazyInitl2 : list.List{}fmt.Println(l2)l2.PushBack(3)fmt.Println(l2)运行结果 {{nil nil nil nil} 0} {{0xc0000b62a0 0xc0000b62a0 nil nil} 1}使用l2 : list.List{}声明的链表l2可以正常使用但仅仅凭借l2这个链表是无法正常使用的需要结果Go中链表的延迟初始化 机制才可以使用。 查看Go中container/list的源码中的PushBack()方法 // PushBack inserts a new element e with value v at the back of list l and returns e. func (l *List) PushBack(v any) *Element {l.lazyInit()return l.insertValue(v, l.root.prev) }// lazyInit lazily initializes a zero List value. func (l *List) lazyInit() {if l.root.next nil {l.Init()} }// Init initializes or clears list l. func (l *List) Init() *List {l.root.next l.rootl.root.prev l.rootl.len 0return l }通过上述源码可以看到lazeInit()这个函数这个函数就是链表的延迟初始化技术只有在使用到的时候才对链表进行初始化。 延迟初始化把初始化操作延后仅在实际需要的时候才进行。延迟初始化的优点在于“延后”它可以分散初始化操作带来的计算量和存储空间消耗。 例如如果我们需要集中声明非常多的大容量切片的话那么那时的 CPU 和内存空间的使用量肯定都会一个激增并且只有设法让其中的切片及其底层数组被回收内存使用量才会有所降低。 如果数组是可以被延迟初始化的那么计算量和存储空间的压力就可以被分散到实际使用它们的时候。这些数组被实际使用的时间越分散延迟初始化带来的优势就会越明显。可以对照Java中的数组的扩容以及Go中切片的扩容都是延迟化思想的体现。 延迟初始化是否有缺点以及Go是如何减小延迟初始化对链表操作的影响 延迟初始化的缺点恰恰也在于“延后”如果我在调用链表的每个方法的时候它们都需要先去判断链表是否已经被初始化那这也会是一个计算量上的浪费。在这些方法被非常频繁地调用的情况下这种浪费的影响就开始显现了程序的性能将会降低。 在Go语言链表设计中只对插入操作如PushFront方法、PushBack方法、PushBackList方法以及PushFrontList方法在执行时总是会先判断链表的状态并在必要时进行初始化一旦初始化后后续在执行这些方法的时候就无需进行初始化可以直接使用。 此外在Go语言设计中对于删除元素、移动元素以及一些用于插入元素的方法中只要判断一下传入的元素中指向所属链表的指针是否与当前链表的指针相等就可以了。如果不相等就一定说明传入的元素不是这个链表中的后续的操作就不用做了。反之就一定 说明这个链表已经被初始化了。此时使用到Element元素的list属性这个操作是内部源码调用的。 // Remove removes e from l if e is an element of list l. // It returns the element value e.Value. // The element must not be nil. func (l *List) Remove(e *Element) any {if e.list l {// if e.list l, l must have been initialized when e was inserted// in l or l nil (e is a zero Element) and l.remove will crashl.remove(e)}return e.Value }对于Front方法和Back方法只需对链表的长度进行判断即可一旦发现链表的长度为0直接返回nil就好了。 container包中的list和ring包中的List和Ring的区别 container/ring包中的Ring类型实现的是一个循环链表也就是我们俗称的环。其实List在内部就是一个循环链表。它的根元素永远不会持有任何实际的元素值而该元素的存在就是为了连接这个循环链表的首尾两端。 所以也可以说List的零值是一个只包含了根元素但不包含任何实际元素值的空链表。那么既然Ring和List在本质上都是循环链表那它们到底有什么不同呢最主要的不同有下面几种。 Ring类型的数据结构仅由它自身即可代表而List类型则需要由它以及Element类型联合表示。这是表示方式上的不同也是结构复杂度上的不同。一个Ring类型的值严格来讲只代表了其所属的循环链表中的一个元素而一个List类型的值则代表了一个完整的链表。这是表示维度上的不同。在创建并初始化一个Ring值的时候我们可以指定它包含的元素的数量但是对于一个List值来说却不能这样做也没有必要这样做。循环链表一旦被创建其长度是不可变的。这是两个代码包中的New函数在功能上的不同也是两个类型在初始化值方面的第一个不同。仅通过var r ring.Ring语句声明的r将会是一个长度为1的循环链表而List类型的零值则是一个长度为0的链表。别忘了List中的根元素不会持有实际元素值因此计算长度时不会包含它。这是两个类型在初始化值方面的第二个不同。Ring值的Len方法的算法复杂度是 O(N) 的而List值的Len方法的算法复杂度则是 O(1)的。这是两者在性能方面最显而易见的差别。其他的不同基本上都是方法方面的了。比如循环链表也有用于插入、移动或删除元素的方法不过用起来都显得更抽象一些等等。 上述内容参考极客时间Go语言核心36讲 Go语言提供的链表常用API // Remove removes e from l if e is an element of list l. // It returns the element value e.Value. // The element must not be nil. func (l *List) Remove(e *Element) any {if e.list l {// if e.list l, l must have been initialized when e was inserted// in l or l nil (e is a zero Element) and l.remove will crashl.remove(e)}return e.Value }// PushFront inserts a new element e with value v at the front of list l and returns e. func (l *List) PushFront(v any) *Element {l.lazyInit()return l.insertValue(v, l.root) }// PushBack inserts a new element e with value v at the back of list l and returns e. func (l *List) PushBack(v any) *Element {l.lazyInit()return l.insertValue(v, l.root.prev) }// InsertBefore inserts a new element e with value v immediately before mark and returns e. // If mark is not an element of l, the list is not modified. // The mark must not be nil. func (l *List) InsertBefore(v any, mark *Element) *Element {if mark.list ! l {return nil}// see comment in List.Remove about initialization of lreturn l.insertValue(v, mark.prev) }// InsertAfter inserts a new element e with value v immediately after mark and returns e. // If mark is not an element of l, the list is not modified. // The mark must not be nil. func (l *List) InsertAfter(v any, mark *Element) *Element {if mark.list ! l {return nil}// see comment in List.Remove about initialization of lreturn l.insertValue(v, mark) }// MoveToFront moves element e to the front of list l. // If e is not an element of l, the list is not modified. // The element must not be nil. func (l *List) MoveToFront(e *Element) {if e.list ! l || l.root.next e {return}// see comment in List.Remove about initialization of ll.move(e, l.root) }// MoveToBack moves element e to the back of list l. // If e is not an element of l, the list is not modified. // The element must not be nil. func (l *List) MoveToBack(e *Element) {if e.list ! l || l.root.prev e {return}// see comment in List.Remove about initialization of ll.move(e, l.root.prev) }// MoveBefore moves element e to its new position before mark. // If e or mark is not an element of l, or e mark, the list is not modified. // The element and mark must not be nil. func (l *List) MoveBefore(e, mark *Element) {if e.list ! l || e mark || mark.list ! l {return}l.move(e, mark.prev) }// MoveAfter moves element e to its new position after mark. // If e or mark is not an element of l, or e mark, the list is not modified. // The element and mark must not be nil. func (l *List) MoveAfter(e, mark *Element) {if e.list ! l || e mark || mark.list ! l {return}l.move(e, mark) }// PushBackList inserts a copy of another list at the back of list l. // The lists l and other may be the same. They must not be nil. func (l *List) PushBackList(other *List) {l.lazyInit()for i, e : other.Len(), other.Front(); i 0; i, e i-1, e.Next() {l.insertValue(e.Value, l.root.prev)} }// PushFrontList inserts a copy of another list at the front of list l. // The lists l and other may be the same. They must not be nil. func (l *List) PushFrontList(other *List) {l.lazyInit()for i, e : other.Len(), other.Back(); i 0; i, e i-1, e.Prev() {l.insertValue(e.Value, l.root)} } 贴一个其他博主使用Go中的container/list实现的一个二维链表 有了上面的基础后我们再来实战下。 需求实现一个二维链表要求第一维以价格从低到高排序第二维以时间从小到大排序。 package mainimport (container/listfmtsortstringstime )type Order struct {Price float64CreatedTime time.Time }// TwoDList 二维链表要求第一维以价格从低到高排序第二维以时间从小到大排序。 type TwoDList struct {// 索引相同,即表示价格相同,同一索引的链表节点,越靠后时间越大// 索引越大,价格越高Rows []*list.List }func NewTwoDList() *TwoDList {return TwoDList{Rows: make([]*list.List, 0),} }func (tdl *TwoDList) AddNode(price float64, createdTime time.Time) {order : Order{Price: price, CreatedTime: createdTime}// 1、index : sort.Search(len(tdl.Rows), func(i int) bool {return tdl.Rows[i].Front().Value.(*Order).Price order.Price})if index len(tdl.Rows) {// 此价格不存在 tdl 中, 新增newList : list.New()newList.PushFront(order)tdl.Rows append(tdl.Rows, newList)return}// 判断 index 处的价格是否和 order.Price 相等,// 相等, 则往链表添加// 不相等, 则需要先将 index 之后的往后移一位if order.Price ! tdl.Rows[index].Front().Value.(*Order).Price {newList : list.New()newList.PushFront(order)// 插入元素 newListtdl.Rows append(tdl.Rows[:index], append([]*list.List{newList}, tdl.Rows[index:]...)...)return}// 时间从小到大排curRow : tdl.Rows[index]insertPosition : curRow.Front()for insertPosition ! nil order.CreatedTime.After(insertPosition.Value.(*Order).CreatedTime) {insertPosition insertPosition.Next()}if insertPosition nil {curRow.PushBack(order)} else {curRow.InsertBefore(order, insertPosition)} }func (tdl *TwoDList) Print() {for i, row : range tdl.Rows {fmt.Printf(index: %d\n, i)for node : row.Front(); node ! nil; node node.Next() {order : node.Value.(*Order)fmt.Printf(order price: %f, time: %v \n, order.Price, order.CreatedTime)}fmt.Println(strings.Repeat(-, 20))} }func main() {// 创建一个新的二维链表myTwoDList : NewTwoDList()// 向二维链表添加节点myTwoDList.AddNode(100, time.Now())myTwoDList.AddNode(75, time.Now().Add(time.Hour))myTwoDList.AddNode(75, time.Now().Add(time.Hour))myTwoDList.AddNode(150, time.Now().Add(2*time.Hour))myTwoDList.AddNode(75, time.Now().Add(3*time.Hour))myTwoDList.AddNode(200, time.Now().Add(4*time.Hour))// 打印二维链表myTwoDList.Print() }参考链接 博客园-点击
http://www.tj-hxxt.cn/news/234356.html

相关文章:

  • 一个网站的上线流程网站建设所有权
  • 个人博客网站需要备案吗鱼台县建设局网站
  • 长沙网站seo按天计费wordpress评论随机
  • 网站建设 价格低Wordpress 打开xml rpc
  • 网站客户需求分析天津做网站建设公司
  • 提供常州网站优化赣州市人才网招聘信息查询信息
  • 任务一 分析电子商务网站栏目结构天津做推广的公司
  • 淘宝网站建设方式怎样自做网站
  • 企业做网站需要什么网络信息工程师
  • 台州手机端建站模板如何推广
  • 重庆万州网站建设公司电话永久免费crm客户管理系统
  • 杭州建设网站公司哪家好宁波互联网公司排名
  • php网站上传漏洞网站首页面设计
  • cms网站制作网站推广排名
  • 网站上传后打不开wordpress登陆死循环
  • 京东网站 用什么做的wordpress更改轮播图
  • 大数据分析网站东莞网站哪家好
  • 巩义网站建设公司seo的名词解释
  • 全国网站建设排名咨询公司名称大全
  • 怎么做一个免费网站网站的建设多少钱
  • 表格布局网站网站和微信公众号建设方案
  • 做电商网站前端需要什么框架保定哪家公司做网站
  • 专门做图标的网站六安市城市建设档案馆网站
  • 免费网站如何赚钱php做的网站打包成exe
  • 自助构建网站广州网站建设gzzhixun
  • 要怎样建立自己的网站坪山网站建设哪家便宜
  • 东莞做网站哪家最好衡阳网页设计
  • 台州网站建设企业网站建设设计有哪些
  • 网站设计自学要怎么制作网站
  • 网站的制作流程有哪些步骤浙江网站建设专家评价