使用PicGo和Github搭建图床

使用PicGo和Github搭建图床

今天接到TX云的客服电话,大意是我在贵站购买的一个域名没有在他家服务器解析,

只能在TX云解析,但是那个域名在七牛云作为图床使用,而且七牛云要求必须备案后的域名才能使用。

貌似形成了一个死循环。。太麻烦了,于是想换个方案。

将七牛的图床转移到Github上,使用PicGO+Github/Gitee做图床。

1. Github/Gitee新建一个仓库

新建一个普通仓库即可,这里我选择在Github新建一个仓库后,Gitee由GitHub同步过来。

然后本地的git remote 同时设置Github和Gitee,这样就能同时使用Github和Gitee管理一个仓库了。

2. 生成Github/Gitee的私人令牌(Token)

用于使用PicGO上传图片时使用,这里需要注意的是这个Token只有在生成的时候显示一下,注意保存好并且不要公开。

3. 下载并安装PicGo

PicGo是一个用于快速上传图片并获取图片 URL 链接的工具

PicGo项目地址:PicGo

下载地址:PicGo Release

Mac也可以使用brew install picgo --cask来安装PicGo

4. 批量下载所有的七牛图片

注意:只有需要从七牛移植图片的同学需要执行这一步,如果之前没有使用过七牛或者其他图床,这一步可以跳过。

访问七牛云的空间管理,将所有文件存储为一个列表放到qiniulist.tzt文件中,然后使用七牛的qshell get bucket file命令下载列表中的所有文件:

1
2
3
4
5
6
for i in `cat qiniulist.txt`
do
echo $i
qshell get moonwhite $i
sleep 1
done

文件下载完成后提交至Github和Gitee就可以了

1
2
3
4
5
6
7
8
9
10
11
# 将默认远程仓库名字修改为github
git remote rename origin github
# 添加gitee的远程仓库
git remote add gitee git@gitee.com/xxx/xxx.git
# 添加所有改动至暂存区
git add .
# 提交本地修改内容
git commit -m "init commit"
# 推送至远程github和gitee仓库
git push github
git push gitee

5. Typora

Typora中添加一键上传图片至Gitee和Github的支持

typora => 偏好设置 => 图像
当插入图片时选择上传图片,上传服务选择PicGo:

image-20211001231454668

picgo添加gitee插件

6. VS Code 设置上传Github

image-20211001235456122

批量替换原blog内容中的图片链接地址

1
find . -name "*.md" |xargs perl -pi -e 's|https://yinyang.space|https://gitee.com/geekhall/pic/raw/main|g'

机器学习与人工智能笔记

机器学习与人工智能笔记

安装TensorFlow

1
2
3
4
5
6
7
8
# Requires the latest pip
pip install --upgrade pip

# Current stable release for CPU and GPU
pip install tensorflow

# Or try the preview build (unstable)
pip install tf-nightly

也可以使用Docker

1
2
docker pull tensorflow/tensorflow:latest  # Download latest stable image
docker run -it -p 8888:8888 tensorflow/tensorflow:latest-jupyter # Start Jupyter server
1
2
3
4
5
6
>>> import tensorflow as tf
>>> tf.add(1, 2).numpy()
3
>>> hello = tf.constant('Hello, TensorFlow!')
>>> hello.numpy()
b'Hello, TensorFlow!'

TensorFlow教程:https://www.tensorflow.org/tutorials/
Google AI-Tools : https://ai.google/tools/

算法导论笔记

算法导论笔记

常用算法设计方法

  • 增量方法(Incremental)
    在排好子数组A[1..j-1]后,将元素A[j]插入,形成排好序的子数组A[1..j]
  • 分治法(divide-and-conquer)
    将原问题划分成n个规模较小而结构与原问题相似的子问题;递归地解决这些子问题,然后再合并其结果,就得到原问题的解。
    分治模式在每一层递归上都有三个步骤:
    • 分解(Divide)将原问题分解成一系列子问题;
    • 解决(Conquer)递归地解决子问题,若子问题足够小,则直接求解;
    • 合并(Combine)将子问题的解合并成原问题的解。

定理

定理3.1 对于任意两个函数f(n)g(n)f(n)=θ(g(n)) 当且仅当f(n)=O(g(n))f(n)=Ω(g(n))

公式

性质

任何底大于1的指数函数比任何多项式函数增长得更快

先导

  1. 数据结构与算法概述
  2. 如何衡量算法的性能

基础篇

  1. 数组、栈、队列
  2. 链表
  3. 哈希表
  4. 双指针
  5. 模拟、枚举与递推

专题篇

  1. 二分
  2. 滑动窗口
  3. 搜索(BFS、DFS、回溯)专题
  4. 动态规划
  5. 背包
  6. 分治
  7. 贪心
  8. 位运算

进阶篇

  1. Trie树(字典树)
  2. 并查集
  3. 剪枝
  4. 字符串匹配
  5. 跳表
  6. 线段树:线段树区间合并法解决多次询问的「区间最长连续上升序列问题」和「区间最大子段和问题」

BST(Binary Search Tree又叫二叉搜索树、二叉排序树、二叉查找树)

1
2
3
4
5
6
7
8
9
10
11
12
二叉查找树(Binary Search Tree),(又:二叉搜索树,二叉排序树)它或者是一棵空树,或者是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值; 它的左、右子树也分别为二叉排序树。二叉搜索树作为一种经典的数据结构,它既有链表的快速插入与删除操作的特点,又有数组快速查找的优势;所以应用十分广泛,例如在文件系统和数据库系统一般会采用这种数据结构进行高效率的排序与检索操作。

搜索树(BST)又称二叉查找树或二叉排序树。一棵二叉搜索树是以二叉树来组织的,可以使用一个链表数据结构来表示,其中每一个结点就是一个对象。一般地,除了key和位置数据之外,每个结点还包含属性lchild、rchild和parent,分别指向结点的左孩子、右孩子和双亲(父结点)。如果某个孩子结点或父结点不存在,则相应属性的值为空(NIL)。根结点是树中唯一父指针为NULL的结点,而叶子结点的孩子结点指针也为NULL。

二叉搜索树是能够高效地进行如下操作的数据结构。
1.插入一个数值
2.查询是否包含某个数值
3.删除某个数值

