GO自研微服务框架-路由实现

路由实现

1.不用框架

不用框架的路由实现

package main

import (
   "fmt"
   "log"
   "net/http"
)

func main() {
   http.HandleFunc("/hello", func(writer http.ResponseWriter, request *http.Request) {
      fmt.Fprintf(writer, "%s 欢迎来到goweb教程", "lisus.com")
   })
   err := http.ListenAndServe(":8111", nil)
   if err != nil {
      log.Fatal(err)
   }
}

2.路由实现

package main

import (
   "log"
   "net/http"
)

type HandleFunc func(w http.ResponseWriter, r *http.Request)
//定义路由结构体
type router struct {
    //定义一个路由处理函数map
   handleFuncMap map[string]HandleFunc
}
//定义引擎
type Engine struct {
   router
}

func (r *router) Add(name string, handleFunc HandleFunc) {
   r.handleFuncMap[name] = handleFunc
}

func New() *Engine {
   return &Engine{
      router: router{handleFuncMap: make(map[string]HandleFunc)},
   }
}
func (e *Engine) Run() {
    //循环处理路由
   for key, value := range e.handleFuncMap {
      http.HandleFunc(key, value)
   }
   err := http.ListenAndServe(":8111", nil)
   if err != nil {
      log.Fatal(err)
   }
}

测试代码

package main

import (
   "fmt"
   "net/http"
)

func main() {
   engine := New()
   engine.Add("/hello", func(w http.ResponseWriter, r *http.Request) {
      fmt.Fprintf(w, "%s 欢迎学习GO自研框架", "lisus2000")
   })
   engine.Run()
}

3.实现分组路由

package main

import (
   "log"
   "net/http"
)

type HandleFunc func(w http.ResponseWriter, r *http.Request)

// 定义路由分组结构
type routerGroup struct {
   name          string
   handleFuncMap map[string]HandleFunc
}

type router struct {
   routerGroups []*routerGroup
}

// Group 分组方法
func (r *router) Group(name string) *routerGroup {
   routerGroup := &routerGroup{
      name:          name,
      handleFuncMap: make(map[string]HandleFunc),
   }
   r.routerGroups = append(r.routerGroups, routerGroup)
   return routerGroup
}

// Add 添加路由
func (r *routerGroup) Add(name string, handleFunc HandleFunc) {
   r.handleFuncMap[name] = handleFunc
}

type Engine struct {
   router
}

