Go基础学习06-Golang标准库container/list(双向链表)深入讲解;延迟初始化技术;Element;List;Ring

news2024/11/16 11:56:44

基础介绍

单向链表中的每个节点包含数据和指向下一个节点的指针。其特点是每个节点只知道下一个节点的位置,使得数据只能单向遍历。

示意图如下:
在这里插入图片描述

双向链表中的每个节点都包含指向前一个节点和后一个节点的指针。这使得在双向链表中可以从前向后或从后向前遍历。

示意图如下:
在这里插入图片描述

结合上面的图就很容易明白单、双链表的定义。其中双向链表可以从前向后,也可以从后向前遍历,操作起来也更加方便。

接下来我们看看官方给的例子:

import (
	"container/list"
	"fmt"
)

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。

  • List
  • Element

在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 used
	len  int     // current list length excluding (this) sentinel element
}

// Init initializes or clears list l.
func (l *List) Init() *List {
	l.root.next = &l.root
	l.root.prev = &l.root
	l.len = 0
	return 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 Node,root.prev 表示 Tail Node。注意 List.len 的长度是不包含 root 节点的。方法后,root.next 表示 Head Node,root.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 Node,root.prev 表示 Tail Node。

Go中链表的延迟初始化机制

示例代码:

	// 声明一个链表但未初始化,随后使用链表的延迟初始化技术在插入元素时进行初始化(lazyInit)
	l2 := 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.root
	l.root.prev = &l.root
	l.len = 0
	return 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 crash
		l.remove(e)
	}
	return e.Value
}
  • 对于Front方法和Back方法,只需对链表的长度进行判断即可,一旦发现链表的长度为0直接返回nil就好了。

container包中的list和ring包中的List和Ring的区别

container/ring包中的Ring类型实现的是一个循环链表,也就是我们俗称的环。其实List在内部就是一个循环链表。它的根元素永远不会持有任何实际的元素值,而该元素的存在,就是为了连接这个循环链表的首尾两端。
所以也可以说,List的零值是一个只包含了根元素,但不包含任何实际元素值的空链表。那么,既然Ring和List在本质上都是循环链表,那它们到底有什么不同呢?最主要的不同有下面几种。

  1. Ring类型的数据结构仅由它自身即可代表,而List类型则需要由它以及Element类型联合表示。这是表示方式上的不同,也是结构复杂度上的不同。
  2. 一个Ring类型的值严格来讲,只代表了其所属的循环链表中的一个元素,而一个List类型的值则代表了一个完整的链表。这是表示维度上的不同。
  3. 在创建并初始化一个Ring值的时候,我们可以指定它包含的元素的数量,但是对于一个List值来说,却不能这样做(也没有必要这样做)。循环链表一旦被创建,其长度是不可变的。这是两个代码包中的New函数在功能上的不同,也是两个类型在初始化值方面的第一个不同。
  4. 仅通过var r ring.Ring语句声明的r将会是一个长度为1的循环链表,而List类型的零值则是一个长度为0的链表。别忘了List中的根元素不会持有实际元素值,因此计算长度时不会包含它。这是两个类型在初始化值方面的第二个不同。
  5. 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 crash
		l.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 l
	return 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 l
	return 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 l
	l.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 l
	l.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 main

import (
	"container/list"
	"fmt"
	"sort"
	"strings"
	"time"
)


type Order struct {
	Price       float64
	CreatedTime 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)

		// 插入元素 newList
		tdl.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.coloradmin.cn/o/2170163.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Docker仓库搭建

目录 一、Docker Hub 二、私有Registry仓库搭建 1、下载并开启仓库镜像registry 2、Registry加密传输 3、建立一个registry仓库 4、为客户端建立证书 5、测试 6、为仓库建立登录认证 三、Harbor仓库搭建 Docker 仓库&#xff08;Docker Registry&#xff09; 是用于存…

8种数值变量的特征工程技术:利用Sklearn、Numpy和Python将数值转化为预测模型的有效特征

特征工程是机器学习流程中的关键步骤&#xff0c;在此过程中&#xff0c;原始数据被转换为更具意义的特征&#xff0c;以增强模型对数据关系的理解能力。 特征工程通常涉及对现有数据应用转换&#xff0c;以生成或修改数据&#xff0c;这些转换后的数据在机器学习和数据科学的…

书生大模型实战营学习[9] OpenCompass 评测 InternLM-1.8B 实践

准备工作 打开开发机&#xff0c;选择cuda11.7环境&#xff0c;A100选择10%&#xff0c;点击创建&#xff0c;然后进入开发机即可&#xff0c;和之前的操作一样。接下来创建环境&#xff0c;下载必要的依赖包 conda create -n opencompass python3.10 conda install pytorch2…

什么是网络安全自动化以及优势与挑战

目录 网络安全自动化的工作原理 网络安全自动化的好处 增强的安全功能 改善表现和姿势 降低安全成本 简化的安全合规性和审计 更好的端点管理 网络安全自动化的挑战 耗时且容易出错的安全流程 可见性降低&#xff0c;风险和成本增加 合规管理 有用的网络安全自动化…

2024年合肥市职业院校技能大赛(中职组)赛 网络安全 竞赛样题

2024年合肥市职业院校技能大赛(中职组)赛 网络安全 竞赛样题 (总分100分) 培训、环境、资料、考证 公众号&#xff1a;Geek极安云科 网络安全群&#xff1a;624032112 网络系统管理群&#xff1a;223627079 网络建设与运维群&#xff1a;870959784 极安云科专注于技能提升&am…

基于nodejs+vue的旅游管理系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码 精品专栏&#xff1a;Java精选实战项目…

Docker启动失败,Failed to start docker,只需三个步骤,看我怎么搞定它!