删除节点时需要注意若删除节点只有左子树或者右子树的情况下直接删除就可以了,很好理解,但是当要删除的节点左右子树都存在的情况下,
就找到其右子树的最左孩子,与该节点交换,然后再删除该左孩子即可。

红黑树(一种平衡二叉搜索树)

能够保证在最坏情况下,基本的动态集合操作的时间为O(lgn)

定义:

一颗二叉查找树如果满足下面的红黑性质,则为一颗红黑树:

  1. 每个节点或是红的,或是黑的;
  2. 根节点是黑的;
  3. 每个叶节点(Nil)是黑的;
  4. 如果一个节点是红的,则他的两个儿子都是黑的;
  5. 对每个节点,从该节点到其子孙结点的所有路径上包含相同数据的黑节点。

AVL树

红黑树相对于AVL树来说,牺牲了部分平衡性以换取插入/删除操作时少量的旋转操作,整体来说性能要优于AVL树

2-3-4树

指阶为4的B树

B树

根据 Knuth 的定义,一个 m 阶的B树是一个有以下属性的树:

  1. 每一个节点最多有 m 个子节点
  2. 每一个非叶子节点(除根节点)最少有 ⌈m/2⌉ 个子节点
  3. 如果根节点不是叶子节点,那么它至少有两个子节点
  4. 有 k 个子节点的非叶子节点拥有 k − 1 个键
  5. 所有的叶子节点都在同一层

跳表

贪心算法
图(Dijkstra,Hellman-Ford)
Kruskal最小生成树算法:按照边的权值从小到大排序,每次从剩余的边中选择权值较小且边的两个顶点不在同一集合内的边(不会产生回路的边)
加入到生成树中,直到加入了n-1条边为止
图的割点、割边
二分图的最大匹配

Tag

数组1097

字符串545

动态规划404

哈希表399

数学382

深度优先搜索281

排序244

广度优先搜索225

树224

贪心210

二叉树195

二分查找180

双指针172

矩阵170

数据库168

位运算149

栈139

设计129

堆(优先队列)112

回溯106

图91

链表90

模拟77

滑动窗口72

前缀和72

并查集66

计数64

递归60

二叉搜索树54

分治50

字典树49

单调栈43

有序集合37

队列37

记忆化搜索33

状态压缩32

几何32

线段树24

拓扑排序24

哈希函数22

枚举22

博弈21

数据流20

字符串匹配18

交互18

树状数组18

滚动哈希14

随机化14

最短路13

组合数学13

双向链表11

归并排序10

单调队列10

快速选择10

数论10

迭代器10

脑筋急转弯10

概率与统计9

多线程9

桶排序8

计数排序6

后缀数组5

最小生成树5

扫描线4

Shell4

水塘抽样4

强连通分量2

欧拉回路2

拒绝采样2

基数排序2

双连通分量1

Go语言简明教程

本文旨在最短的时间内了解Go语言,适用于有其他语言基础,想迅速入门的同学。

1. 下载安装

到Go官网:https://golang.org/下载安装包:

或者Mac的同学可以直接使用brew命令来安装:

1
2
brew update
brew install golang

创建一个workspace folder,通常放在$Home目录下

1
mkdir $HOME/<your_go_workspace_folder_name>

添加环境变量:

编辑~/.zshrc,添加如下内容:

1
2
3
4
export GOPATH=$HOME/<your_go_workspace_folder_name>
export GOROOT=/usr/local/opt/go/libexec
export PATH=$PATH:$GOPATH/bin
export PATH=$PATH:$GOROOT/bin

配置VSCode 支持Go

Command+ Shift + P
后,输入 Go:Install/Update Tools,弹出的全选安装

这里需要注意如果你使用Mac的话,可能会弹出Permission denined错误,
原因是不存在/usr/local/pkg目录,或者这个目录权限不足。执行下面命令修复:

1
2
sudo mkdir /usr/local/pkg
sudo chown -R $(whoami) /usr/local/pkg

Hello World

新建一个hello.go文件,内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
// 定义包名。必须在源文件中非注释的第一行指明这个文件属于哪个包。
// package main表示一个可独立执行的程序,每个 Go 应用程序都包含一个名为 main 的包。
package main

// 引入fmt包
import "fmt"

// main() 是程序开始执行的函数。main 函数是每一个可执行程序所必须包含的,
// 一般来说都是在启动后第一个执行的函数
// (如果有 init() 函数则会先执行该函数)
func main() {
fmt.Println("Hello Go!")
}
1
2
$ go run hello.go
Hello, Go!

可以使用go build hello.go来生成可执行的二进制文件。

1
2
3
4
5
$ go build hello.go
$ ls
hello hello.go
$ ./hello
Hello, go!

语法

变量

Go语言使用var identifier type的方式定义变量。
可以一次声明多个变量:

1
var identifier1, identifier2 type
1
2
3
4
5
var a string = "Geek Go"
fmt.Println(a) // 输出结果:Geek Go

var b, c int = 1, 2
fmt.Println(b, c) // 输出结果:1 2

可以使用:=声明同时赋值变量

1
2
int_val := 100        // 相当于var int_val int; int_val = 1
fmt.Println(int_val) // 输出结果:100

也可以多变量声明

1
2
3
4
var vname1, vname2, vname3 = v1, v2, v3 // 和 python 很像,不需要显示声明类型,自动推断

vname1, vname2, vname3 := v1, v2, v3 // 出现在 := 左侧的变量不应该是已经被声明过的,否则会导致编译错误

常量

1
const identifier [type] = value

数组

1
var variable_name [SIZE] type

例如:

1
var balance [10] float32

初始化

1
var balance = [5]int{1,2,3,4,5}

for循环

Go语言的For循环有3种形式,

1
2
3
4
5
// 等价于C语言中的for
// init: 一般为赋值表达式,给控制变量赋初值;
// condition: 关系表达式或逻辑表达式,循环控制条件;
// post: 一般为赋值表达式,给控制变量增量或减量。
for init; condition; post { }
1
2
// 等价于C语言中的while
for condition { }
1
2
// 等价于C语言中的for(;;)
for { }

栗子:

1
2
3
4
var balance = [5]int{1, 2, 3, 4, 5}
for i := 0; i < len(balance); i++ {
fmt.Println(balance[i])
}

