0%

  1. 首先在系统中安装Anaconda。可以打开命令行输入conda -v 检查是否安装以及当前conda版本。

  2. conda常用命令

    1)conda list 查看安装了哪些包

    2)conda env list 或 conda info -e 查看当前存在哪些虚拟环境

    3)conda update conda 检查更新当前conda

    4)conda update -all 更新本地已安装的包

  3. 创建Python虚拟环境

    使用 conda create -n your_env_name python=X.X anaconda命令创建python版本为x.x,名字为your_env_name的虚拟环境。your_env_name目录可在Anaconda安装目录envs目录下找到。

  4. 使用激活(或切换不同python版本)的虚拟环境。打开命令行输入python —version可以检查当前python版本。

    source activate your_env_name

  5. 对虚拟环境中安装额外的包使用命令,使用命令 conda install -n your_env_name [pachage] 即可安装packageyour_env_name 中; 也可以激活环境后使用 pip 进行安装

  6. 关闭虚拟环境使用默认 path 中配置的 python 版本 使用命令如下:

    source deactivate

  7. 删除虚拟环境

    conda remove -n your_env_name -all

  8. 删除环境中的某个包

    conda remove --name your_env_name package_name 即可

什么是OPENVPN

​ VPN直译就是虚拟专用通道是提供给企业之间或者个人与公司之间安全数据传输的隧道OpenVPN无疑是Linux下开源VPN的先锋提供了良好的性能和友好的用户GUI,使用了OpenSSL加密库中的SSLv3/TLSv1协议函数库,目前OpenVPN能在Solaris、Linux、OpenBSD、FreeBSD、NetBSD、Mac OS X与Microsoft Windows以及Android和iOS上运行并包含了许多安全性的功能。它并不是一个基于Web的VPN软件也不与IPsec及其他VPN软件包兼容。

原理

​ openvpn技术核心是虚拟网卡然后是ssl协议实现。虚拟网卡使用网络底层编程实现的软件驱动。安装后在主机上多出一个网卡可以向其他网卡一样进行配置。服务程序可以在应用层打开虚拟网卡如果应用软件如IE向虚拟网卡发送数据则服务程序可以读取到该数据如果服务程序写合适的数据到虚拟网卡应用软件也可以接收得到。虚拟网卡在很多的操作系统下都有相应的实现这也是OpenVpn能够跨平台一个很重要的理由。

​ 在OpenVpn中如果用户访问一个远程的虚拟地址属于虚拟网卡配用的地址系列区别于真实地址则操作系统会通过路由机制将数据包TUN模式或数据帧TAP模式发送到虚拟网卡上服务程序接收该数据并进行相应的处理后通过SOCKET从外网上发送出去远程服务程序通过SOCKET从外网上接收数据并进行相应的处理后发送给虚拟网卡则应用软件可以接收到完成了一个单向传输的过程反之亦然。

OPENVPN搭建

yum安装

1
yum install openvpn easy-rsa -y

配置服务器初始化

