本篇文章我们用go来实现红黑树,部分参考资料如下:

1.节点定义

type RBNode struct {
color uint8
key Type
left, right, parent *RBNode
} type RBRoot struct {
node *RBNode
}

2.树的遍历

// 前序遍历
func PreTraverse(p *RBNode) {
if p != nil {
fmt.Printf("%d ", p.key)
PreTraverse(p.left)
PreTraverse(p.right)
}
} // 中序遍历
func InTraverse(p *RBNode) {
if p != nil {
InTraverse(p.left)
fmt.Printf("%d ", p.key)
InTraverse(p.right)
}
} // 前序遍历
func PostTraverse(p *RBNode) {
if p != nil {
PostTraverse(p.left)
PostTraverse(p.right)
fmt.Printf("%d ", p.key)
}
}

3.树的旋转

/*
* 对红黑树的节点(x)进行左旋转
情况1:
* x y
* / \ --(左旋)--> / \
* lx y x ry
* / \ / \
* ly ry lx ly
情况2:
* px px
* / /
* x y
* / \ --(左旋)--> / \
* lx y x ry
* / \ / \
* ly ry lx ly
情况3:
* px px
* \ \
* x y
* / \ --(左旋)--> / \
* lx y x ry
* / \ / \
* ly ry lx ly
*
*/
func left_rotate(root *RBRoot, x *RBNode) {
var y *RBNode = x.right // ly 和 x 的关系
x.right = y.left
if y.left != nil {
y.left.parent = x
} // px 和 y 的关系(要考虑px为空,即x为根节点的情况)
y.parent = x.parent
if x.parent == nil {
root.node = y
} else {
if x.parent.left == x {
x.parent.left = y
} else {
x.parent.right = y
}
} // y 和 x 的关系
y.left = x
x.parent = y } /*
* 对红黑树的节点(y)进行右旋转
* 情况1:
* y x
* / \ --(右旋)--> / \
* x ry lx y
* / \ / \
* lx rx rx ry
* 情况2:
* py py
* / /
* y x
* / \ --(右旋)--> / \
* x ry lx y
* / \ / \
* lx rx rx ry
* 情况3:
* py py
* \ \
* y x
* / \ --(右旋)--> / \
* x ry lx y
* / \ / \
* lx rx rx ry
*/
func right_rotate(root *RBRoot, y *RBNode) {
var x *RBNode = y.left // rx 和 y 的关系
y.left = x.right
if x.right != nil {
x.right.parent = y
} // py 和 x 的关系(要考虑py为空,即y为根节点的情况)
x.parent = y.parent
if y.parent == nil {
root.node = x
} else {
if y.parent.right == y {
y.parent.right = x
} else {
y.parent.left = x
}
} // y 和 x 的关系
x.right = y
y.parent = x
}

4.添加节点

// 添加节点:将节点(node)插入到红黑树中
func Add(root *RBRoot, node *RBNode) {
var y *RBNode
var x *RBNode = root.node // 找到node应插入位置的父节点
for x != nil {
y = x
if node.key < x.key {
x = x.left
} else {
x = x.right
}
}
// 设置node和父节点的关系
node.parent = y
if y != nil {
if node.key < y.key {
y.left = node
} else {
y.right = node
}
} else {
root.node = node
}
// 设置节点为红色
node.color = RED
// 修正为红黑树
add_fixup(root, node) } // 红黑树插入修正
func add_fixup(root *RBRoot, node *RBNode) {
var parent, gparent *RBNode // 若“父节点存在,并且父节点的颜色是红色”
for parent=node.parent; parent != nil && rb_is_red(parent); {
gparent = parent.parent //若“父节点”是“祖父节点的左孩子”
if parent == gparent.left {
var uncle *RBNode = gparent.right
// Case 1条件:叔叔节点是红色
if uncle != nil && rb_is_red(uncle) {
rb_set_black(uncle)
rb_set_black(parent)
rb_set_red(gparent)
node = gparent
continue
}
// Case 2条件:叔叔是黑色,且当前节点是右孩子
if parent.right == node {
left_rotate(root, parent)
var tmp *RBNode = parent
parent = node
node = tmp
}
// Case 3条件:叔叔是黑色,且当前节点是左孩子。
rb_set_black(parent)
rb_set_red(gparent)
right_rotate(root, gparent)
} else {
//若“父节点”是“祖父节点的右孩子” var uncle *RBNode = gparent.left
// Case 1条件:叔叔节点是红色
if uncle != nil && rb_is_red(uncle) {
rb_set_black(uncle)
rb_set_black(parent)
rb_set_red(gparent)
node = gparent
continue
}
// Case 2条件:叔叔是黑色,且当前节点是左孩子
if parent.left == node {
right_rotate(root, parent)
var tmp *RBNode = parent
parent = node
node = tmp
}
// Case 3条件:叔叔是黑色,且当前节点是右孩子。
rb_set_black(parent)
rb_set_red(gparent)
left_rotate(root, gparent)
}
}
// 将根节点设为黑色
rb_set_black(root.node)
}