指针

指针使用流程:

  • 定义指针变量。
  • 为指针变量赋值。
  • 访问指针变量中指向地址的值。
1
var var_name *var_type

栗子:

1
2
var ip *int        /* 指向整型*/
var fp *float32 /* 指向浮点型 */

当一个指针被定义后没有分配到任何变量时,它的值为 nil。

nil 指针也称为空指针。

nil在概念上和其它语言的null、None、nil、NULL一样,都指代零值或空值。

结构体

结构体定义需要使用 type 和 struct 语句。struct 语句定义一个新的数据类型,结构体中有一个或多个成员。type 语句设定了结构体的名称。结构体的格式如下:

1
2
3
4
5
6
type struct_variable_type struct {
member definition
member definition
...
member definition
}

一旦定义了结构体类型,它就能用于变量的声明,语法格式如下:

1
2
3
variable_name := structure_variable_type {value1, value2...valuen}
// 或
variable_name := structure_variable_type { key1: value1, key2: value2..., keyn: valuen}

栗子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
type Books struct {
title string
author string
price float32
}

func main() {
book1 := Books{"Go语言教程", "GeekHall", 9.9}
book2 := Books{title: "Python语言教程", author: "GeekHall", price: 8.8}
var book3 Books
book3.title = "西游记"
book3.author = "吴承恩"
book3.price = 19.9

fmt.Println(book1) // {Go语言教程 GeekHall 9.9}
fmt.Println(book2.title) // Python语言教程
fmt.Println(book3) // {西游记 吴承恩 19.9}
}

结构体也可以作为函数参数或者结构体指针来使用

1
2
3
4
5
6
7
8
func printBook( book *Books ) {
fmt.Printf( "Book title : %s\n", book.title)
fmt.Printf( "Book author : %s\n", book.author)
fmt.Printf( "Book price : %f\n", book.price)
}

// 调用:
printBook(&book3)

切片(Slice)

Go 语言切片是对数组的抽象。

Go 数组的长度不可改变,在特定场景中这样的集合就不太适用,Go 中提供了一种灵活,功能强悍的内置类型切片(“动态数组”),与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大。

可以声明一个未指定大小的数组来定义切片:

1
var identifier []type

或使用 make() 函数来创建切片:

1
2
3
4
5
var slice1 []type = make([]type, len)

// 也可以简写为

slice1 := make([]type, len, [capacity])

len为数组的长度,并且也是切片的初始长度,可以使用可选参数capacity来指定容量

类似Python列表的切片操作

1
2
3
4
5
6
7
8
9
10
s := []int{0, 1, 2, 3, 4, 5, 6, 7}
s1 := make([]int, 8, 16)
fmt.Println(s[0:4]) // [0, 1, 2, 3]
fmt.Println(s[3:]) // [3 4 5 6 7]
fmt.Println(s[:3]) // [0 1 2]
fmt.Println(s[:]) // [0 1 2 3 4 5 6 7]
fmt.Println(len(s)) // 8
fmt.Println(cap(s)) // 8
fmt.Println(len(s1)) // 8
fmt.Println(cap(s1)) // 16

可以使用appendcopy方法来拷贝和追加元素

make和new 的区别

  • make和new都是golang用来分配内存的內建函数,且在堆上分配内存,make 即分配内存,也初始化内存。new只是将内存清零,并没有初始化内存。
  • make返回的还是引用类型本身;而new返回的是指向类型的指针。
  • make只能用来分配及初始化类型为slice,map,channel的数据;new可以分配任意类型的数据。

范围(Range)

在数组上使用range将传入index和值两个变量,可使用空白符”_”省略.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
nums := []int{1, 2, 3, 4}
sum := 0

for _, num := range nums {
sum += num
}
fmt.Println("sum: ", sum) // sum: 10

nums = append(nums, 5, 6, 7)
fmt.Println(nums) //[1 2 3 4 5 6 7]
for i, num := range nums {
if num%3 == 0 {
fmt.Println("index: ", i) // index : 2 和 index : 5
}
}

range也可以用在map的键值对上。

1
2
3
4
cars := map[string]string{"A": "Audi", "B": "BMW", "T": "Tesla"}
for k, v := range cars {
fmt.Printf("%s -> %s\n", k, v)
}

输出结果:

1
2
3
T -> Tesla
A -> Audi
B -> BMW

range也可以用来枚举Unicode字符串。

  • 第一个参数是字符的索引,
  • 第二个是字符(Unicode的值)本身。
1
2
3
for i, c := range "ABCabc" {
fmt.Println(i, c)
}

输出:

1
2
3
4
5
6
0 65
1 66
2 67
3 97
4 98
5 99

集合(Map)

Map 是一种无序的键值对的集合。Map 最重要的一点是通过 key 来快速检索数据,key 类似于索引,指向数据的值。

Map 是一种集合,所以我们可以像迭代数组和切片那样迭代它。不过,Map 是无序的,我们无法决定它的返回顺序,这是因为 Map 是使用 hash 表来实现的。

定义 Map
可以使用内建函数 make 也可以使用 map 关键字来定义 Map:

1
2
3
4
5
/* 声明变量,默认 map 是 nil */
var map_variable map[key_data_type]value_data_type

/* 使用 make 函数 */
map_variable := make(map[key_data_type]value_data_type)

栗子:

1
2
3
4
5
6
7
8
9
10
11
var bookAuthorMap map[string]string // 创建集合
bookAuthorMap = make(map[string]string)

bookAuthorMap["三国演义"] = "罗贯中"
bookAuthorMap["西游记"] = "吴承恩"
bookAuthorMap["水浒传"] = "施耐庵"
bookAuthorMap["红楼梦"] = "曹雪芹"

for book := range bookAuthorMap {
fmt.Println(book, "作者是:", bookAuthorMap[book])
}

打印结果:

1
2
3
4
三国演义 作者是: 罗贯中
西游记 作者是: 吴承恩
水浒传 作者是: 施耐庵
红楼梦 作者是: 曹雪芹

查看元素是否在集合中存在:

1
2
3
4
5
6
jpm, ok := bookAuthorMap["金瓶梅"]
if ok {
fmt.Println("金瓶梅的作者是:", jpm)
} else {
fmt.Println("金瓶梅不是四大名著") // will print
}