在项目部署上线的时候 1打开hyper-v虚拟机 怎么打开呢&#xff1f; 命令提示符输入control,点击回车&#xff0c;打开控制面板&#xff0c;打开“程序和功能”&#xff0c;“启用和关闭windows功能”&#xff0c;选择Hyper-v&#xff0c;勾选下面的每一项内容。完成之后又点…

机器学习-模型集成

文章目录 模型集成为什么要集成&#xff1f;模型集成要解决的问题主要的集成思想 Committees多个模型的结果进行融合。BaggingBagging 特点 BoostingAdaBoost算法过程 GBDT负梯度拟合 XGBoostXGBoost 参数通用参数booster 参数学习目标参数 模型保存 模型集成 三个臭皮匠顶一个…

结构连接能否成为精神病早期的功能预后标志?

TLDR: 该研究发现精神病首次发作的患者&#xff08;未经药物治疗&#xff09;脑部存在广泛的结构连接减少&#xff0c;尤其是前额叶-纹状体-丘脑系统的连接异常。基线时的脑部结构连接可以有效预测患者12个月后的功能恢复情况。研究表明&#xff0c;脑部结构连接的减少可能与精…

【游戏策划 还能这么玩】

长期陷入低迷的桌游圈最近重新焕发出希望的光芒。由腾讯和网易离职的策划及美术团队开发的桌游《王冠继承者之战》在一小时内众筹金额突破20万。 对于游戏策划者来说&#xff0c;不妨拓展思路&#xff0c;将目光转向尚未充分挖掘的桌游市场&#xff0c;那里可能蕴藏着让人意想…

C++ 标准模板库(STL)之集合(set)

啊&#xff0c;哈喽&#xff0c;小伙伴们大家好。我是#张亿&#xff0c;今天呐&#xff0c;学的是集合&#xff08;set&#xff09; C 标准模板库&#xff08;STL&#xff09;之集合&#xff08;set&#xff09; (下面代码中的std::要去掉) 在C标准模板库&#xff08;STL&am…

HCIP——HCIA回顾

第一章.HCIA复习 引入场景 其实IA我们主要学习的无非就是数据包在网络中传递的基本过程&#xff0c;我们设计一个场景&#xff0c;通过回顾web页面的请求过程&#xff0c;将IA学过的基本网络工作原理来串一遍。 (在本例中&#xff0c;ISP网络为学校提供了DNS服务&#xff0c;…

数据恢复新宠!2024年Top3宝藏工具

在这个离不开电脑和手机的时代&#xff0c;U盘是我们存储照片、文档、视频的得力助手。万一U盘格式化了&#xff0c;别担心&#xff0c;数据很可能能恢复。2024年流行的三种恢复方法&#xff0c;都能帮你找回丢失的数据。记住&#xff0c;格式化后数据并非真正消失&#xff0c;…

【SpringBoot详细教程】-05-整合Druid操作数据库【持续更新】

文末看往期系列教程&#xff1a;给个关注可好&#xff1f; &#x1f332; SpringData简介 对于数据访问层&#xff0c;无论是 SQL (关系型数据库) 还是 NoSql (非关系型数据库)&#xff0c;Spring Boot 底层都是采用 Spring Data 的方式进行统一处理。 Spring Boot 底层都是采…

Python实现:时间序列趋势外推法应用-龚珀兹曲线拟合

龚珀兹曲线 下表数据为某跨国公司1989-2021年的年销售量数据&#xff0c;使用适合的模型预测该公司2022年的销售额&#xff0c;并得出理由。 部分数据如下表&#xff08;具体数据从主页资源下载&#xff09;&#xff1a; 年份时序&#xff08;t&#xff09;总额&#xff08;y…

Spring JDBC及声明式事务

目录 Spring JDBC基础概念 Spring声明式事务 事务传播方式 Spring JDBC基础概念 Spring JDBC 封装了原生的JDBC API&#xff0c;使得处理关系型数据库更加简单。Spring JDBC的核心是JdbcTemplate&#xff0c;里面封装了大量数据库CRUD的操作。使用Spring JDBC…

八大核心能力铸就销售精英:解锁成功销售的密钥

成功销售&#xff0c;既是精妙绝伦的艺术展现&#xff0c;也是融汇多元技能的卓越实践。无论企业处于初创的萌芽阶段&#xff0c;还是屹立行业的巅峰之列&#xff0c;跨越销售高峰的征途上&#xff0c;销售人员所掌握的八大核心能力&#xff0c;如同星辰指引&#xff0c;不可或…

高性能、高可靠,MK SD卡让数据存储无忧!

文章目录 SD卡&#xff08;Secure Digital Memory Card&#xff09;&#xff0c;作为当代数字生活中不可或缺的存储媒介&#xff0c;凭借其卓越的数据传输效率、灵活的热插拔功能以及惊人的存储容量&#xff0c;在多个领域大放异彩。从日常使用的智能手机、平板电脑到追求极致体…

【ARM】解决ArmDS Fast Models 中部分内核无法上电的问题

【更多软件使用问题请点击亿道电子官方网站】 1、 文档目标 解决ArmDS Fast Models 中部分内核无法上电的问题。 2、 问题场景 在调用ArmDS的Fast Models中的Cortex-A55的模型&#xff0c;只有Core 0是上电状态&#xff0c;而Core 1处于掉电状态&#xff0c;如图2-1所示&…

美国林氏集团宣布全面进军Web3领域

吉隆坡&#xff0c;马来西亚——近日举行的第六界博览会上&#xff0c;美国林氏集团董事局主席林建中先生宣布&#xff0c;集团将通过旗下的大东亚银行创建一个全新的、合规的区块链交易所&#xff0c;并正式进军Web3、元宇宙及AI领域。同时&#xff0c;美国林氏集团将利用其在…