5.删除节点

func Delete(root *RBRoot, node *RBNode) {
var child, parent *RBNode
var color uint8 // 被删除节点的"左右孩子都不为空"的情况。
if node.left != nil && node.right != nil {
// 获取后继节点
var replace *RBNode = node.right
for replace.left != nil {
replace = replace.left
} // "node节点"不是根节点
if node.parent != nil {
if node.parent.left == node {
node.parent.left = replace
} else {
node.parent.right = replace
}
} else {
// "node节点"是根节点,更新根节点。
root.node = replace
}
// child是"取代节点"的右孩子,也是需要"调整的节点"。
// "取代节点"肯定不存在左孩子!因为它是一个后继节点。
child = replace.right
parent = replace.parent
// 保存"取代节点"的颜色(注意这里删掉的是node节点,但实际删掉的颜色是replace的)
color = replace.color // "被删除节点"是"它的后继节点的父节点"
if parent == node {
parent = replace
} else {
// child不为空
if child != nil {
child.parent = parent
}
parent.left = child replace.right = node.right
node.right.parent = replace
} replace.parent = node.parent
replace.color = node.color
replace.left = node.left
node.left.parent = replace if color == BLACK {
delete_fixup(root, child, parent)
} return
} if node.left != nil {
child = node.left
} else {
child = node.right
} parent = node.parent
color = node.color // 保存"取代节点"的颜色 if child != nil {
child.parent = parent
} // "node节点"不是根节点
if parent != nil {
if parent.left == node {
parent.left = child
} else {
parent.right = child
}
} else {
root.node = child
} if color == BLACK {
delete_fixup(root, child, parent)
} } func delete_fixup(root *RBRoot, node *RBNode, parent *RBNode) {
var other *RBNode for (node == nil || rb_is_black(node)) && node != root.node {
// node是父节点的左孩子
if parent.left == node {
other = parent.right
// Case 1: node的兄弟节点是红色的
if rb_is_red(other) {
rb_set_black(other)
rb_set_red(parent)
left_rotate(root, parent)
other = parent.right
}
// Case 2: node的兄弟w是黑色,且w的俩个孩子也都是黑色的
if (other.left == nil || rb_is_black(other.left)) && (other.right == nil || rb_is_black(other.right)) {
rb_set_red(other)
node = parent
parent = node.parent
} else {
// Case 3: node的兄弟w是黑色的,并且w的左孩子是红色,右孩子为黑色。
if other.right == nil || rb_is_black(other.right) {
rb_set_black(other.left)
rb_set_red(other)
right_rotate(root, other)
other = parent.right
}
// Case 4: node的兄弟w是黑色的;并且w的右孩子是红色的,左孩子任意颜色。
other.color = parent.color
rb_set_black(parent)
rb_set_black(other.right)
left_rotate(root, parent)
node = root.node
break
}
} else {
other = parent.left
// Case 1: node的兄弟w是红色的
if rb_is_red(other) {
rb_set_black(other)
rb_set_red(parent)
right_rotate(root, parent)
other = parent.left
}
// Case 2: node的兄弟w是黑色,且w的俩个孩子也都是黑色的
if (other.left == nil || rb_is_black(other.left)) && (other.right == nil || rb_is_black(other.right)) {
rb_set_red(other)
node = parent
parent = node.parent
} else {
// Case 3: node的兄弟w是黑色的,并且w的左孩子是红色,右孩子为黑色。
if other.left == nil || rb_is_black(other.left) {
rb_set_black(other.right)
rb_set_red(other)
left_rotate(root, other)
other = parent.left
}
// Case 4: node的兄弟w是黑色的;并且w的右孩子是红色的,左孩子任意颜色。
other.color = parent.color
rb_set_black(parent)
rb_set_black(other.left)
right_rotate(root, parent)
node = root.node
break
}
}
} if node != nil {
rb_set_black(node)
}
}