delete()函数用于删除集合的元素,参数为map和其对应的key:

1
delete(bookAuthorMap, "水浒传")  // 删除后集合中只剩三本了

递归函数

1
2
3
func recursion() {
recursion() /* 函数调用自身 */
}

类型转换

1
type_name(expression)

例:

1
2
3
4
5
6
7
8
9
10
var sum int = 17
var count int = 5
var mean float32
var org int

org = sum / count
mean = float32(sum) / float32(count)

fmt.Printf("org 的值为: %d\n", org) // 3
fmt.Printf("mean 的值为: %f\n", mean) // 3.4

接口

Go 语言提供了另外一种数据类型即接口,它把所有的具有共性的方法定义在一起,任何其他类型只要实现了这些方法就是实现了这个接口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

type ICar interface {
drive()
}

type Tesla struct {
}

type Toyota struct {
}

type Audi struct {
}

func (tesla Tesla) drive() {
fmt.Println("I am a Tesla, I can take you for a ride.")
}

func (toyota Toyota) drive() {
fmt.Println("I am a Toyota, I can take you for a ride.")
}

func (audi Audi) drive() {
fmt.Println("I am an Audi, I can take you for a ride.")
}

func main() {
var car ICar

car = new(Tesla)
car.drive() // I am a Tesla, I can take you for a ride.

car = new(Audi)
car.drive() // I am an Audi, I can take you for a ride.

car = new(Toyota)
car.drive() // I am a Toyota, I can take you for a ride.
}

错误处理

Go 语言通过内置的错误接口提供了非常简单的错误处理机制。

error类型是一个接口类型,这是它的定义:

1
2
3
type error interface {
Error() string
}

我们可以在编码中通过实现 error 接口类型来生成错误信息。

函数通常在最后的返回值中返回错误信息。使用errors.New 可返回一个错误信息:

并发

Go 语言支持并发,我们只需要通过 go 关键字来开启 goroutine 即可。

goroutine 是轻量级线程,goroutine 的调度是由 Golang 运行时进行管理的。

goroutine 语法格式:

1
go 函数名( 参数列表 )

如:

1
go f(x, y, z)

例子:

1
2
3
4
5
6
7
8
9
10
11
func concurrent_test(s string) {
for i := 0; i < 5; i++ {
time.Sleep(100 * time.Millisecond)
fmt.Println(s)
}
}

go concurrent_test("Audi")
go concurrent_test("BMW")
go concurrent_test("Tesla")
concurrent_test("Toyota")

结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Audi
Toyota
BMW
Tesla
Tesla
Audi
BMW
Toyota
Audi
Toyota
BMW
Tesla
Audi
Tesla
BMW
Toyota
Toyota

通道(channel)

通道(channel)是用来传递数据的一个数据结构。

通道可用于两个 goroutine 之间通过传递一个指定类型的值来同步运行和通讯。操作符 <- 用于指定通道的方向,发送或接收。如果未指定方向,则为双向通道。

1
2
ch <- v    // 把 v 发送到通道 ch
v := <-ch // 从 ch 接收数据,并把值赋给 v

声明一个通道很简单,我们使用chan关键字即可,通道在使用前必须先创建:

1
ch := make(chan int, [buffer])

make 的第二个参数可以指定缓冲区的大小:

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
func sum(s []int, c chan int) {
sum := 0
for _, v := range s {
sum += v
}
c <- sum // 把 sum 发送到通道 c
}

func main() {
s := []int{7, 2, 8, -9, 4, 0}

c := make(chan int)
go sum(s[:len(s)/2], c)
go sum(s[len(s)/2:], c)
x, y := <-c, <-c // 从通道 c 中接收

fmt.Println(x, y, x+y) // -5 17 12
}

下面的代码分配了三个缓冲区,但是同时使用4个造成了deadlock

1
2
3
4
5
6
7
8
9
c1 := make(chan int, 3)
c1 <- 100
c1 <- 200
c1 <- 300
c1 <- 400
fmt.Println(<-c1)
fmt.Println(<-c1)
fmt.Println(<-c1)
fmt.Println(<-c1) // fatal error: all goroutines are asleep - deadlock!

可以通过 range 关键字来实现遍历读取到的数据,类似于与数组或切片。格式如下:

1
v, ok := <-ch

如果通道接收不到数据后 ok 就为 false,这时通道就可以使用 close() 函数来关闭。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
func fibonacci(n int, c chan int) {
x, y := 0, 1
for i := 0; i < n; i++ {
c <- x
x, y = y, x+y
}
close(c)
}

func main(){
c := make(chan int, 10)
go fibonacci(cap(c), c)
for i := range c {
fmt.Println(i)
}
}

Go常用数据结构和算法

Queue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// initialization
queue := make([]int, 5)
for i := range queue {
queue[i] = i
}
// or
queue := make([]int, 0)
for i := 0; i < 5; i++ {
queue = append(queue, i)
}
// or
var queue []int
for i := 0; i < 5; i++ {
queue = append(queue, i)
}

// enque
queue = append(queue, 5)
fmt.Println(queue)

// deque
res := queue[0]
fmt.Println(res)
queue = queue[1:]
fmt.Println(queue)


Stack

uniapp开发教程

使用uni-app框架

uni-app 是一个使用 Vue.js 开发所有前端应用的框架,

开发者编写一套代码,可发布到iOS、Android、Web(响应式)、以及各种小程序(微信/支付宝/百度/头条/QQ/快手/钉钉/淘宝)、快应用等多个平台。

官网:https://uniapp.dcloud.io/resource

uni-app推荐使用HbuilderX作为项目IDE,HBuilderX是通用的前端开发工具,但为uni-app做了特别强化。

