百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 文章教程 > 正文

go语言的循环双链表Ring

xsobi 2024-11-24 00:28 1 浏览


go作为类C语言,自己的库里面包括了循环双链表的实现

/*
 * 1. go语言的循环双向链表;
 * 2. 就是每个元素存在两个指针,指向前驱和后继,外加一个链表节点的值Value
 * 3. 这个值采用的是interface{}, 就是类似于C语言中的指针,随便什么类型都行
 * 4. 链表是没有头、也没有尾部的,随便从某个节点都可以遍历
 */
package ring

// A Ring is an element of a circular list, or ring.
// Rings do not have a beginning or end; a pointer to any ring element
// serves as reference to the entire ring. Empty rings are represented
// as nil Ring pointers. The zero value for a Ring is a one-element
// ring with a nil Value.
//
type Ring struct {
	next *Ring		// 后继
	prev *Ring		// 前驱
	Value      interface{} // for use by client; untouched by this library
						   // 链表挂着的节点值,interface{}即任何类型都可以
}

// 初始化,next和prev为本身
func (r *Ring) init() *Ring {
	r.next = r
	r.prev = r
	return r
}

// Next returns the next ring element. r must not be empty.
// 返回下一个元素,如果发现下一个元素指针为空,则返回本身
func (r *Ring) Next() *Ring {
	if r.next == nil {
		return r.init()
	}
	return r.next
}

// Prev returns the previous ring element. r must not be empty.
// 返回上一个元素,如果发现上一个元素指针为空,则返回本身
func (r *Ring) Prev() *Ring {
	if r.next == nil {
		return r.init()
	}
	return r.prev
}

// Move moves n % r.Len() elements backward (n < 0) or forward (n >= 0)
// in the ring and returns that ring element. r must not be empty.
// 当前指针移动n个元素,返回移动后的结果
// 如果这个n的绝对值过大,则实际移动到 n % r.len()元素,但是依然会遍历n次,因为r.len()只能通过遍历知道
func (r *Ring) Move(n int) *Ring {
	// 如果Ring的下一个为nil,则返回本身
	if r.next == nil {
		return r.init()
	}
	switch {
	case n < 0:
		// 如果n为负数,则往prev方向移动,并返回移动的结果
		for ; n < 0; n++ {
			r = r.prev
		}
	case n > 0:
		// 如果n为正数,则往next方向移动
		for ; n > 0; n-- {
			r = r.next
		}
	}
	return r
}

// New creates a ring of n elements.
// 产生n个元素的Ring对象
// n <=0, 则返回nil
func New(n int) *Ring {
	if n <= 0 {
		return nil
	}
	// 产生第一个Ring对象
	r := new(Ring)
	p := r
	// 产生n-1个对象,并赋予值
	for i := 1; i < n; i++ {
		p.next = &Ring{prev: p} // 当前的next指向新的Ring,新Ring的prev为当前的地址
		p = p.next
	}
	
	// 最后一个Ring对象的next指向第一个
	// 第一个的prev指向最后一个
	p.next = r
	r.prev = p
	return r
}

// Link connects ring r with ring s such that r.Next()
// becomes s and returns the original value for r.Next().
// r must not be empty.
//
// If r and s point to the same ring, linking
// them removes the elements between r and s from the ring.
// The removed elements form a subring and the result is a
// reference to that subring (if no elements were removed,
// the result is still the original value for r.Next(),
// and not nil).
//
// If r and s point to different rings, linking
// them creates a single ring with the elements of s inserted
// after r. The result points to the element following the
// last element of s after insertion.
//

/**
 * 连接两个循环链表:
 * r: A0 A1 A2
 * s: B0 B1 B2 B3
 * rs = r.Link(s): A1 A2 A0 [B0 B1 B2 B3]
 *  连接实际上是将s的元素放入r的第一个元素(A0)和第二个元素A1之间,再返回第二个元素A1(rs)为头进行遍历
 * 1)A0 [B0 B1 B2 B3] A1 A2
 * 2) 以A1为头,进行遍历,A1 A2 A0 [B0 B1 B2 B3]
 * 假设s只有一个元素,即[B0],那么r.Link(s): A1 A2 A0 [B0],在以r为头进行遍历,得出A0 B0 A1 A2,
 */
func (r *Ring) Link(s *Ring) *Ring {
	n := r.Next()
	if s != nil {
		p := s.Prev()
		// Note: Cannot use multiple assignment because
		// evaluation order of LHS is not specified.
		r.next = s
		s.prev = r
		n.prev = p
		p.next = n
	}
	return n
}

// Unlink removes n % r.Len() elements from the ring r, starting
// at r.Next(). If n % r.Len() == 0, r remains unchanged.
// The result is the removed subring. r must not be empty.
//

// 假设r: A0 A1 A2 A3 A4 A5,  n = 3
// r.Move(n+1) 为A4的位置
// 将A0与A4连接,即A0 [A1 A2 A3] A4 A5,[]内的元素被删除掉,即A0 A4 A5
func (r *Ring) Unlink(n int) *Ring {
	if n <= 0 {
		return nil
	}
	return r.Link(r.Move(n + 1))
}