1
2
3
4
5
6
7
8
9
10
11
12
# 注openvpn-2.4.3这个目录是以当前openvpn版本命名的
cp /usr/share/doc/openvpn-2.4.3/sample/sample-config-files/server.conf /etc/openvpn/
cp -r /usr/share/easy-rsa/2.0/* /etc/openvpn/
# 修改vars文件中的 变量作为服务端ca证书的缺省配置
# export KEY_COUNTRY="CN"
# export KEY_PROVINCE="BJ"
# export KEY_CITY="Beijing"
# export KEY_ORG="IT"
# export KEY_EMAIL="admin@admin.com"
vi vars
# 修改vars权限为可执行
chmod +x vars

证书配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 清空已有证书
./clean-all

# 生成服务端证书
# 下面这个命令在第一次安装时可以运行以后在添加完客户端后慎用因为这个命令会清除所有已经生成的证书密钥和上面的提示对应
# 由于之前做过缺省配置vars一路确认即可
./build-ca
# 生成服务端秘钥证书,名字可以随便起但是后面会用到
./build-key-server server

# 生成diffie hellman参数用于增强openvpn安全性,生成需要漫长等待
./build-dh

# 生成客户端
# 名字任意建议写成你要发给的人的姓名方便管理这里与生成服务端证书配置类似中间一步提示输入服务端密码也可以不设置密码其他按照缺省提示一路回车即可
./build-key client

服务器端配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
cat /etc/openvpn/server.conf
port 11940
proto tcp
dev tun
ca keys/ca.crt
cert keys/server.crt
key keys/server.key
dh keys/dh2048.pem
server 10.18.18.0 255.255.255.0
ifconfig-pool-persist ipp.txt
keepalive 10 120
comp-lzo
persist-key
persist-tun
status openvpn-status.log
log /var/log/openvpn/openvpn.log
log-append /var/log/openvpn/openvpn.log
push "route 172.16.10.0 255.255.255.0"
mode server
client-to-client
verb 3
duplicate-cn

配置项说明:

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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
;local a.b.c.d  //设置监听IP默认是监听所有IP
port 11940 //设置监听端口必须要对应的在防火墙里面打开
proto tcp //设置用TCP还是UDP协议
dev tun // 设置创建tun的路由IP通道还是创建tap的以太网通道由于路由IP容易控制所以推荐使用tunnel

// certificate(cert), and private key (key)// ca文件是服务端和客户端都必须使用的但不需要ca.key// 服务端和客户端指定各自的.crt和.key// 请注意路径,可以使用以配置文件开始为根的相对路径,// 也可以使用绝对路径// 请小心存放.key密钥文件
ca keys/ca.crt
cert keys/server.crt
key keys/server.key

// 指定Diffie hellman parameters.默认是2048如果生成ca的时候修改过dh参数“export KEY_SIZE”则改为对应的数字
dh keys/dh2048.pem


// 配置VPN使用的网段OpenVPN会自动提供基于该网段的DHCP服务但不能和任何一方的局域网段重复保证唯一server 10.18.18.0 255.255.255.0

// 维持一个客户端和virtual IP的对应表以方便客户端重新连接可以获得同样的IP
ifconfig-pool-persist ipp.txt


// 设置服务端检测的间隔和超时时间 每 10 秒 ping 一次如果 120 秒没有回应则认为对方已经 down
keepalive 10 120

// 使用lzo压缩的通讯,服务端和客户端都必须配置
comp-lzo

// 重启时仍保留一些状态
persist-keypersist-tun

// 输出短日志,每分钟刷新一次,以显示当前的客户端
status openvpn-status.log

// 缺省日志会记录在系统日志中但也可以导向到其他地方/
/ 建议调试的使用先不要设置,调试完成后再定义
log /var/log/openvpn/openvpn.log
log-append /var/log/openvpn/openvpn.log


// 这里主要填写openvpn所在局域网的网段我的openvpn所在的局域网是172.16.10.0
push "route 172.16.10.0 255.255.255.0"


// 默认客户端之间是不能直接通讯的除非把下面的语句注释掉
client-to-client


//持久化选项可以尽量避免访问在重启时由于用户权限降低而无法访问的某些资源//指定日志文件的记录详细级别可选0-9等级越高日志内容越详细
verb 3


// 常用于测试开启的话一个证书可以多个客户端连接
duplicate-cn

创建日志目录

1
mkdir -p /var/log/openvpn/

启动OPENVPN服务

1
systemctl start openvpn@server.service

设置开机启动

1
systemctl enable openvpn@server.service

开启路由转发功能

1
2
3
vim /etc/sysctl.conf
# 找到net.ipv4.ip_forward = 0 把0改成1
sysctl -p

iptables设置

这一条至关重要通过配置nat将vpn网段IP转发到server内网,10.18.18.0/24是vpn网段

1
2
3
4
5
# 智能选择
iptables -t nat -A POSTROUTING -s 10.18.18.0/24 -j MASQUERADE
iptables -A INPUT -p TCP --dport 11940 -j ACCEPT
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
service iptables save

客户端使用

将服务器端生成的key

ca.crt

client.crt

client.key

下载到本地

复制OpenVPN目录将sample-config下的client.ovpn下载到本地

将client.ovpn配置文件做相应的修改

1
2
3
4
5
6
7
8
9
client
dev tun
proto tcp
remote 1.2.3.5 11940
ca ca.crtcert
client.crt
key client.key
comp-lzo
verb 3

客户端可安装Tunnelblick进行连接。

OPENVPN用户管理

证书有效期管理

​ 默认的OpenVPN配置,客户端证书有效期是10年。如何自定义客户端证书的时间呢?其实比较简单,编辑vars文件,找到export KEY_EXPIRE=3650这一行,把默认的3650,改为你想设置的天数即可。编辑后保存,运行一次vars,设置好环境变量,再用build-key生成客户端证书,即可。这样一来,客户端证书的有效期,就是你所设置的有效期了。

客户端证书的吊销

​ 和PPTP VPN不一样,PPTP VPN直接删除客户端帐号,就可以了。在OpenVPN中,是通过revoke操作,吊销客户端证书,来实现禁止客户端连接OpenVPN的。 具体的方法如下:

1
2
3
4
5
6
#进入OpenVPN配置文件所在目录
#执行vars,初始化环境
. vars
#使用revoke-full命令,吊销客户端证书
./revoke-full clientName
#clientName是被吊销的客户端证书名称

​ 命令执行后,我们能在keys目录中找到一个文件,名叫:crl.pem ,这个文件中包含了吊销证书的名单。然后,在服务端配置文件中,加入如下一行:

1
crl-verify crl.pem

最后一步,重启OpenVPN服务,即可

1. nginx下载

​ 下载地址: http://nginx.org/en/download.html

​ 选择安装版本:1.13.9

2.nginx依赖

  • GCC

​ 依赖版本:4.8.5

  • pcre

​ 依赖版本:8.32

  • gcc-c++

​ 依赖版本:4.8.5

  • zlib-devel

​ 依赖版本:1.2.7

3.安装

​ rpm包下载地址 https://rpmfind.net/linux/RPM/

​ 安装依赖包过程中会出现缺少依赖包,或者依赖包版本号较低的情况;缺少依赖包下载缺省依赖包安装,依赖包版本低的情况,下载正确版本使用

1
rmp -ivh xxxx.rpm --force

命令强制安装。

​ 其中在阿里云centos 7上安装总共下载的依赖包:

​ 解压 nginx-1.13.9.tar.gz

1
2
3
4
5
tar -xzvf nginx-1.13.9.tar.gz
cd nginx-1.13.9
./configure
make
make install

​ 根据安装日志发现nginx默认安装在/usr/local/nginx目录下

nginx常用命令

1
2
3
./nginx/sbin/nginx             # 启动nginx
./nginx/sbin/nginx stop # 停止
./nginx/sbin/nginx -s reload # 重新加载配置

什么是RPM

​ RPM是RedHat Package Manager(RedHat软件包管理工具)类似windows里的“添加删除程序”

RPM包种类

​ RPM安装包有两类:二进制包(Binary)以及源代码包(Source)两种。二进制包可以直接安装在计算机中,而源代码包将会由RPM自动编译、安装。源代码包经常以src.rpm座位后缀。

常用命令

1
2
3
4
5
6
7
-ivh 安装显示安装进度 --install--verbose--hash
-Uvh 升级软件包
-qpl 列出RPM软件包内的文件信息
-qpi 列出RPM软件包的描述信息
-qf 查找指定文件数据那个RPM软件包
-Va 检查所有的RPM包,查找丢失的文件[View Lost]
-e 删除包

RPM源代码包安装

1
2
rpm --recompile vim-4.6-4.src.rpm #该命令会把源代码解包并编译、安装
rpm --rebuild vim-4.6-4.src.rpm #在安装完后,还会把生成的可执行文件重新包装成i386.rpm的RPM软件包

离线安装rabbitmq

安装包下载

版本介绍:https://www.rabbitmq.com/install-rpm.html

rabbitmq版本: 3.6.10

1
wget http://www.rabbitmq.com/releases/rabbitmq-server/v3.6.10/rabbitmq-server-generic-unix-3.6.10.tar.xz

erlang版本:

https://www.erlang.org/downloads

otp_src_20.2.tar.gz

erlang安装

安装中提示 curses notfond 手动安装 cureses包

http://ftp.gnu.org/gnu/ncurses/ 下载最新版ncureses

1
2
3
4
5
# tar -zxvf ncurses-6.1.tar.gz
# cd ncurses-6.0
# ./configure --with-shared --without-debug --without-ada --enable-overwrite
# make
# make install

安装erlang

1
2
3
4
5
6
7
cd otp_src_19.3
./configure -prefix=/mnt/data/erlang
make
make install
cd /mnt/data/erlang
cd bin
ln -s /mnt/data/erlang/bin/erl /usr/local/bin/erl

rabbitmq安装

rpm -ivh rabbitmq-server-3.7.4-1.el7.noarch.rpm

提示

1
2
3
4
tar -Jxvf rabbitmq-server-generic-unix-3.6.10.tar.xz 
tar -xvf rabbitmq-server-generic-unix-3.6.10.tar
cp -R rabbitmq_server-3.6.10/ /mnt/data/rabbitmq/
chmod -R 755 /mnt/data/rabbitmq/sbin

rabbitmq基本操作

rabbitMQ启动

1
2
cd /mnt/data/rabbitmq/sbin
nohup ./rabbitmq-server &

开启Web管理插件

1
2
cd /usr/local/rabbitmq/sbin
./rabbitmq-plugins enable rabbitmq_management

rabbitMQ重启

1
2
3
cd /usr/local/rabbitmq/sbin
./rabbitmqctl stop ## 停应用
./rabbitmq-server ## 启动应用

通过web管理

访问http://localhost:15672
默认用户/密码 : guest/guest
注意初始状态只能本地登录,不可通过远程web访问,给权限也不行。需要远程访问的,必须新建用户并赋权限。

用户管理并赋权限

  1. 添加用户
    如用户名:mqadmin 密码:mqadmin
1
2
cd /usr/local/rabbitmq/sbin
./rabbitmqctl add_user mqadmin mqadmin
  1. 给用户添加角色
    角色有none、management、policymaker、monitoring、administrator等,administrator为超级管理员
1
./rabbitmqctl set_user_tags mqadmin administrator
  1. 给用户授权
1
./rabbitmqctl set_permissions -p / mqadmin '.*' '.*' '.*'

在浏览器中输入:http://ip:15672

端口 说明
4369 epmd, a peer discovery service used by RabbitMQ nodes and CLI tools
5672, 5671 used by AMQP 0-9-1 and 1.0 clients without and with TLS
25672 used by Erlang distribution for inter-node and CLI tools communication and is allocated from a dynamic range (limited to a single port by default, computed as AMQP port + 20000). See networking guide for details.
15672 HTTP API clients and rabbitmqadmin (only if the management plugin is enabled)
61613, 61614 STOMP clients without and with TLS (only if the STOMP plugin is enabled)
1883, 8883 (MQTT clients without and with TLS, if the MQTT plugin is enabled
15674 STOMP-over-WebSockets clients (only if the Web STOMP plugin is enabled)
15675 MQTT-over-WebSockets clients (only if the Web MQTT plugin is enabled)

机器学习-模型评估

评价指标

​ 评价指标是机器学习中重要的一环。不同的机器学习任务有着不同的评价指标,同一种机器学习任务也有着不同的评价指标,每个指标的着重点不同。如分类(classification)、回归(regression)、排序(ranking)、聚类(clustering)、热门主题模型(topic modeling)、推荐(recommendation)等。并且很多指标可以多种不同机器学习进行评价,如精确率-召回率(precision-recall),可以用在分类、推荐、排序等中;像分类、回归、排序都是监督式机器学习。

分类评价指标

​ 分类是指对指定的数据记录预测该记录所属的类别。并且类别空间已知。它包括二分类、多分类;二分类便是指只有两种类别,如垃圾邮件分类

准确率(Accuracy)

平均准确率(Average Pre-class Accuracy)

对数损失函数(Log-loss)

精确率-召回率(Precision-Recall)

F1-score

AUC(Area under the Cure(Receiver Operating Characeristic,ROC))

混淆矩阵(Confusion Matrix)

附录

http://blog.csdn.net/heyongluoyao8/article/details/49408319

设计模式

看懂UML类图

从一个示例开始

  • 车的类图结构为<>,表示车是一个抽象类;
  • 它有两个继承类:小汽车和自行车;它们之间的关系为实现关系,使用带空心箭头的虚线表示;
  • 小汽车为与SUV之间也是继承关系,它们之间的关系为泛化关系,使用带空心箭头的实线表示;
  • 小汽车与发动机之间是组合关系,使用带实心箭头的实线表示;
  • 学生与班级之间是聚合关系,使用带空心箭头的实线表示;
  • 学生与身份证之间为关联关系,使用一根实线表示;
  • 学生上学需要用到自行车,与自行车是一种依赖关系,使用带箭头的虚线表示;

类之间的关系

泛化关系(generalization)

类的继承结构表现在UML中为:泛化(generalize)与实现(realize):

泛化是一种继承关系用空心箭头表示

实现是一种实现关系用空心虚线箭头表示

聚合关系(aggregation)

聚合关系用一条带空心菱形箭头的直线表示,如下图表示A聚合到B上,或者说B由A组成;

组合关系(composition)

组合关系用一条带实心菱形箭头表示,如下图A组成B;

关联关系(association)

关联关系是用一条直线表示的;它描述不同类的对象之间的结构关系;它是一种静态关系, 通常与运行状态无关,一般由常识等因素决定的;它一般用来定义对象之间静态的、天然的结构; 所以,关联关系是一种“强关联”的关系;

比如,乘车人和车票之间就是一种关联关系;学生和学校就是一种关联关系;

关联关系默认不强调方向,表示对象间相互知道;如果特别强调方向,如下图,表示A知道B,但 B不知道A;

依赖关系(dependency)

依赖关系使用一套带箭头的虚线表示;如下图表示A依赖B;他描述一个对象在运行期间会用到另一个对象的性关系;

创建型模式

简单工程模式(Simple Factory Pattern)

模式动机

​ 考虑一个简单的软件应用场景,一个软件系统可以提供多个外观不同的按钮(如,圆形、方形、菱形),这些按钮都源自同一个基类,继承后不同的子类修改了部分属性从而使得他们可以呈现不同的外观,如果我们希望使用这些按钮时,不需要知道这些按钮类的具体名字,只需要知道表示一个按钮类的一个参数,并提供一个调用方便的方法,把该参数传入方法即可返回一个相应的按钮对象,此时就可以使用简单工厂模式。

模式定义

​ 简单工厂模式:又称静态工厂方法模式,它属于类创建型模式。在简单工厂模式中,可根据参数的不同返回不同类的实例。简单工厂模式专门定义一个类负责创建其他类的实例,被创建的实例通常具有共同的父类。

模式结构

​ 简单工厂模式包含如下角色:

  • Factory:工厂角色

    工厂角色 负责创建所有实例的内部逻辑

  • Product:抽象产品角色

    抽象产品角色是创建所有对象的父类,负责描述所有实例所共有公共接口

  • ConcreteProduct:具体产品角色

    具体产品角色是创建目标,所有创建的对象都充当这个角色的某个具体类的实例

使用案例

​ DateFormat、KeyGenerator.getInstance(“DESede”)、Cipher.getInstance(“DESede”)等

工厂方法模式(Factory Method Pattern)

模式动机

​ 针对简单工厂模式,当按钮种类增加时,需要对工厂类进行修改;特别是种类增加到一定量时,增加开发维护难度;此时考虑将工厂方法进行抽象,具体的按钮创建交给不同的工厂子类来实现;这种改良使得整体设计更加符合“开闭原则”

模式定义

工厂方法模式又成为工厂模式,也叫虚拟构造器模式、多态工厂模式;它属于类创建型模式。在工厂方法模式中工厂类负责定义创建产品的公共接口,而工厂子类负责生成具体的产品对象,这样做的目的是产品创建延后,使具体的产品使用者不用关注产品的创建过程。

模式结构

工厂方法模式包含如下角色:

  • Product 抽象产品
  • ConcreteProduct 具体产品
  • Factory抽象工厂
  • ConcreteFactory具体工厂

使用案例

JDBC中的工厂方法

1
2
3
4
5
//Connection创建Statemeng:
Connection conn=DriverManager.getConnection("jdbc:microsoft:sqlserver://loc
alhost:1433; DatabaseName=DB;user=sa;password=");
Statement statement=conn.createStatement();
ResultSet rs=statement.executeQuery("select * from UserInfo");

抽象工程模式(Abstract Factory)

模式动机

​ 在工厂方法模式中具体的工厂负责生产具体的产品,每一个具体的工厂对应一个具体的产品,工厂方法也具有唯一性,有时候我们需要一个工厂可以提供多个产品对象,而不是单一的产品对象。

模式定义

​ 抽象工厂模式(Abstract Factory Pattern):提供一系列相关相互依赖的接口,而无需指定他们具体的类。抽象工厂模式又称Kit模式,属于对象创建型模式。

模式结构

  • AbstractFactory 抽象工厂
  • ConcreteFactory 具体工厂
  • AbstractProduct 抽象产品
  • ConcreteProduct 具体产品

建造者模式

模式动机

​ 在软件开复杂对象中开发过程中,存在大量类似汽车一样的复杂对象,他们拥有一些列的成员属性,这些成员属性中有些是引用类型的成员对象。而在这些复杂对象中还可能存在一些限制条件,如默写属性没有复制不能做一个完整的产品使用,以及属性的初始化顺序等。

模式定义

​ 建造者模式(Builder Pattern):将复杂的对象的构建与他的表示分离,使得同样的构建可以创造不同的表示。

​ 建造者模式是一步步创建一个复杂对象的过程,它允许用户指定复杂对象的类型和内容j就可以构建他们,用户不需要知道内部具体的构建细节。

模式结构

建造者的模式角色包含如下角色:

  • Builder:抽象建造者
  • ConcreteBuilder:具体的建造者
  • Director:指挥者
  • Product:产品

单例模式

结构型模式

适配器模式

桥接模式

装饰模式

外观模式

享元模式

代理模式

行为型模式

命令模式

中介者模式

观察者模式

状态模式

策略模式

附录

摘自 https://design-patterns.readthedocs.io/zh_CN/latest/read_uml.html#id1

堆排序

堆排序(Heapsort)是指利用[堆]这种数据结构所设计的一种排序算法。堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。

堆节点的访问

通常堆是通过一维数组来实现的。在数组起始位置为0的情形中:

  • 父节点i的左子节点位置(2i+1);
  • 父节点i的右子节点位置(2i+2);
  • 子节点i的父节点在位置floor((i-1)/2);

堆的操作

在堆的数据结构中,堆中最大值总是位于根节点(在优先队列中使用堆的话堆中的最小值位于根节点)。堆中定义以下几种操作:

  • 最大堆调整(Max_Heapify): 将堆末端的子节点做调整,使得子节点永远小于父节点
  • 创建最大堆(Build_Max_Heap): 将堆中所有数据重新排序
  • 堆排序(HeapSort):移除在第一个数据的根节点,并做最大堆调整(递归运算)

实现示例

JAVA

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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
import java.util.Arrays;

public class HeapSort {

private int[] arr;

public HeapSort(int[] arr){
this.arr = arr;
}

/**
* 堆排序的主要入口方法,共两步。
*/
public void sort(){
/*
* 第一步:将数组堆化
* beginIndex = 第一个非叶子节点。
* 从第一个非叶子节点开始即可。无需从最后一个叶子节点开始。
* 叶子节点可以看作已符合堆要求的节点,根节点就是它自己且自己以下值为最大。
*/
int len = arr.length - 1;
int beginIndex = (len - 1) >> 1;
for(int i = beginIndex; i >= 0; i--){
maxHeapify(i, len);
}

/*
* 第二步:对堆化数据排序
* 每次都是移出最顶层的根节点A[0],与最尾部节点位置调换,同时遍历长度 - 1。
* 然后从新整理被换到根节点的末尾元素,使其符合堆的特性。
* 直至未排序的堆长度为 0。
*/
for(int i = len; i > 0; i--){
swap(0, i);
maxHeapify(0, i - 1);
}
}