HBuilderX官网README

  1. 轻巧
    仅10余M的绿色发行包(不含插件)
  2. 极速
    不管是启动速度、大文档打开速度、编码提示,都极速响应
    C++的架构性能远超Java或Electron架构
  3. vue开发强化
    HX对vue做了大量优化投入,开发体验远超其他开发工具
    详见 按下Alt+鼠标左键可直接打开网址
  4. 小程序支持
    国外开发工具没有对中国的小程序开发优化,HX可新建uni-app小程序快应用等项目,为国人提供更高效工具
  5. markdown利器
    HX是唯一一个新建文件默认类型是markdown的编辑器,也是对md支持最强的编辑器
    HX为md强化了众多功能,请务必点击【菜单-帮助-markdown语法示例】,快速掌握md及HX的强化技巧!
  6. 清爽护眼
    HX的界面比其他工具更清爽简洁,绿柔主题经过科学的脑疲劳测试,是最适合人眼长期观看的主题界面
    详见
  7. 强大的语法提示
    HX是中国唯一一家拥有自主IDE语法分析引擎的公司,对前端语言提供准确的代码提示和转到定义(Alt+鼠标左键)
  8. 高效极客工具
    更强大的多光标、智能双击…让字处理的效率大幅提升
    了解HX的极客技巧,详见
  9. 更强的json支持
    现代js开发中大量json结构的写法,HX提供了比其他工具更高效的操作
    详见
  10. 扩展性
    HX支持java插件、nodejs插件,并兼容了很多vscode的插件及代码块。
    还可以通过外部命令,方便的调用各种命令行功能,并设置快捷键。

如果你习惯了其他工具(如vscode或sublime)的快捷键,在菜单工具-快捷键方案中可以切换。

常用操作

  • 新建项目

    文件 -> 新建 -> 项目,选择uni-app项目:

  • 运行项目
    运行 -> 运行到浏览器 -> 选择一个浏览器

这里需要注意,uni-app项目的unpackage/dist/dev/mp-alipay位置才是支付宝小程序项目的位置,

比如这里使用unigeekhall项目为例,unigeekhall的项目在/Users/yiny/Sites/unigeekhall/geekhall,

那么支付宝小程序开发者工具启动后,在支付宝小程序开发者工具中打开:/Users/yiny/Sites/unigeekhall/geekhall/unpackage/dist/dev/mp-alipay

然后在HBuilderX中修改文件并保存,会自动刷新支付宝小程序开发者工具

选择运行微信开发者工具,就可以在微信开发者工具的模拟器看到uni-app的页面了。

其他开发者工具也基本类似。

  • 目录和文件的作用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
┌─uniCloud              云空间目录,阿里云为uniCloud-aliyun,腾讯云为uniCloud-tcb(详见uniCloud)
│─components 符合vue组件规范的uni-app组件目录
│ └─comp-a.vue 可复用的a组件
├─hybrid App端存放本地html文件的目录,详见
├─platforms 存放各平台专用页面的目录,详见
├─pages 业务页面文件存放的目录
│ ├─index
│ │ └─index.vue index页面
│ └─list
│ └─list.vue list页面
├─static 存放应用引用的本地静态资源(如图片、视频等)的目录,注意:静态资源只能存放于此
├─uni_modules 存放[uni_module](/uni_modules)规范的插件。
├─unpackage 打包目录,这里有各个平台的打包文件
├─wxcomponents 存放小程序组件的目录,详见
├─main.js Vue初始化入口文件
├─App.vue 应用配置,用来配置App全局样式以及监听 应用生命周期
├─manifest.json 配置应用名称、appid、logo、版本等打包信息
├─uni.scss 整体控制应用的风格。文件里预置了一批scss变量预置。
├─static 静态资源目录,例如图片等
└─pages.json 配置页面路由、导航条、选项卡等页面类信息

微信小程序开发教程

1. 注册微信小程序

到微信公众平台官网:https://mp.weixin.qq.com/,点击右上角的立即注册:

可以选择服务号、订阅号或者小程序。服务号只有企业和组织可以注册,个人可以选择订阅号和小程序。

2. 下载安装微信开发者工具

到微信官方https://developers.weixin.qq.com/miniprogram/dev/devtools/download.html
下载微信开发者工具并安装。

3. 新建项目

打开微信开发者工具,选择创建小程序:

工程界面:

版本管理

点击右上角的版本管理图标会弹出版本管理界面:

如果没有.git和文件夹,开发者工具会为你生成一个。

其实开发者工具自带的版本管理功能就已经非常强大足够你使用了,

如果你还想使用第三方版本管理工具比如GitHub或者Gitee的话,可以使用如下命令进行关联:

1
2
git remote add github git@github.com:user/project.git
git remote add gitee git@gitee.com:user/project.git

算法与数据结构笔记

1. 关于算法的时间/空间复杂度

Big-O 名称 描述
O(1) 恒定(Constant) 最好,程序执行的结果与输入数据量大小无关,总是需要相同的时间/空间量,例如:用索引查找数组元素。
O(logN) 对数(Constant) 非常好,时间或空间性能与输入呈对数比例增长。即使对于大量的数据,这也是非常快的。例子:二进制搜索。
O(n) 线性(Linear) 良好,时间或空间性能与输入呈对数比例增长。例子:顺序搜索。
O(NlogN) 线性化(Linearithmic) 不错,比线性略差,但也不坏。
O(N^2)) 平方(Quadratic) 较慢,时间或空间性能与输入呈平方比例增长。通常使用两层嵌套循环的算法都属于此类,如:插入排序、冒泡排序。
O(N^3) 立方(Cubic) 不佳,时间或空间性能与输入呈平方比例增长。如:矩阵乘法。
O(2^N) 指数级(Exponential) 非常差,输入的少量增加即可使得运行事件加倍。如:旅行推销员问题。
O(N!) 阶乘(Factorial) 不能忍受,做任何事情都需要一百万年的时间。

1.1 O(1) - 固定时间/空间(Constant)

O(1)表示无论输入的大小怎样,一个算法都将在相同的时间或空间内完成。

1
int n = 1000;

1.2 O(n) - 线性

O(1)表示一个算法的时间或空间与输入呈线性比例增长。

1
2
3
for (int i =0; i < n; i++) {
// some constant time operation
}

1.3 O(n^2) - 平方

O(n^2)表示一个算法的时间或空间性能与输入呈平方比例增长。

1
2
3
4
5
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
// some constant time operation
}
}

1.4 O(log n) - 对数

O(log n) 表示一个算法的时间或空间性能与输入呈对数比例增长。

1
2
3
for (int i = 0; i < n; i *= 2) {
// some constant time operation
}