func New() *Engine {
   return &Engine{
      router: router{},
   }
}
func (e *Engine) Run() {
   //user key:get value func
   for _, group := range e.routerGroups {
      for key, value := range group.handleFuncMap {
         http.HandleFunc("/"+group.name+key, value)
      }
   }

   err := http.ListenAndServe(":8111", nil)
   if err != nil {
      log.Fatal(err)
   }

测试代码

package main

import (
   "fmt"
   "net/http"
)

func main() {
   engine := New()
   g := engine.Group("user")
   g.Add("/hello", func(w http.ResponseWriter, r *http.Request) {
      fmt.Fprintf(w, "%s 欢迎学习GO自研框架", "lisus2000")
   })
   g.Add("/info", func(w http.ResponseWriter, r *http.Request) {
      fmt.Fprintf(w, "%s info功能", "lisus2000")
   })
   engine.Run()
}

4.支持不同的请求方式

package main

import (
   "fmt"
   "log"
   "net/http"
)

type HandleFunc func(w http.ResponseWriter, r *http.Request)

// 定义路由分组结构
type routerGroup struct {
   name             string
   handleFuncMap    map[string]HandleFunc
   handlerMethodMap map[string][]string
}

type router struct {
   routerGroups []*routerGroup
}

// Group 分组方法
func (r *router) Group(name string) *routerGroup {
   routerGroup := &routerGroup{
      name:             name,
      handleFuncMap:    make(map[string]HandleFunc),
      handlerMethodMap: make(map[string][]string),
   }
   r.routerGroups = append(r.routerGroups, routerGroup)
   return routerGroup
}

// Add 添加路由
func (r *routerGroup) Add(name string, handleFunc HandleFunc) {
   r.handleFuncMap[name] = handleFunc
}
func (r *routerGroup) Any(name string, handleFunc HandleFunc) {
   r.handleFuncMap[name] = handleFunc
   r.handlerMethodMap["ANY"] = append(r.handlerMethodMap["ANY"], name)
}
func (r *routerGroup) Get(name string, handleFunc HandleFunc) {
   r.handleFuncMap[name] = handleFunc
   r.handlerMethodMap[http.MethodGet] = append(r.handlerMethodMap[http.MethodGet], name)
}
func (r *routerGroup) Post(name string, handleFunc HandleFunc) {
   r.handleFuncMap[name] = handleFunc
   r.handlerMethodMap[http.MethodPost] = append(r.handlerMethodMap[http.MethodPost], name)
}

type Engine struct {
   router
}

func New() *Engine {
   return &Engine{
      router: router{},
   }
}
func (e *Engine) ServeHTTP(w http.ResponseWriter, r *http.Request) {
   method := r.Method
   for _, group := range e.routerGroups {
      for name, methodHandle := range group.handleFuncMap {
         url := "/" + group.name + name
         if r.RequestURI == url {
            routers, ok := group.handlerMethodMap["ANY"]
            if ok {
               for _, routerName := range routers {
                  if routerName == name {
                     methodHandle(w, r)
                     return
                  }
               }
            }
            //method 进行匹配
            routers, ok = group.handlerMethodMap[method]
            if ok {
               for _, routerName := range routers {
                  if routerName == name {
                     methodHandle(w, r)
                     return
                  }
               }
            }
            w.WriteHeader(http.StatusMethodNotAllowed)
            fmt.Fprintf(w, "%s %s not allow\n", r.RequestURI, method)
            return
         }
      }
   }
   w.WriteHeader(http.StatusNotFound)
   fmt.Fprintf(w, "%s not found\n", r.RequestURI)
}
func (e *Engine) Run() {
   http.Handle("/", e)
   err := http.ListenAndServe(":8111", nil)
   if err != nil {
      log.Fatal(err)
   }
}

测试代码

package main

import (
   "fmt"
   "net/http"
)

func main() {
   engine := New()
   g := engine.Group("user")
   g.Get("/hello", func(w http.ResponseWriter, r *http.Request) {
      fmt.Fprintf(w, "%s 欢迎学习GO自研框架", "lisus2000")
   })
   g.Post("/info", func(w http.ResponseWriter, r *http.Request) {
      fmt.Fprintf(w, "%s info功能", "lisus2000")
   })
   engine.Run()
}

5.支持同一个路径不同请求方式

package main

import "net/http"

type Context struct {
   W http.ResponseWriter
   R *http.Request
}
package main

import (
   "fmt"
   "log"
   "net/http"
)

const ANY = "ANY"

type HandleFunc func(ctx *Context)

// 定义路由分组结构
type routerGroup struct {
   name             string
   handleFuncMap    map[string]map[string]HandleFunc
   handlerMethodMap map[string][]string
}

type router struct {
   routerGroups []*routerGroup
}

// Group 分组方法
func (r *router) Group(name string) *routerGroup {
   routerGroup := &routerGroup{
      name:             name,
      handleFuncMap:    make(map[string]map[string]HandleFunc),
      handlerMethodMap: make(map[string][]string),
   }
   r.routerGroups = append(r.routerGroups, routerGroup)
   return routerGroup
}

func (r *routerGroup) handle(name string, method string, handleFunc HandleFunc) {
   _, ok := r.handleFuncMap[name]
   if !ok {
      r.handleFuncMap[name] = make(map[string]HandleFunc)
   }
   _, ok = r.handleFuncMap[name][method]
   if ok {
      panic("有重复的路由")
   }
   r.handleFuncMap[name][method] = handleFunc
   //r.handlerMethodMap[method] = append(r.handlerMethodMap[method], name)
}

func (r *routerGroup) Any(name string, handleFunc HandleFunc) {
   r.handle(name, ANY, handleFunc)
}
func (r *routerGroup) Get(name string, handleFunc HandleFunc) {
   r.handle(name, http.MethodGet, handleFunc)
}
func (r *routerGroup) Post(name string, handleFunc HandleFunc) {
   r.handle(name, http.MethodPost, handleFunc)
}

type Engine struct {
   router
}

func New() *Engine {
   return &Engine{
      router: router{},
   }
}
func (e *Engine) ServeHTTP(w http.ResponseWriter, r *http.Request) {
   method := r.Method
   for _, group := range e.routerGroups {
      for name, methodHandle := range group.handleFuncMap {
         url := "/" + group.name + name
         if r.RequestURI == url {
            ctx := &Context{
               W: w,
               R: r,
            }
            handle, ok := methodHandle[ANY]
            if ok {
               handle(ctx)
               return
            }

            handle, ok = methodHandle[method]
            if ok {
               handle(ctx)
               return
            }
            //method 进行匹配
            w.WriteHeader(http.StatusMethodNotAllowed)
            fmt.Fprintf(w, "%s %s not allow\n", r.RequestURI, method)
            return

         }
      }
   }
   w.WriteHeader(http.StatusNotFound)
   fmt.Fprintf(w, "%s not found\n", r.RequestURI)
}
func (e *Engine) Run() {
   http.Handle("/", e)
   err := http.ListenAndServe(":8111", nil)
   if err != nil {
      log.Fatal(err)
   }
}

测试代码

package main

import (
   "fmt"
)

func main() {
   engine := New()
   g := engine.Group("user")
   g.Get("/hello", func(ctx *Context) {
      fmt.Fprintf(ctx.W, "%s Get欢迎学习GO自研框架", "lisus2000")
   })
   g.Post("/hello", func(ctx *Context) {
      fmt.Fprintf(ctx.W, "%s Post欢迎学习GO自研框架", "lisus2000")
   })
   g.Post("/info", func(ctx *Context) {
      fmt.Fprintf(ctx.W, "%s info功能", "lisus2000")
   })
   engine.Run()
}

6.前缀树

在前面实现的时候,我们的路径匹配实现的很简陋,不能实现更为复杂的需求,比如/user/get/:id 这种带有参数的,这种带有参数的路径,我们称之为动态路由

除了带有参数的,一般情况下,我们可能还希望支持通配符**,比如/static/**, 可以匹配/static/vue.js或者/static/css/index.css这些。

在这里插入图片描述

实现代码

package msgo

import "strings"

type treeNode struct {
	name       string
	children   []*treeNode
	routerName string
	isEnd      bool
}

//put path: /user/get/:id

func (t *treeNode) Put(path string) {
	root := t
	strs := strings.Split(path, "/")
	for index, name := range strs {
		if index == 0 {
			continue
		}
		children := t.children
		isMatch := false
		for _, node := range children {
			if node.name == name {
				isMatch = true
				t = node
				break
			}
		}
		if !isMatch {
			isEnd := false
			if index == len(strs)-1 {
				isEnd = true
			}
			node := &treeNode{name: name, children: make([]*treeNode, 0), isEnd: isEnd}
			children = append(children, node)
			t.children = children
			t = node
		}

	}
	t = root
}

//get path:/user/get/11
// hello

func (t *treeNode) Get(path string) *treeNode {
	strs := strings.Split(path, "/")
	routerName := ""
	for index, name := range strs {
		if index == 0 {
			continue
		}
		children := t.children
		isMatch := false
		for _, node := range children {
			if node.name == name || node.name == "*" ||
				strings.Contains(node.name, ":") {

				isMatch = true
				routerName += "/" + node.name
				node.routerName = routerName
				t = node
				//到达最尾部结点
				if index == len(strs)-1 {
					return node
				}
				break
			}
		}
		if !isMatch {
			for _, node := range children {
				// /user/**
				// /user/get/userinfo
				// /user/aa/bb
				if node.name == "**" {
					routerName += "/" + node.name
					node.routerName = routerName
					return node
				}
			}
		}
	}
	return nil
}

测试代码

package msgo

import (
   "fmt"
   "testing"
)

func TestTreeNode(t *testing.T) {
   root := &treeNode{
      name:     "/",
      children: make([]*treeNode, 0),
   }
   root.Put("/user/get/:id")
   root.Put("/user/create/hello")
   root.Put("/user/create/aaa")
   root.Put("/order/get/aaa")

   node := root.Get("/user/get/1")
   fmt.Println(node)

   node = root.Get("/user/create/hello")
   fmt.Println(node)

   node = root.Get("/user/create/aaa")
   fmt.Println(node)

   node = root.Get("/order/get/aaa")
   fmt.Println(node)

}

6.1适配前缀树

实现代码

package msgo

import (
	"fmt"
	"log"
	"net/http"
)

const ANY = "ANY"

type HandleFunc func(ctx *Context)

// 定义路由分组结构
type routerGroup struct {
	name             string
	handleFuncMap    map[string]map[string]HandleFunc
	handlerMethodMap map[string][]string
	treeNode         *treeNode
}

type router struct {
	routerGroups []*routerGroup
}

// Group 分组方法
func (r *router) Group(name string) *routerGroup {
	routerGroup := &routerGroup{
		name:             name,
		handleFuncMap:    make(map[string]map[string]HandleFunc),
		handlerMethodMap: make(map[string][]string),
		treeNode:         &treeNode{name: "/", children: make([]*treeNode, 0)},
	}
	r.routerGroups = append(r.routerGroups, routerGroup)
	return routerGroup
}

func (r *routerGroup) handle(name string, method string, handleFunc HandleFunc) {
	_, ok := r.handleFuncMap[name]
	if !ok {
		r.handleFuncMap[name] = make(map[string]HandleFunc)
	}
	_, ok = r.handleFuncMap[name][method]
	if ok {
		panic("有重复的路由")
	}
	r.handleFuncMap[name][method] = handleFunc
	r.treeNode.Put(name)
}

func (r *routerGroup) Any(name string, handleFunc HandleFunc) {
	r.handle(name, ANY, handleFunc)
}
func (r *routerGroup) Get(name string, handleFunc HandleFunc) {
	r.handle(name, http.MethodGet, handleFunc)
}
func (r *routerGroup) Post(name string, handleFunc HandleFunc) {
	r.handle(name, http.MethodPost, handleFunc)
}
func (r *routerGroup) Delete(name string, handlerFunc HandleFunc) {
	r.handle(name, http.MethodDelete, handlerFunc)
}
func (r *routerGroup) Put(name string, handlerFunc HandleFunc) {
	r.handle(name, http.MethodPut, handlerFunc)
}
func (r *routerGroup) Patch(name string, handlerFunc HandleFunc) {
	r.handle(name, http.MethodPatch, handlerFunc)
}
func (r *routerGroup) Options(name string, handlerFunc HandleFunc) {
	r.handle(name, http.MethodOptions, handlerFunc)
}
func (r *routerGroup) Head(name string, handlerFunc HandleFunc) {
	r.handle(name, http.MethodHead, handlerFunc)
}

type Engine struct {
	router
}

func New() *Engine {
	return &Engine{
		router: router{},
	}
}
func (e *Engine) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	method := r.Method
	for _, group := range e.routerGroups {
		routerName := SubStringLast(r.RequestURI, "/"+group.name)
		// get/1
		node := group.treeNode.Get(routerName)
		if node != nil && node.isEnd {
			//路由匹配上了
			ctx := &Context{
				W: w,
				R: r,
			}
			handle, ok := group.handleFuncMap[node.routerName][ANY]
			if ok {
				handle(ctx)
				return
			}

			handle, ok = group.handleFuncMap[node.routerName][method]
			if ok {
				handle(ctx)
				return
			}
			//method 进行匹配
			w.WriteHeader(http.StatusMethodNotAllowed)
			fmt.Fprintf(w, "%s %s not allow\n", r.RequestURI, method)
			return
		}
	}
	w.WriteHeader(http.StatusNotFound)
	fmt.Fprintf(w, "%s not found\n", r.RequestURI)
}
func (e *Engine) Run() {
	http.Handle("/", e)
	err := http.ListenAndServe(":8111", nil)
	if err != nil {
		log.Fatal(err)
	}
}

测试代码

package main

import (
	"fmt"
	"msgo"
)

func main() {
	engine := msgo.New()
	g := engine.Group("user")
	//g.Get("/hello", func(ctx *msgo.Context) {
	//	fmt.Fprintf(ctx.W, "%s Get欢迎学习GO自研框架", "lisus2000")
	//})
	g.Get("/hello/get", func(ctx *msgo.Context) {
		fmt.Fprintf(ctx.W, "%s Get欢迎学习GO自研框架", "lisus2000")
	})
	g.Post("/hello", func(ctx *msgo.Context) {
		fmt.Fprintf(ctx.W, "%s Post欢迎学习GO自研框架", "lisus2000")
	})
	g.Post("/info", func(ctx *msgo.Context) {
		fmt.Fprintf(ctx.W, "%s info功能", "lisus2000")
	})
	g.Get("/get/:id", func(ctx *msgo.Context) {
		fmt.Fprintf(ctx.W, "%s get user 路径变量的值", "lisus2000")
	})
	engine.Run()
}

il)
if err != nil {
log.Fatal(err)
}
}


测试代码

```go
package main

import (
	"fmt"
	"msgo"
)

func main() {
	engine := msgo.New()
	g := engine.Group("user")
	//g.Get("/hello", func(ctx *msgo.Context) {
	//	fmt.Fprintf(ctx.W, "%s Get欢迎学习GO自研框架", "lisus2000")
	//})
	g.Get("/hello/get", func(ctx *msgo.Context) {
		fmt.Fprintf(ctx.W, "%s Get欢迎学习GO自研框架", "lisus2000")
	})
	g.Post("/hello", func(ctx *msgo.Context) {
		fmt.Fprintf(ctx.W, "%s Post欢迎学习GO自研框架", "lisus2000")
	})
	g.Post("/info", func(ctx *msgo.Context) {
		fmt.Fprintf(ctx.W, "%s info功能", "lisus2000")
	})
	g.Get("/get/:id", func(ctx *msgo.Context) {
		fmt.Fprintf(ctx.W, "%s get user 路径变量的值", "lisus2000")
	})
	engine.Run()
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/312011.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

白嫖啦,微软面向初学者的机器学习课程

网址: https://microsoft.github.io/ML-For-Beginners/#/ Microsoft 提供了一个名为 "Machine Learning for Beginners" 的课程,这是一个为期12周、包含26节课的课程,旨在帮助初学者了解机器学习的基本概念。这个课程由 Azure Clou…

全网最细RocketMQ源码二:Producer

入口 这里分析源码用的入口是: org.apache.rocketmq.example.quickstart package org.apache.rocketmq.example.quickstart;public class Producer {public static void main(String[] args) throws MQClientException, InterruptedException {/** Instantiate wi…

有效的回文

常用方法就是双指针。使用两个指针从字符串的两端向中间移动,同时比较对应位置的字符,直到两个指针相遇。由于题目忽略非字母和非数字的字符且忽略大小写,所以跳过那些字符,并将字母转换为小写(或大写)进行…

java基础day04 -- 命令行运行java文件

package com.exmaple;/*** 命令行参数*/ public class ArgsOfMain {public static void main(String[] args) {//增强for循环for(String arg : args){System.out.println(arg);}} }当我打开idea终端运行javac命令完成后(需要配置java环境变量,注意idea使…

FineBI实战项目一(16):下订单总用户数分析开发

点击新建组件,创建下订单总用户数组件。 选择自定义图表,选择文本,拖拽要分析的字段到文本中。 修改指针值名称。 将指针值名称修改为用户数。 进入仪表板,拖拽刚刚的组件进入仪表板,然后在再编辑标题。 效果如下&…

【排序】归并排序(C语言实现)

文章目录 1. 递归版的归并排序1.1 归并排序的思想2. 递归版的归并排序的实现 2. 非递归版的归并排序 1. 递归版的归并排序 1.1 归并排序的思想 归并排序(MERGE - SORT)是建立在归并操作上的一种有效的排序算法, 该算法是采用分治法(Divide a…

快速了解VR全景拍摄技术运用在旅游景区的优势

豆腐脑加了糖、烤红薯加了勺,就连索菲亚大教堂前都有了“人造月亮”,在这个冬季,“尔滨”把各地游客宠上了天。面对更多的游客无法实地游玩,哈尔滨冰雪世界再添新玩法,借助VR全景拍摄技术对冬季经典冰雪体验项目进行全…

vue上传文件加进度条,fake-progress一起使用

el-upload上传过程中加进度条,进度条el-progress配合fake-progress一起使用,效果如下: 安装 npm install fake-progress 在用到的文件里面引用 import Fakeprogress from "fake-progress"; 这个进度条主要是假的进度条&#xff…

129基于matlab的粒子群算法、遗传算法、鲸鱼算法、改进鲸鱼算法优化最小二乘支持向量机(lssvm)的gam正则化参数和sig2RBF函数的参数

基于matlab的粒子群算法、遗传算法、鲸鱼算法、改进鲸鱼算法优化最小二乘支持向量机(lssvm)的gam正则化参数和sig2RBF函数的参数。输出适应度曲线,测试机和训练集准确率。程序已调通,可直接运行。 129 matlabLSSVM优化算法 (xiaoh…

14.kubernetes部署Dashboard

Dashboard 是基于网页的 Kubernetes 用户界面。 你可以使用 Dashboard 将容器应用部署到 Kubernetes 集群中,也可以对容器应用排错,还能管理集群资源。 你可以使用 Dashboard 获取运行在集群中的应用的概览信息,也可以创建或者修改 Kubernetes 资源 (如 Deployment,Job,D…

云服务器租用价格表,阿里云腾讯云华为云2024年优惠对比

作为多年站长使市面上大多数的云厂商的云服务器都使用过,很多特价云服务器都是新用户专享的,本文有老用户特价云服务器,阿腾云atengyun.com有多个网站、小程序等,国内头部云厂商阿里云、腾讯云、华为云、UCloud、京东云都有用过&a…

基础篇_开发命令行程序(输入输出,类型、变量、运算符,条件语句,循环语句,方法,package与jar)

文章目录 一. 输入输出1. System.out2. System.in3. Scanner4. 变量名5. 关键字 二. 类型、变量、运算符1. 字符与字符串字符值与字符串值转义字符文本块 2. 类型何为类型数字类型字符类型 3. 变量与运算符变量运算符 4. 练习 - 房贷计算器Math.pow()数字格式化查阅 Javadoc 三…

虽然是个去年的旧新闻,但这透露了IBM的新去向

引言:老树盘根发新芽,只为云数添新彩。 【科技明说 | 科技热点关注】 就在2023年12月25日左右,外媒有消息被传入国内,IBM正在斥资21.3亿欧元收购德国企业软件公司Software AG旗下的两个iPaaS企业技术平台。 具体包括&…

YOLOv5 损失函数改进 | 引入 Shape-IoU 考虑边框形状与尺度的度量

🗝️改进YOLOv8注意力系列一:结合ACmix、Biformer、BAM注意力机制 论文讲解加入代码本文提供了改进 YOLOv8注意力系列包含不同的注意力机制以及多种加入方式,在本文中具有完整的代码和包含多种更有效加入YOLOv8中的yaml结构,读者可以获取到注意力加入的代码和使用经验,总…

关于网络安全,你了解多少?

随着互联网技术的飞速发展,网络安全问题日益凸显。国际标准化组织(ISO)对计算机网络安全做出了明确定义:为保护数据处理系统而采取的技术和管理的安全措施,旨在保护计算机硬件、软件和数据免受偶然和故意原因造成的破坏…

使用Pygame显示文字的示例代码

import pygame import syspygame.init()# 设置窗口尺寸 win_size (800, 600) screen pygame.display.set_mode(win_size) pygame.display.set_caption("文字显示示例")# 设置字体和文字内容 font pygame.font.SysFont(None, 48) # 使用系统默认字体,字…

全网最好的Java集合总结

1.List (有序、可重复) 1.1 ArrayList 底层是一个Object[],在不指定容量的时候,会进行懒加载,创建一个{}对象,然后在add的时候创建一个容量为10的Object[],当数组容量不够的时候会扩容,每次扩容…

实现线程同步的几种方式

线程同步 1. 线程同步概念 线程同步是指多个线程协调它们的执行顺序,以确保它们正确、安全地访问共享资源。在并发编程中,当多个线程同时访问共享数据或资源时,可能会导致竞争条件(Race Condition)和其他并发问题 所…

【Python基础】一文搞懂:Python 中 csv 文件的写入与读取

文章目录 1 引言2 CSV 文件简介3 Python 中的 csv 模块4 写入 CSV 文件4.1 基本用法4.2 高级用法 5 读取 CSV 文件5.1 基本用法5.2 高级用法 6 实例演示7 注意事项8 总结 1 引言 在数据处理和数据分析领域,CSV (逗号分隔值) 文件是一种常见的文件格式,用…

Day31 贪心算法 part01 理论基础 455.分发饼干 376.摆动序列 53.最大子序和

贪心算法 part01 理论基础 455.分发饼干 376.摆动序列 53.最大子序和 理论基础(转载自代码随想录) 什么是贪心 贪心的本质是选择每一阶段的局部最优,从而达到全局最优。 这么说有点抽象,来举一个例子: 例如&#…