private void swap(int i,int j){
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}

/**
* 调整索引为 index 处的数据,使其符合堆的特性。
*
* @param index 需要堆化处理的数据的索引
* @param len 未排序的堆(数组)的长度
*/
private void maxHeapify(int index,int len){
int li = (index << 1) + 1; // 左子节点索引
int ri = li + 1; // 右子节点索引
int cMax = li; // 子节点值最大索引,默认左子节点。

if(li > len) return; // 左子节点索引超出计算范围,直接返回。
if(ri <= len && arr[ri] > arr[li]) // 先判断左右子节点,哪个较大。
cMax = ri;
if(arr[cMax] > arr[index]){
swap(cMax, index); // 如果父节点被子节点调换,
maxHeapify(cMax, len); // 则需要继续判断换下后的父节点是否符合堆的特性。
}
}

/**
* 测试用例
*
* 输出:
* [0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9]
*/
public static void main(String[] args) {
int[] arr = new int[]{3,5,3,0,8,6,1,5,8,6,2,4,9,4,7,0,1,8,9,7,3,1,2,5,9,7,4,0,2,6};
new HeapSort(arr).sort();
System.out.println(Arrays.toString(arr));
}

}

说明

摘自 https://zh.wikipedia.org/wiki/%E5%A0%86%E6%8E%92%E5%BA%8F#Java

用做笔记