1.5 O(NlogN) 线性化(linearithmic)

线性化算法对于非常大的数据量来说但是比较好的,比如快速排序、归并排序、堆排序等。

1
2
3
4
5
for (int i = 0; i < n; i++ )
for (int j = 1; j < n; j *= 2) {
// some constant time operation
}
}

总结:
算法的复杂度分析可以帮助你理解一个算法的事件或者空间性能,上面各种复杂度放在一起比较可以有一个直观的认识:

2. 排序算法

2.1 冒泡排序

依次比较相邻的两个数,较小数放前面,较大数放后面,直到最大数放在最后, 然后重复操作,最后排序为升序。

演示:

第一层循环为重复的次数,循环次数为array.length-1

第二次循环为依次比较相邻的数,循环次数为array.length-1-i

1
2
3
4
5
6
7
for (int i = 0; i < arr.length - 1; i++){
for (int j = 0; j<arr.length - i -1; j++){
if (arr[j] > arr[j+1]){
swap(arr, j, j+1);
}
}
}

2.2 选择排序

选择排序是一种简单直观的排序算法,无论什么数据进去都是 O(n²) 的时间复杂度。所以用到它的时候,数据规模越小越好。唯一的好处可能就是不占用额外的内存空间了吧。

  • 算法步骤
  1. 首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置。
  2. 再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。
  3. 重复第二步,直到所有元素均排序完毕。
  • 动图演示
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public void sort(int[] arr) {
// 总共需要N-1轮比较
for(int i = 0; i <arr.length - 1; i++){
int min = i; // 最小值位置

// 每轮需要比较的次数(N-i)
for (int j = i+1; j<arr.length; j++) {
if (arr[j]< arr[min]){
min = j; // 记录最小值索引
}
}

// 每轮将找到的最小值和i位置所在的值进行交换
if (i != min){
swap(arr, i, min);
}
}
}

2.3 插入排序

插入排序的代码实现虽然没有冒泡排序和选择排序那么简单粗暴,但它的原理应该是最容易理解的了,

插入排序是一种最简单直观的排序算法,它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。

演示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public void sort(int[] arr) {
for (int i = 1; i < arr.length; i++) { // 从第2个元素开始遍历,默认第一个元素为有序的。
int key = arr[i]; // 记录要插入的数据

// 从已经排序的序列的最右边开始比较,找到比其小的数
int j = i;
while (j > 0 && key < arr[j - 1]) {
arr[j] = arr[j - 1];
j--;
}
// 存在比其小的数,插入
if (j != i)
arr[j] = key;
}
}

[6, 1, 2, 7, 9, 3, 4, 5, 10, 8]

  1. 第一轮:
1
2
3
4
5
6
7
8
9
10
11
12
13
i = 1
key = arr(1) = 1

j 由 1 循环至 0
[[6], (1), 2, 7, 9, 3, 4, 5, 10, 8]
满足j>0并且key<6, 则:
arr[j] = arr[j-1]
[[6], (6), 2, 7, 9, 3, 4, 5, 10, 8]
j--;(执行后j=0)

j(0)<i(1) ,说明存在比其小的数,则执行插入
arr[j] = key;
[[1], (6), 2, 7, 9, 3, 4, 5, 10, 8]
  1. 第二轮
1
2
3
4
5
6
7
8
9
10
11
12
i = 2;
key = arr(2) = 2
j初始=i=2,由右侧向左遍历
[[1, 6], (2), 7, 9, 3, 4, 5, 10, 8]

满足key < arr[j - 1] ,即2<arr(1) = 6, 则:
arr[j] = arr[j-1]
[[1, 6], (6), 7, 9, 3, 4, 5, 10, 8]
继续向左遍历j=1时,不满足key < arr[j - 1] ,即2< arr(0) = 1 条件不成立,因此本轮循环结束后j=1
插入:
[[1, 2, 6], 7, 9, 3, 4, 5, 10, 8]

2.4 希尔排序

通过增量(gap)将元素两两分组,对每组使用直接插入排序算法排序;增量(gap)逐渐减少,当增量(gap)减至1时,整个数据恰被分成一组,最后进行一次插入排序,整个数组就有序了。

演示:

第一轮:

第二轮:

第三轮:

参考https://zhuanlan.zhihu.com/p/122472667

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public void sort(int[] arr) {
int length = arr.length;
int temp;
for (int step = length / 2; step >= 1; step /= 2) {
for (int i = step; i < length; i++) {
temp = arr[i];
int j = i - step;
while (j >= 0 && arr[j] > temp) {
arr[j + step] = arr[j];
j -= step;
}
arr[j + step] = temp;
}
}
}

2.5 归并排序

演示:

2.6 快速排序

算法思想:选择序列中一个元素作为基准元素base,维护两个指针front和back分别指向序列的首尾,

首先用front指向的元素和base进行比较,如果大于base,则与back指向的元素进行交换,否则指针右移;

然后比较back指向的元素是否小于base,返回为true则与front指向的元素进行交换,否则指针左移。

前后交替这样就能保证较小数都放在base的左边,较大数在base的右边,然后递归调用对各部分继续排序,直到最后排好序。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public void sort(int[] sourceArray) {
quickSort(sourceArray, 0, sourceArray.length - 1);
}

private void quickSort(int[] arr, int left, int right) {

if (left > right)
return;

int i = left;
int j = right;
int pivot = arr[left]; // 取第一个元素作为基准

while (i!=j){
while (i<j && arr[j] >= pivot) // 这里需要注意:一定要右侧先开始移动!
j--;
while (i<j && arr[i] <= pivot)
i++;
if (i<j)
swap(arr, i, j);
}
arr[left] = arr[i];
arr[i] = pivot;
System.out.println(Arrays.toString(arr));
quickSort(arr, left, i - 1);
quickSort(arr, i+1, right);
}
1
[6, 1, 2, 7, 9, 3, 4, 5, 10, 8]
  1. 第一轮:

1
2
3
4
基准为6:左边找个大的,右边找个小的:
[6, 1, 2, (7), 9, 3, 4, (5), 10, 8]
交换后:
[6, 1, 2, (5), 9, 3, 4, (7), 10, 8]