6.完整代码

package main

import "fmt"

const RED  = 0
const BLACK = 1 type Type int type RBNode struct {
color uint8
key Type
left, right, parent *RBNode
} type RBRoot struct {
node *RBNode
} func rb_is_red(node *RBNode) bool {
return node.color == RED
} func rb_is_black(node *RBNode) bool {
return node.color == BLACK
} func rb_set_red(node *RBNode) {
node.color = RED
} func rb_set_black(node *RBNode) {
node.color = BLACK
} // 前序遍历
func PreTraverse(p *RBNode) {
if p != nil {
fmt.Printf("%d ", p.key)
PreTraverse(p.left)
PreTraverse(p.right)
}
} // 中序遍历
func InTraverse(p *RBNode) {
if p != nil {
InTraverse(p.left)
fmt.Printf("%d ", p.key)
InTraverse(p.right)
}
} // 前序遍历
func PostTraverse(p *RBNode) {
if p != nil {
PostTraverse(p.left)
PostTraverse(p.right)
fmt.Printf("%d ", p.key)
}
} // 查找键值为key的节点
func Find(node *RBNode, key Type) *RBNode {
for node != nil && node.key != key {
if key < node.key {
node = node.left
} else {
node = node.right
}
}
return node
} // 打印红黑树
func Print(node *RBNode, key Type, direction int) {
if node != nil {
if direction == 0 {
fmt.Printf("%2d(B) is root\n", node.key)
} else {
var color, _direction string
if rb_is_red(node) {
color = "R"
} else {
color = "B"
}
if direction == 1 {
_direction = "right"
} else {
_direction = "left"
}
// TODO: 这里key 和 %d 的关系
fmt.Printf("%2d(%s) is %2d's %6s child\n", node.key, color, key, _direction)
}
Print(node.left, node.key, -1)
Print(node.right, node.key, 1)
}
} // 打印根节点的所有路径(检测两条红黑树特性)
func PrintRoute(node *RBNode) {
if node == nil {
return
}
if node.left == nil && node.right == nil {
var tmp *RBNode = node
var num int
for tmp != nil {
var color string
if rb_is_red(tmp) {
color = "R"
} else {
color = "B"
num++
}
fmt.Printf("%2d(%s)-->", tmp.key, color)
if tmp.parent != nil && (tmp.color == RED && tmp.parent.color == RED) {
fmt.Println("检测到违反红黑树特性:红节点的子节点是红节点")
}
tmp = tmp.parent
}
fmt.Printf("共 %d 个黑节点\n", num)
}
PrintRoute(node.left)
PrintRoute(node.right)
} /*
* 对红黑树的节点(x)进行左旋转
情况1:
* x y
* / \ --(左旋)--> / \
* lx y x ry
* / \ / \
* ly ry lx ly
情况2:
* px px
* / /
* x y
* / \ --(左旋)--> / \
* lx y x ry
* / \ / \
* ly ry lx ly
情况3:
* px px
* \ \
* x y
* / \ --(左旋)--> / \
* lx y x ry
* / \ / \
* ly ry lx ly
*
*/
func left_rotate(root *RBRoot, x *RBNode) {
var y *RBNode = x.right // ly 和 x 的关系
x.right = y.left
if y.left != nil {
y.left.parent = x
} // px 和 y 的关系(要考虑px为空,即x为根节点的情况)
y.parent = x.parent
if x.parent == nil {
root.node = y
} else {
if x.parent.left == x {
x.parent.left = y
} else {
x.parent.right = y
}
} // y 和 x 的关系
y.left = x
x.parent = y } /*
* 对红黑树的节点(y)进行右旋转
* 情况1:
* y x
* / \ --(右旋)--> / \
* x ry lx y
* / \ / \
* lx rx rx ry
* 情况2:
* py py
* / /
* y x
* / \ --(右旋)--> / \
* x ry lx y
* / \ / \
* lx rx rx ry
* 情况3:
* py py
* \ \
* y x
* / \ --(右旋)--> / \
* x ry lx y
* / \ / \
* lx rx rx ry
*/
func right_rotate(root *RBRoot, y *RBNode) {
var x *RBNode = y.left // rx 和 y 的关系
y.left = x.right
if x.right != nil {
x.right.parent = y
} // py 和 x 的关系(要考虑py为空,即y为根节点的情况)
x.parent = y.parent
if y.parent == nil {
root.node = x
} else {
if y.parent.right == y {
y.parent.right = x
} else {
y.parent.left = x
}
} // y 和 x 的关系
x.right = y
y.parent = x
} // 添加节点:将节点(node)插入到红黑树中
func Add(root *RBRoot, node *RBNode) {
var y *RBNode
var x *RBNode = root.node // 找到node应插入位置的父节点
for x != nil {
y = x
if node.key < x.key {
x = x.left
} else {
x = x.right
}
}
// 设置node和父节点的关系
node.parent = y
if y != nil {
if node.key < y.key {
y.left = node
} else {
y.right = node
}
} else {
root.node = node
}
// 设置节点为红色
node.color = RED
// 修正为红黑树
add_fixup(root, node) } // 红黑树插入修正
func add_fixup(root *RBRoot, node *RBNode) {
var parent, gparent *RBNode // 若“父节点存在,并且父节点的颜色是红色”
for parent=node.parent; parent != nil && rb_is_red(parent); {
gparent = parent.parent //若“父节点”是“祖父节点的左孩子”
if parent == gparent.left {
var uncle *RBNode = gparent.right
// Case 1条件:叔叔节点是红色
if uncle != nil && rb_is_red(uncle) {
rb_set_black(uncle)
rb_set_black(parent)
rb_set_red(gparent)
node = gparent
continue
}
// Case 2条件:叔叔是黑色,且当前节点是右孩子
if parent.right == node {
left_rotate(root, parent)
var tmp *RBNode = parent
parent = node
node = tmp
}
// Case 3条件:叔叔是黑色,且当前节点是左孩子。
rb_set_black(parent)
rb_set_red(gparent)
right_rotate(root, gparent)
} else {
//若“父节点”是“祖父节点的右孩子” var uncle *RBNode = gparent.left
// Case 1条件:叔叔节点是红色
if uncle != nil && rb_is_red(uncle) {
rb_set_black(uncle)
rb_set_black(parent)
rb_set_red(gparent)
node = gparent
continue
}
// Case 2条件:叔叔是黑色,且当前节点是左孩子
if parent.left == node {
right_rotate(root, parent)
var tmp *RBNode = parent
parent = node
node = tmp
}
// Case 3条件:叔叔是黑色,且当前节点是右孩子。
rb_set_black(parent)
rb_set_red(gparent)
left_rotate(root, gparent)
}
}
// 将根节点设为黑色
rb_set_black(root.node)
} func Delete(root *RBRoot, node *RBNode) {
var child, parent *RBNode
var color uint8 // 被删除节点的"左右孩子都不为空"的情况。
if node.left != nil && node.right != nil {
// 获取后继节点
var replace *RBNode = node.right
for replace.left != nil {
replace = replace.left
} // "node节点"不是根节点
if node.parent != nil {
if node.parent.left == node {
node.parent.left = replace
} else {
node.parent.right = replace
}
} else {
// "node节点"是根节点,更新根节点。
root.node = replace
}
// child是"取代节点"的右孩子,也是需要"调整的节点"。
// "取代节点"肯定不存在左孩子!因为它是一个后继节点。
child = replace.right
parent = replace.parent
// 保存"取代节点"的颜色(注意这里删掉的是node节点,但实际删掉的颜色是replace的)
color = replace.color // "被删除节点"是"它的后继节点的父节点"
if parent == node {
parent = replace
} else {
// child不为空
if child != nil {
child.parent = parent
}
parent.left = child replace.right = node.right
node.right.parent = replace
} replace.parent = node.parent
replace.color = node.color
replace.left = node.left
node.left.parent = replace if color == BLACK {
delete_fixup(root, child, parent)
} return
} if node.left != nil {
child = node.left
} else {
child = node.right
} parent = node.parent
color = node.color // 保存"取代节点"的颜色 if child != nil {
child.parent = parent
} // "node节点"不是根节点
if parent != nil {
if parent.left == node {
parent.left = child
} else {
parent.right = child
}
} else {
root.node = child
} if color == BLACK {
delete_fixup(root, child, parent)
} } func delete_fixup(root *RBRoot, node *RBNode, parent *RBNode) {
var other *RBNode for (node == nil || rb_is_black(node)) && node != root.node {
// node是父节点的左孩子
if parent.left == node {
other = parent.right
// Case 1: node的兄弟节点是红色的
if rb_is_red(other) {
rb_set_black(other)
rb_set_red(parent)
left_rotate(root, parent)
other = parent.right
}
// Case 2: node的兄弟w是黑色,且w的俩个孩子也都是黑色的
if (other.left == nil || rb_is_black(other.left)) && (other.right == nil || rb_is_black(other.right)) {
rb_set_red(other)
node = parent
parent = node.parent
} else {
// Case 3: node的兄弟w是黑色的,并且w的左孩子是红色,右孩子为黑色。
if other.right == nil || rb_is_black(other.right) {
rb_set_black(other.left)
rb_set_red(other)
right_rotate(root, other)
other = parent.right
}
// Case 4: node的兄弟w是黑色的;并且w的右孩子是红色的,左孩子任意颜色。
other.color = parent.color
rb_set_black(parent)
rb_set_black(other.right)
left_rotate(root, parent)
node = root.node
break
}
} else {
other = parent.left
// Case 1: node的兄弟w是红色的
if rb_is_red(other) {
rb_set_black(other)
rb_set_red(parent)
right_rotate(root, parent)
other = parent.left
}
// Case 2: node的兄弟w是黑色,且w的俩个孩子也都是黑色的
if (other.left == nil || rb_is_black(other.left)) && (other.right == nil || rb_is_black(other.right)) {
rb_set_red(other)
node = parent
parent = node.parent
} else {
// Case 3: node的兄弟w是黑色的,并且w的左孩子是红色,右孩子为黑色。
if other.left == nil || rb_is_black(other.left) {
rb_set_black(other.right)
rb_set_red(other)
left_rotate(root, other)
other = parent.left
}
// Case 4: node的兄弟w是黑色的;并且w的右孩子是红色的,左孩子任意颜色。
other.color = parent.color
rb_set_black(parent)
rb_set_black(other.left)
right_rotate(root, parent)
node = root.node
break
}
}
} if node != nil {
rb_set_black(node)
}
} func main() {
//var datas = []Type{10, 40, 30, 60, 90, 70, 20, 50, 80}
var datas = []Type{10, 40, 30, 60, 90, 70, 20, 80} var root *RBRoot = new(RBRoot)
for _, data := range datas {
var node = &RBNode{key:data}
Add(root, node)
}
fmt.Print("前序遍历:")
PreTraverse(root.node)
fmt.Print("\n中序遍历:")
InTraverse(root.node)
fmt.Print("\n后序遍历:")
PostTraverse(root.node)
fmt.Print("\n\n") Print(root.node, root.node.key, 0)
fmt.Print("\n") var delNode = Find(root.node, 30)
Delete(root, delNode)
Print(root.node, root.node.key, 0)
fmt.Print("\n")
PrintRoute(root.node) }