// 获取Ring的长度,其实也就是遍历
func (r *Ring) Len() int {
	n := 0
	if r != nil {
		n = 1
		for p := r.Next(); p != r; p = p.next {
			n++
		}
	}
	return n
}

// 处理循环链表的每个元素,实际上就是一种遍历
// 传入一个Callback即可
// 注意,这里只能做类似读操作,不能进行修改删除节点操作
func (r *Ring) Do(f func(interface{})) {
	if r != nil {
		f(r.Value)
		for p := r.Next(); p != r; p = p.next {
			f(p.Value)
		}
	}
}


简单的测试代码,用来测Link接口

package main
import (
    "fmt"
    "container/ring"
)

func main() {
   
    r := ring.New(3)
    
    var p *ring.Ring
    p = r
    for i := 0; i < r.Len(); i++ {
       p.Value = fmt.Sprintf("%s_%d", "A", i)
       p = p.Next()
    }
    
    s := ring.New(4)
    p = s
    for j := 0; j < s.Len(); j++ {
        p.Value = fmt.Sprintf("%s_%d", "B", j)
        p = p.Next()
    }
    rs :=  r.Link(s)
    p = rs
    for i := 0; i < rs.Len(); i++ {
        fmt.Println(p.Value)
        p = p.Next()
    }

}


当然,也可以参考go语言自己的源码src/container/ring/ring_test.go等文件。

相关推荐

好用的云函数!后端低代码接口开发,零基础编写API接口

前言在开发项目过程中,经常需要用到API接口,实现对数据库的CURD等操作。不管你是专业的PHP开发工程师,还是客户端开发工程师,或者是不懂编程但懂得数据库SQL查询,又或者是完全不太懂技术的人,通过...

快速上手:Windows 平台上 cURL 命令的使用方法

在工作流程中,为了快速验证API接口有效性,团队成员经常转向直接执行cURL命令的方法。这种做法不仅节省时间,而且促进了团队效率的提升。对于使用Windows系统的用户来说,这里有一套详细...

使用 Golang net/http 包:基础入门与实战

简介Go的net/http包是构建HTTP服务的核心库,功能强大且易于使用。它提供了基本的HTTP客户端和服务端支持,可以快速构建RESTAPI、Web应用等服务。本文将介绍ne...

#小白接口# 使用云函数,人人都能编写和发布自己的API接口

你只需编写简单的云函数,就可以实现自己的业务逻辑,发布后就可以生成自己的接口给客户端调用。果创云支持对云函数进行在线接口编程,进入开放平台我的接口-在线接口编程,设计一个新接口,设计和配置好接口参...

极度精神分裂:我家没有墙面开关,但我虚拟出来了一系列开关

本内容来源于@什么值得买APP,观点仅代表作者本人|作者:iN在之前和大家说过,在iN的家里是没有墙面开关的。...

window使用curl命令的注意事项 curl命令用法

cmd-使用curl命令的注意点前言最近在cmd中使用curl命令来测试restapi,发现有不少问题,这里记录一下。在cmd中使用curl命令的注意事项json不能由单引号包括起来json...

Linux 系统curl命令使用详解 linuxctrl

curl是一个强大的命令行工具,用于在Linux系统中进行数据传输。它支持多种协议,包括HTTP、HTTPS、FTP等,用于下载或上传数据,执行Web请求等。curl命令的常见用法和解...

Tornado 入门:初学者指南 tornados

Tornado是一个功能强大的PythonWeb框架和异步网络库。它最初是为了处理实时Web服务中的数千个同时连接而开发的。它独特的Web服务器和框架功能组合使其成为开发高性能Web...

PHP Curl的简单使用 php curl formdata

本文写给刚入PHP坑不久的新手们,作为工具文档,方便用时查阅。CURL是一个非常强大的开源库,它支持很多种协议,例如,HTTP、HTTPS、FTP、TELENT等。日常开发中,我们经常会需要用到cur...

Rust 服务器、服务和应用程序:7 Rust 中的服务器端 Web 应用简介

本章涵盖使用Actix提供静态网页...

我给 Apache 顶级项目提了个 Bug apache顶级项目有哪些

这篇文章记录了给Apache顶级项目-分库分表中间件ShardingSphere提交Bug的历程。说实话,这是一次比较曲折的Bug跟踪之旅。10月28日,我们在GitHub上提...

linux文件下载、服务器交互(curl)

基础环境curl命令描述...

curl简单使用 curl sh

1.curl--help#查看关键字2.curl-A“(添加user-agent<name>SendUser-Agent<name>toserver)”...

常用linux命令:curl 常用linux命令大全

//获取网页内容//不加任何选项使用curl时,默认会发送GET请求来获取内容到标准输出$curlhttp://www.baidu.com//输出<!DOCTYPEh...

三十七,Web渗透提高班之hack the box在线靶场注册及入门知识

一.注册hacktheboxHackTheBox是一个在线平台,允许测试您的渗透技能和代码,并与其他类似兴趣的成员交流想法和方法。它包含一些不断更新的挑战,并且模拟真实场景,其风格更倾向于CT...