1
2
3
4
继续左边找个大的,右边找个小的:
[6, 1, 2, 5, (9), 3, (4), 7, 10, 8]
交换后:
[6, 1, 2, 5, (4), 3, (9), 7, 10, 8]

1
2
3
4
5
6
交换pivot和patation index
[(6), 1, 2, 5, 4, (3), 9, 7, 10, 8]
交换后:
[(3), 1, 2, 5, 4, (6), 9, 7, 10, 8]
这样交换之后,就以pivot(6)为分界,左边都比6小,右边都比6大
[3, 1, 2, 5, 4] 6, [9, 7, 10, 8]

然后两边再次递归使用快速排序,知道每个patation都只有一个元素为止。

  1. 第二轮(左边):
1
2
3
4
5
6
7
8
[3, 1, 2, 5, 4]

右边找个小的,左边没找到,i和j碰头在元素2上面
[3, 1, (2), 5, 4]
则交换(2)和pivot(3)后:
[2, 1, 3, 5, 4]
分成
[2, 1], 3, [5, 4]
  1. 第三轮(左边):
1
2
3
[2, 1]
交换后:
[1, 2]
  1. 第四轮(左边):
1
2
3
[5, 4]
交换后:
[4, 5]

至此,第一轮左边patition部分均已排序完成

  1. 第五轮(右边):
1
2
3
4
5
6
7
8
9
10
11
[9, 7, 10, 8]

[9, 7, (10), (8)]
交换后
[9, 7, (8), (10)]
右侧继续移动后,找到patition index元素,与pivot进行交换
[(9), 7, (8), 10]
交换后:
[(8), 7, (9), 10]

[8, 7], 9 [10]
  1. 第六轮(右边):
1
2
3
[8, 7]
交换后:
[7, 8]

至此,数组排序完成:
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

2.7 堆排序

演示:

2.8 计数排序

2.9 桶排序

2.10 基数排序

Java常用数据结构与操作

数组

声明

1
2
3
String[] aArray = new String[5];
String[] bArray = {"a","b","c", "d", "e"};
String[] cArray = new String[]{"a","b","c","d","e"};

根据数组创建ArrayList

1
2
3
4
String[] stringArray = { "a", "b", "c", "d", "e" };
List<String> arrayList = new ArrayList<String>(Arrays.asList(stringArray));
System.out.println(arrayList); // [a, b, c, d, e]

数组内是否包含某个值

1
2
3
String[] stringArray = { "a", "b", "c", "d", "e" };
boolean b = Arrays.asList(stringArray).contains("a");
System.out.println(b);

注意:

1
2
3
4
int[] int_arr = {1,2,3,4};
Integer[] integer_arr = {1,2,3,4};
System.out.println(Arrays.asList(int_arr).contains(1)); // false
System.out.println(Arrays.asList(integer_arr).contains(1)); // true

声明

声明

声明

声明

ArrayList

ArrayList 类是一个可以动态修改的数组,与普通数组的区别就是它是没有固定大小的限制,我们可以添加或删除元素。
ArrayList 继承了 AbstractList ,并实现了 List 接口。

常用操作

  • 添加元素:list.add(index, element),(index不传入的话默认添加到List的最后)
  • 删除元素:list.remove(index),删除指定位置的元素,或者使用list.removeAll()删除所有元素
  • 修改元素:list.set(index, element),替换指定位置的元素
  • 获取元素:list.get(index)
  • 获取size:list.size()
  • 遍历:for(Type element : lists)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
List<Integer> list01 = new ArrayList<Integer>();
list01.add(1);
list01.add(2);
list01.add(3);
list01.add(4);
list01.add(5);
System.out.println(list01); // [1, 2, 3, 4, 5]
System.out.println(list01.get(2)); // 3

list01.add(2, 6); // 向ArrayList的索引值为2的元素前插入数值6
System.out.println(list01); // [1, 2, 6, 3, 4, 5]

list01.remove(3); // 删除ArrayList的索引值为3的元素
System.out.println(list01); // [1, 2, 6, 4, 5]

list01.set(0, 10); // 修改第一个元素为10
System.out.println(list01); // [10, 2, 6, 4, 5]
System.out.println(list01.size()); // 5

其他常用操作

方法 描述
addAll() 添加集合中的所有元素到 arraylist 中
clear() 删除 arraylist 中的所有元素
clone() 复制一份 arraylist
contains() 判断元素是否在 arraylist
indexOf() 返回 arraylist 中元素的索引值
isEmpty() 判断 arraylist 是否为空
subList() 截取部分 arraylist 的元素
sort() 对 arraylist 元素进行排序
toArray() 将 arraylist 转换为数组
toString() 将 arraylist 转换为字符串
ensureCapacity() 设置指定容量大小的 arraylist
lastIndexOf() 返回指定元素在 arraylist 中最后一次出现的位置
retainAll() 保留 arraylist 中在指定集合中也存在的那些元素
containsAll() 查看 arraylist 是否包含指定集合中的所有元素
trimToSize() 将 arraylist 中的容量调整为数组中的元素个数
removeRange() 删除 arraylist 中指定索引之间存在的元素
replaceAll() 将给定的操作内容替换掉数组中每一个元素
removeIf() 删除所有满足特定条件的 arraylist 元素
forEach() 遍历 arraylist 中每一个元素并执行特定操作

Map

创建

1
Map<Integer, Integer> numMap = new HashMap<>();

添加元素

1
numMap.put(key, value);

判断是否包含key

1
2
3
if (numMap.containsKey(key)){
// do something.
}

Python常用数据结构与操作

元组(Tuple)

zip()函数

zip() 函数用于将可迭代的对象作为参数,将对象中对应的元素打包成一个个元组,然后返回由这些元组组成的对象,这样做的好处是节约了不少的内存。

我们可以使用 list() 转换来输出列表。

如果各个迭代器的元素个数不一致,则返回列表长度与最短的对象相同,利用 * 号操作符,可以将元组解压为列表。

语法:

1
zip([iterable, ...])

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
>>>a = [1,2,3]
>>> b = [4,5,6]
>>> c = [4,5,6,7,8]
>>> zipped = zip(a,b) # 返回一个对象
>>> zipped
<zip object at 0x103abc288>
>>> list(zipped) # list() 转换为列表
[(1, 4), (2, 5), (3, 6)]
>>> list(zip(a,c)) # 元素个数与最短的列表一致
[(1, 4), (2, 5), (3, 6)]