(四)用go实现红黑树的更多相关文章

  1. Linux红黑树(二)——访问节点

    核心对红黑树使用两点说明 1.头文件 <Documentation/rbtree.txt> Linux's rbtree implementation lives in the file ...

  2. 红黑树(四)之 C++的实现

    概要 前面分别介绍红黑树的理论知识和红黑树的C语言实现.本章是红黑树的C++实现,若读者对红黑树的理论知识不熟悉,建立先学习红黑树的理论知识,再来学习本章. 目录1. 红黑树的介绍2. 红黑树的C++ ...

  3. 新秀nginx源代码分析数据结构篇(四)红黑树ngx_rbtree_t

    新秀nginx源代码分析数据结构篇(四)红黑树ngx_rbtree_t Author:Echo Chen(陈斌) Email:chenb19870707@gmail.com Blog:Blog.csd ...

  4. 数据结构(四)--- 红黑树(RedBlock-Tree)

      文章图片来自邓俊辉老师课件 先提几个问题去思考学习本文 : 红黑树和2-4树(B-Tree)很像,那么它存在的动机又是什么呢 插入和删除操作的逻辑又是怎么样的,时间和空间复杂度可以达到怎么样 和 ...

  5. 菜鸟nginx源码剖析数据结构篇(四)红黑树ngx_rbtree_t[转]

    菜鸟nginx源码剖析数据结构篇(四)红黑树ngx_rbtree_t Author:Echo Chen(陈斌) Email:chenb19870707@gmail.com Blog:Blog.csdn ...

  6. 第十四章 红黑树——C++代码实现

    红黑树的介绍 红黑树(Red-Black Tree,简称R-B Tree),它一种特殊的二叉查找树.红黑树是特殊的二叉查找树,意味着它满足二叉查找树的特征:任意一个节点所包含的键值,大于等于左孩子的键 ...

  7. 红黑树——算法导论(15)

    1. 什么是红黑树 (1) 简介     上一篇我们介绍了基本动态集合操作时间复杂度均为O(h)的二叉搜索树.但遗憾的是,只有当二叉搜索树高度较低时,这些集合操作才会较快:即当树的高度较高(甚至一种极 ...

  8. 算法设计和数据结构学习_5(BST&AVL&红黑树简单介绍)

    前言: 节主要是给出BST,AVL和红黑树的C++代码,方便自己以后的查阅,其代码依旧是data structures and algorithm analysis in c++ (second ed ...

  9. 数据结构中很常见的各种树(BST二叉搜索树、AVL平衡二叉树、RBT红黑树、B-树、B+树、B*树)

    数据结构中常见的树(BST二叉搜索树.AVL平衡二叉树.RBT红黑树.B-树.B+树.B*树) 二叉排序树.平衡树.红黑树 红黑树----第四篇:一步一图一代码,一定要让你真正彻底明白红黑树 --- ...

  10. 红黑树(二)之 C语言的实现

    概要 红黑树在日常的使用中比较常用,例如Java的TreeMap和TreeSet,C++的STL,以及Linux内核中都有用到.之前写过一篇文章专门介绍红黑树的理论知识,本文将给出红黑数的C语言的实现 ...

随机推荐

  1. springcloud12-spring cloud stream

    1.基础说明 官网:https://spring.io/projects/spring-cloud-stream#overview   文档:https://docs.spring.io/spring ...

  2. vue+elementUI 实现设置还款日字母弹窗组件

    1.业务背景 还款业务,设置每月还款日,选每月几号扣款,不需要29.30.31,因为不是每个月都有这三天的 2.预期效果图 3.代码实现 3.1 初始化vue项目 地址:https://cn.vuej ...

  3. keypoint数据结构

    公有属性 (float)angle:角度,表示关键点的方向,-1为初值. (int)class_id:当要对图片进行分类时,我们可以用class_id对每个特征点进行区分,未设定时为-1,需要靠自己设 ...

  4. 基于Kali Linux的渗透测试工具-Discover脚本

    简介  这篇文章主要介绍了基于Kali Linux的渗透测试工具-Discover脚本以及相关的经验技巧,文章约5629字,浏览量510,点赞数9,值得参考! 如果你正在使用Kali Linux,那么 ...

  5. flutter系列之:Navigator的高级用法

    目录 简介 named routes 给named route传参数 从Screen返回值 向Screen传值 总结 简介 上篇文章我们讲到了flutter中navigator的基本用法,我们可以使用 ...

  6. placeholder 颜色修改

    input::-webkit-input-placeholder{ color:#fff; } input::-moz-placeholder{ /* Mozilla Firefox 19+ */ c ...

  7. Linux:touch 修改文件的时间

    修改本文件的时间 参数 描述 例子 -a 只修改访问时间(Access Time) touch -a hello.txt -m 只更新修改时间(Modify Time) touch -m hello. ...

  8. “堆内存持续占用高 且 ygc回收效果不佳” 排查处理实践

    作者:京东零售 王江波 说明:部分素材来源于网络,数据分析全为真实数据. 一. 问题背景 自建的两套工具,运行一段时间后均出现 内存占用高触发报警,频繁young gc且效果不佳.曾经尝试多次解决,因 ...

  9. 动手写了个简单版的ChatGPT的Java版客户端

    最近ChatGpt大火,我在年前申请过账号忘了下确实强大. 作为Java程序猿社畜就尝试写了个Java版本的简易版客户端. 源码地址:https://github.com/Grt1228/chatgp ...

  10. Django-drf-序列化器高级用法之SerializerMethodField

    在Drf框架中的serializers.py序列化中, SerializerMethodField字段是一个只读字段.它通过调用附加到的序列化程序类上的方法来获取其值.它可用于将任何类型的数据添加到对 ...