>>> a1, a2 = zip(*zip(a,b)) # 与 zip 相反,zip(*) 可理解为解压,返回二维矩阵式
>>> list(a1)
[1, 2, 3]
>>> list(a2)
[4, 5, 6]
>>>

另一个示例:

1
2
3
test_arr = ['a','b','c','d','e']
sorted_test_arr = sorted(zip(test_arr, range(len(test_arr))))
print(sorted_test_arr) # [('a', 0), ('b', 1), ('c', 2), ('d', 3), ('e', 4)

列表(List)

列表(list)是python常用的四种collection数据类型之一,使用方括号括起来即可,方括号中的元素类型不需要相同

1
2
3
4
sample_list = [] # 空列表
list1 = ['张三','李四','王五']
list2 = [1, 2, 3, 4, 5]
list3 = [1, 2, 3, "a", "b", "c"]

列表的创建

列表取值、切片操作

1
2
3
4
5
6
7
8
9
10
list1 = [1,2,3,4,5]
print(list1[2]) # 3 (第三个元素)
print(list1[-2]) # 4 (倒数第二个元素)
print(list1[2:4]) # [3, 4] (第三个元素到第四个元素)
print(list1[2:]) # [3, 4, 5] (从第三个元素开始到最后)
print(list1[-2:]) # [3, 4, 5] (从倒数第二个元素开始到最后)
print(list1[-4:3]) # [2, 3] (从倒数第四个元素到正数第三个元素)
print(list1[::-1]) # [5, 4, 3, 2, 1] 反转
print(list1[::2]) # [1, 3, 5] (每隔两个截取一个元素)
print(list1[1:4:2]) # [2, 4] (每隔两个截取一个元素,从第二个元素开始到第五个(不包括))

添加元素

使用list.append('new element')在列表末尾追加元素:

1
2
3
sample_list =[1,2,2,4,4,4]
sample_list.append(5)
print(sample_list) # [1, 2, 2, 4, 4, 4, 5]

也可以使用list.insert(index, obj)来在列表索引值为index的元素前插入obj元素

1
2
3
sample_list =[1,2,2,4,4,4,5]
sample_list.insert(2, 3)
print(sample_list) # [1, 2, 3, 2, 4, 4, 4, 5]

删除元素

1
2
3
list1 = [1,2,3,4,5]
del list1[2]
print(list1) # [1,2,4,5]

可以使用list.pop([index=-1])方法来移除列表中的一个元素(默认最后一个元素), 并且返回该元素的值:

1
2
3
4
5
6
7
sample_list = [1, 2, 3, 4, 5]
print(sample_list.pop()) # 5
print(sample_list) # [1, 2, 3, 4]

sample_list = [1, 2, 3, 4, 5]
print(sample_list.pop(1)) # 2
print(sample_list) # [1, 3, 4, 5]

可以使用list.remove(obj)方法来移除元素:

1
2
3
4
5
6
7
sample_list = ['BMW', 'Tesla', 'Benz', 'Audi']
sample_list.remove('Tesla')
print(sample_list) # ['BMW', 'Benz', 'Audi']

sample_list1 = [1, 2, 3, 4, 5]
sample_list1.remove(3)
print(sample_list1) # [1, 2, 4, 5]

修改元素

1
2
list1 = [1,2,3,4,5]
del list1[2]

合并列表

  1. 直接使用+来合并列表
1
2
3
list1 = [1,2,3,4,5]
list2 = [6,7,8]
print(list1 + list2) # [1, 2, 3, 4, 5, 6, 7, 8]
  1. 可以使用extend方法
    在列表末尾一次性追加另一个序列中的多个值(用新列表扩展原来的列表)
1
2
3
4
list1 = [1,2,3,4,5]
list2 = [6,7,8]
list1.extend(list2)
print(list1) # [1, 2, 3, 4, 5, 6, 7, 8]
  1. 使用切片
1
2
3
4
list1 = [1,2,3,4,5]
list2 = [6,7,8]
list1[len(list1): len(list1)] = list2
print(list1) # [1, 2, 3, 4, 5, 6, 7, 8]

list1[x:x] = list2,其中x表示list2插入list1的位置

比如:

1
2
3
4
list1 = [1,2,3,4,5]
list2 = [6,7,8]
list1[2:2] = list2
print(list1) # [1, 2, 6, 7, 8, 3, 4, 5]

注意: 如果使用append来合并两个列表会变成下面的结果:

1
2
3
4
list1 = [1,2,3,4,5]
list2 = [6,7,8]
list1.append(list2)
print(list1) # [1, 2, 3, 4, 5, [6, 7, 8]]

判断数组中是否包含某元素

1
2
3
list1 = [1,2,3,4,5]
print( 2 in list1) # True
print( 0 in list1) # False

列表复制

1
2
list1 = [1,2,3,4,5]
print( list1 * 3) # [1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5]

list.count(obj)

同于统计某个元素在列表中出现的次数

1
2
sample_list =[1,2,2,4,4,4,5]
print(sample_list.count(4)) # 3

反转列表中的元素

可以使用list.reverse()来反转列表中的元素

1
2
3
sample_list = [1, 2, 3, 4, 5]
sample_list.reverse()
print(sample_list) # [5, 4, 3, 2, 1]

列表排序

使用list.sort()对原列表进行排序

1
2
3
sample_list = [1, 3, 5, 2, 4, 6]
sample_list.sort()
print(sample_list) # [1, 2, 3, 4, 5, 6]

获取最大、最小值

1
2
3
4
list01 = [1,2,3,4,5]
print(max(list01)) # 5
print(min(list01)) # 1
print(sum(list01)) # 15

字典(Dict)

定义

1
book = {'key': 'valule', ... }

访问

1
book['三国演义']

Conda

mac 安装了conda后,前面会有一个(base),是因为安装了Conda后,
每次启动终端都会自动启动Conda的base环境,Conda的环境可以使用conda env list查看

如果不想每次启动时都显示这个base,可以使用下面的命令关掉自动启动:

1
conda config --set auto_activate_base false

一次性启动或者关闭环境的命令:

1
2
conda deactivate
conda activate