复习
多路 I/O 复用模型,非阻塞 IO
单线程 上下文切换
TCP连接复用是将多个客户端的HTTP请求复用到一个服务器端TCP连接上,而HTTP复用则是一个客户端的多个HTTP请求通过一个TCP连接进行处理
TCP复用 HTTP复用
TCP复用 指的是负载均衡服务器和后台服务端之间的 TCP连接可以被不同的客户端http请求使用
HTTP复用 指的是HTTP1.1请求可以多次使用相同的TCP连接(HTTP1.0每次HTTP请求必须新建TCP链接)
AOP和IOC的概念(即spring的核心)
1.IOC:Spring是开源框架,使用框架可以使我们减少工作量,提高工作效率并且它是分层结构,即相对应的层处理对应的业务逻辑,减少代码的耦合度。而spring的核心是IOC控制反转和AOP面向切面编程。IOC控制反转主要强调的是程序之间的关系是由容器控制的,容器控制对象,控制了对外部资源的获取。而反转即为,在传统的编程中都是由我们创建对象获取依赖对象,而在IOC中是容器帮我们创建对象并注入依赖对象,正是容器帮我们查找和注入对象,对象是被获取,所以叫反转。
2.AOP:面向切面编程,主要是管理系统层的业务,比如日志,权限,事物等。AOP是将封装好的对象剖开,找出其中对多个对象产生影响的公共行为,并将其封装为一个可重用的模块,这个模块被命名为切面(aspect),切面将那些与业务逻辑无关,却被业务模块共同调用的逻辑提取并封装起来,减少了系统中的重复代码,降低了模块间的耦合度,同时提高了系统的可维护性。
雪花算法
now //时间戳
workid //机器id
n //序列号
last//上一次的时间戳
if(now==last){//说明和上次生成bit在同一时间
if(n>=4095){
sleep(1);
}
n++;
}else{
n=0;
}
last =now;
id=now<<22|workid<<12|n;
OAuth2.0
- 授权码模式(authorization code):最经典的先拿授权码再拿令牌,授权码存储在浏览器,令牌在服务器(安全)。
- 简化模式(implicit)直接拿令牌存储在服务器,不是很安全。
- 密码模式(resource owner password credentials)拿着用户的密码直接去申请令牌。
- 客户端模式(client credentials)客户端以自己的名义去申请令牌
(A)用户访问客户端,后者将前者导向认证服务器。
(B)用户选择是否给予客户端授权。
(C)假设用户给予授权,认证服务器将用户导向客户端事先指定的"重定向URI"(redirection URI),同时附上一个授权码。
(D)客户端收到授权码,附上早先的"重定向URI",向认证服务器申请令牌。这一步是在客户端的后台的服务器上完成的,对用户不可见。
(E)认证服务器核对了授权码和重定向URI,确认无误后,向客户端发送访问令牌(access token)和更新令牌(refresh token)。
Session Cookie token
Session是在服务端保存的一个数据结构,用来跟踪用户的状态,这个数据可以保存在集群、数据库、文件中;
Cookie是客户端保存用户信息的一种机制,用来记录用户的一些信息,也是实现Session的一种方式。
Token 是用户令牌,里面存储了用户信息,经过校验则可判断是否合法(不需要进行缓存),相较于进行缓存的seesion更适合多点登录。
B树 B+树
为什么mysql不用B树
1、当数据量较大时不可能把整棵树全部拉入内存,只能页一点一点的拉进去。页(节点)的大小有限所以树会变高,IO次数会变多。
2、对于区间查询过于缓慢。
Innodb和MyISAM
- InnoDB支持事务,MyISAM不支持,对于InnoDB每一条SQL语言都默认封装成事务,自动提交,这样会影响速度,所以最好把多条SQL语言放在begin和commit之间,组成一个事务;
- InnoDB支持外键,而MyISAM不支持。对一个包含外键的InnoDB表转为MYISAM会失败;
- InnoDB是聚集索引,使用B+Tree作为索引结构,数据文件是和(主键)索引绑在一起的(表数据文件本身就是按B+Tree组织的一个索引结构),必须要有主键,通过主键索引效率很高。但是辅助索引需要两次查询,先查询到主键,然后再通过主键查询到数据。因此,主键不应该过大,因为主键太大,其他索引也都会很大。
MyISAM是非聚集索引,也是使用B+Tree作为索引结构,索引和数据文件是分离的,索引保存的是数据文件的指针。主键索引和辅助索引是独立的。
也就是说:InnoDB的B+树主键索引的叶子节点就是数据文件,辅助索引的叶子节点是主键的值;而MyISAM的B+树主键索引和辅助索引的叶子节点都是数据文件的地址指针。
锁
共享锁又称为读锁,简称S锁,顾名思义,共享锁就是多个事务对于同一数据可以共享一把锁,都能访问到数据,但是只能读不能修改。
排他锁又称为写锁,简称X锁,顾名思义,排他锁就是不能与其他所并存,如一个事务获取了一个数据行的排他锁,其他事务就不能再获取该行的其他锁,包括共享锁和排他锁,但是获取排他锁的事务是可以对数据就行读取和修改。
对于普通SELECT语句,InnoDB不会加任何锁;当然我们也可以显示的加锁:
共享锁:select * from tableName where ... + lock in share more
排他锁:select * from tableName where ... + for update
TCP报文结构
- 源端口和目的端口: 各占2个字节,分别写入源端口号和目的端口号。
- 序号: 占4个字节。序号使用mod运算。TCP是面向字节流的,在一个TCP连接中传送的字节流中的每一个字节都按顺序编号。故该字段也叫做“报文段序号”。
- 确认序号: 占4个字节,是期望收到对方下一个报文段的第一个数据字节的序号。若确认序号=N,则表明:到序号N-1为止的所有数据都已正确收到。
- 数据偏移: 占4位,表示TCP报文段的首部长度。注意,“数据偏移”的单位是32位字(即以4字节长的字为计算单位)。故TCP首部的最大长度为60字节。
- 保留: 占6位,保留为今后使用,目前置为0;
- 紧急URG: 当URG=1,表明紧急指针字段有效。这时发送方TCP就把紧急数据插入到本报文段数据的最前面,而在紧急数据后面的数据仍是普通数据。
- 确认ACK: 当ACK=1时,确认字段才有效。当ACK=0时,确认号无效。TCP规定,在连接建立后所有传送的报文段都必须把ACK置1。
- 推送PSH: 接收方TCP收到PSH=1的报文段,就尽快地交付给接收应用进程,而不再等到整个缓存都填满了后再向上交付。
- 复位RST: 当RST=1时,表明TCP连接中出现严重差错,必须释放连接,然后再重新建立运输连接。
- 同步SYN: 在连接建立时用来同步序号。当SYN=1而ACK=0时,表明这是一个连接请求报文段。对方若同意建立连接,则应在响应的报文段中使SYN=1和ACK=1。故SYN置为1,就表示这是一个连接请求和连接接收报文。
- 终止FIN: 用来释放连接。当FIN=1时,表明此报文段的发送方的数据已发送完毕,并要求释放运输连接。
- 窗口: 占2个字节。窗口值作为接收方让发送方设置其发送窗口的依据。
- 检验和: 占2字节。检验和字段检验的范围包括首部和数据这两部分。和UDP数据报一样,在计算检验和时,也要在TCP报文段的前面加上12字节的伪首部。伪首部的格式与UDP用户数据报的伪首部一样,但要将伪首部第四个字段中的17 改为6(协议号),把第5字段中的UDP长度改为TCP长度。
- 紧急指针: 占2字节。紧急指针仅在URG=1时才有意义,它指出本报文段中的紧急数据的字节数。
三次握手
1、第一次握手:建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SENT状态,等待服务器确认;SYN:同步序列编号(Synchronize Sequence Numbers)。
2、第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;
3、第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED(TCP连接成功)状态,完成三次握手。
四次挥手
1、客户端进程发出连接释放报文,并且停止发送数据。释放数据报文首部,FIN=1,其序列号为seq=u(等于前面已经传送过来的数据的最后一个字节的序号加1),此时,客户端进入FIN-WAIT-1(终止等待1)状态。 TCP规定,FIN报文段即使不携带数据,也要消耗一个序号。
2、服务器收到连接释放报文,发出确认报文,ACK=1,ack=u+1,并且带上自己的序列号seq=v,此时,服务端就进入了CLOSE-WAIT(关闭等待)状态。TCP服务器通知高层的应用进程,客户端向服务器的方向就释放了,这时候处于半关闭状态,即客户端已经没有数据要发送了,但是服务器若发送数据,客户端依然要接受。这个状态还要持续一段时间,也就是整个CLOSE-WAIT状态持续的时间。
3、客户端收到服务器的确认请求后,此时,客户端就进入FIN-WAIT-2(终止等待2)状态,等待服务器发送连接释放报文(在这之前还需要接受服务器发送的最后的数据)。
4、服务器将最后的数据发送完毕后,就向客户端发送连接释放报文,FIN=1,ack=u+1,由于在半关闭状态,服务器很可能又发送了一些数据,假定此时的序列号为seq=w,此时,服务器就进入了LAST-ACK(最后确认)状态,等待客户端的确认。
5、客户端收到服务器的连接释放报文后,必须发出确认,ACK=1,ack=w+1,而自己的序列号是seq=u+1,此时,客户端就进入了TIME-WAIT(时间等待)状态。注意此时TCP连接还没有释放,必须经过2∗∗MSL(最长报文段寿命)的时间后,当客户端撤销相应的TCB后,才进入CLOSED状态。
6、服务器只要收到了客户端发出的确认,立即进入CLOSED状态。同样,撤销TCB后,就结束了这次的TCP连接。可以看到,服务器结束TCP连接的时间要比客户端早一些。
为什么连接的时候是三次握手,关闭的时候却是四次握手?
1、有可能服务端还有数据没有传送完。
为什么不能用两次握手进行连接?
1、3次握手完成两个重要的功能,既要双方做好发送数据的准备工作(双方都知道彼此已准备好),也要允许双方就初始序列号进行协商,这个序列号在握手过程中被发送和确认。
2、3次握手防止第一次SYN延迟到达,造成服务端空等待,DDOS攻击
3、测试服务端和用户端各自收发功能是否完好。
为什么TIME_WAIT状态(客户端收到第三次挥手之后的等待状态)需要经过2MSL(最大报文段生存时间)才能返回到CLOSE状态?
答:虽然按道理,四个报文都发送完毕,我们可以直接进入CLOSE状态了,但是我们必须假象网络是不可靠的,有可以最后一个ACK丢失。所以TIME_WAIT状态就是用来重发可能丢失的ACK报文。最后一个ACK丢失,服务端收不到最后一次ACK会进行第三次挥手重发。
如果已经建立了连接,但是客户端突然出现故障了怎么办?
TCP还设有一个保活计时器,显然,客户端如果出现故障,服务器不能一直等下去,白白浪费资源。服务器每收到一次客户端的请求后都会重新复位这个计时器,时间通常是设置为2小时,若两小时还没有收到客户端的任何数据,服务器就会发送一个探测报文段,以后每隔75秒钟发送一次。若一连发送10个探测报文仍然没反应,服务器就认为客户端出了故障,接着就关闭连接。
查看端口占用
netstat -ntlp //列出所有端口
netstat -ntlp |grep {端口号} //查看端口被哪个端口占用
lsof -i tcp:{端口号} //查看端口被哪个端口占用
TCB是啥?
Socket包含两部分,一个是IP地址,一个是端口号。 同一个设备可以对应一个IP地址,但不同的管道用不同的端口号区分,于是同一个设备发送给其他不同设备的信息就不会产生混乱。在同一时刻,设备可能会产生多种数据需要分发给不同的设备,为了确保数据能够正确分发,TCP协议用一种叫做TCB(Transmission Control Block,传输控制块) 的数据结构把发给不同设备的数据封装起来。
一个TCB数据块包含了数据发送双方对应的socket信息以及拥有装载数据的缓冲区。在两个设备要建立连接发送数据之前,双方都必须要做一些准备工作,分配内存建立起TCB数据块就是连接建立前必须要做的准备工作。 我们还需要了解的一点是TCP连接的建立方式,由于TCP协议建立在服务器–客户端的模式之上,因此对于两种不同角色的设备,他们发起连接的方式不一样。
TCP粘包问题
粘包问题并不是TCP协议本身问题,TCP连接只是保证数据有序完整的到达。对于区分不同包的需求,是应用层提出的。假如应用层只需要接受字节流字符串存储,自然不存在粘包问题。
解决方法
(1)发送固定长度的消息
无效位需要进行填充 接收方比较难辨认 还会造成网络资源浪费
(2)把消息的尺寸与消息一块发送(固定包头)
发送发需要额外处理长度 降低效率 接收方先需要拆解包头才能进行分析 降低效率
(3)使用特殊标记来区分消息间隔
接收方效率较低 需要逐个字节的判断尾部标记
2、接收端处理
延迟确认 等到窗口由足够大小再进行确认,可能会导致ack超时重发。
clark算法 窗口过小时直接将窗口置为0,这样发送方不会在进行发送,直到能够放下一个最大长度的报文段或缓存空间一半为空就继续接收。
HTTP 和 HTTPS 的区别?
端口 :HTTP 的 URL 由“http://”起始且默认使用端口80,而HTTPS的URL由“https://”起始且默认使用端口443。
安全性和资源消耗: HTTP 协议运行在 TCP 之上,所有传输的内容都是明文,客户端和服务器端都无法验证对方的身份。HTTPS 是运行在 SSL/TLS 之上的 HTTP 协议,SSL/TLS 运行在 TCP 之上。所有传输的内容都经过加密,加密采用对称加密,但对称加密的密钥用服务器方的证书进行了非对称加密。所以说,HTTP 安全性没有 HTTPS 高,但是 HTTPS 比 HTTP 耗费更多服务器资源。
- 对称加密:密钥只有一个,加密解密为同一个密码,且加解密速度快,典型的对称加密算法有 DES、AES 等;
- 非对称加密:密钥成对出现(且根据公钥无法推知私钥,根据私钥也无法推知公钥),加密解密使用不同密钥(公钥加密需要私钥解密,私钥加密需要公钥解密),相对对称加密速度较慢,典型的非对称加密算法有 RSA、DSA 等。
POST和GET区别
- GET请求通过URL(请求行)提交数据,在URL中可以看到所传参数。POST通过“请求体”传递数据,参数不会在url中显示
- GET请求提交的数据有长度限制,POST请求没有限制。
- GET请求返回的内容可以被浏览器缓存起来。而每次提交的POST,浏览器在你按 下F5的时候会跳出确认框,浏览器不会缓存POST请求返回的内容。
- GET对数据进行查询,POST主要对数据进行增删改!简单说,GET是只读,POST是写。GET是幂等性操作,POST是非幂等性操作。
HTTPS 过程
在建立https前现有tcp的三次握手
随机树1,2的作用是防重放攻击
一次会话对应一个密钥
TCP中有哪些定时器?
每个连接。TCP管理4个不用的定时器
- 重传定时器,使用于当希望收到另一端的确认。
- 持续定时器,零窗口探测报文,使窗口大小信息保持不断流动,即使另一端关闭了其接收窗口
- 保活定时器,检测到一个空闲连接的另一端何时崩溃或重启
- 2MSL定时器,测量一个连接处于TIME_WAIT状态的时间
索引建多了为什么不好?
- 数据量小的表不需要建立索引,建立会增加额外的索引开销;
- 数据变更需要维护索引,因此更多的索引意味着更多的维护成本;
- 更多的索引意味着也需要更多的空间(索引也是需要空间来存放的);
UDP的首部格式
1.源端口: 源端口号,需要对方回信时选用,不需要时全部置0.
2.目的端口:目的端口号,在终点交付报文的时候需要用到。
3.长度:UDP的数据报的长度(包括首部和数据)其最小值为8(只有首部)
4.校验和:检测UDP数据报在传输中是否有错,有错则丢弃。
NAT和NAPT的区别
NAPT与NAT的区别在于,NAPT不仅转换IP包中的IP地址,还对IP包中TCP和UDP的Port进行转换。这使得多台私有网主机利用1个NAT公共IP就可以同时和公共网进行通信。(NAPT多了对TCP和UDP的端口号的转换)
为什么端口数量最大限制为65535?
在TCP、UDP协议的开头,会分别有16位来存储源端口号和目标端口号,所以端口个数是
2^16-1=65535个。
http请求组成部分
[root@localhost ~]# http -v baidu.com
GET / HTTP/1.1 # 状态行包括请求方法、路径、http版本
Accept: */* # 3~7行是http的请求头
Accept-Encoding: gzip, deflate # 接收的编码
Connection: keep-alive # 连接,keep-alive表示长连接
Host: baidu.com # 域名
User-Agent: HTTPie/1.0.2 # 请求的代理
# 消息主体为空,这里没有显示,一般是由GET请求的时候它就没有消息主体
-------------------------------------------------------------------------------------
HTTP/1.1 200 OK # 下面是HTTP响应
Accept-Ranges: bytes
Cache-Control: max-age=86400
Connection: Keep-Alive
Content-Length: 81
Content-Type: text/html
Date: Fri, 02 Aug 2019 10:07:15 GMT
ETag: "51-47cf7e6ee8400"
Expires: Sat, 03 Aug 2019 10:07:15 GMT
Last-Modified: Tue, 12 Jan 2010 13:48:00 GMT
Server: Apache
<html>
<meta http-equiv="refresh" content="0;url=http://www.baidu.com/">
</html>
HTTP状态码
无法复制加载中的内容
I/O多路复用 select poll epoll 区别
Fd是句柄的意思
SELECT
1、创建bitmap存socket的fd
2、执行select,bitmap由用户态复制到内核态
3、内核态监控,收到消息的fd置为1
4、内核态复制回用户态,进行遍历然后再取数据
5、重置bitmap
POLL
1、都和select一样
2、除了使用链表加结构体替代bitmap
3、不需要单独重置fd链表
EPOLL
1、epoll_creat 创造数据结构存fd (双向链表+红黑树)
2、epoll_add 传入fd到数据结构
3、epoll_wait 开始在内核态监听
4、内核态返回监控到消息的双向链表fd回用户态
ET/LT
ET 是一次事件只会触发一次,如一次客户端发来消息,fd可读,epoll_wait返回.等下次再调用epoll_wait则不会返回了(即使缓冲区中还有数据)
LT 是一次事件会触发多次,如一次客户端发消息,fd可读,epoll_wait返回,不处理这个fd,再次调用epoll_wait,立刻返回
为什么B+树是三层
这里我们先假设 B+ 树高为 2,即存在一个根节点和若干个叶子节点,那么这棵 B+ 树的存放总记录数为:根节点指针数 * 单个叶子节点记录行数。
上文我们已经说明单个叶子节点(页)中的记录数 =16K/1K=16。(这里假设一行记录的数据大小为 1k,实际上现在很多互联网业务数据记录大小通常就是 1K 左右)。
那么现在我们需要计算出非叶子节点能存放多少指针?
其实这也很好算,我们假设主键 ID 为 bigint 类型,长度为 8 字节,而指针大小在 InnoDB 源码中设置为 6 字节,这样一共 14 字节,我们一个页中能存放多少这样的单元,其实就代表有多少指针,即 16384/14=1170。
那么可以算出一棵高度为 2 的 B+ 树,能存放 1170*16=18720 条这样的数据记录。
根据同样的原理我们可以算出一个高度为 3 的 B+ 树可以存放: 1170117016=21902400 条这样的记录。
redis的list作消息队列和发布订阅系统的区别
1.前者通过key队列方式实现,阻塞取出就删掉了,其他进程取不到;后者可以支持多订阅端获取同一个频道发布的消息。
2..如果想要持续获取消息,前者需要在程序中添加监听或定时轮询;后者不会出现这样的情况,订阅端自己本身就会时刻监听。
进程间的通信方式
- 管道/匿名管道(Pipes) :用于具有亲缘关系的父子进程间或者兄弟进程之间的通信。
- 有名管道(Names Pipes) : 匿名管道由于没有名字,只能用于亲缘关系的进程间通信。为了克服这个缺点,提出了有名管道。有名管道严格遵循先进先出(first in first out) 。有名管道以磁盘文件的方式存在,可以实现本机任意两个进程通信。
- 信号(Signal) :信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生;
- 消息队列(Message Queuing) :消息队列是消息的链表,具有特定的格式,存放在内存中并由消息队列标识符标识。管道和消息队列的通信数据都是先进先出的原则。与管道(无名管道:只存在于内存中的文件;命名管道:存在于实际的磁盘介质或者文件系统)不同的是消息队列存放在内核中,只有在内核重启(即,操作系统重启)或者显式地删除一个消息队列时,该消息队列才会被真正的删除。消息队列可以实现消息的随机查询,消息不一定要以先进先出的次序读取,也可以按消息的类型读取.比 FIFO 更有优势。消息队列克服了信号承载信息量少,管道只能承载无格式字 节流以及缓冲区大小受限等缺点。
- 信号量(Semaphores) :信号量是一个计数器,用于多进程对共享数据的访问,信号量的意图在于进程间同步。这种通信方式主要用于解决与同步相关的问题并避免竞争条件。
- 共享内存(Shared memory) :使得多个进程可以访问同一块内存空间,不同进程可以及时看到对方进程中对共享内存中数据的更新。这种方式需要依靠某种同步操作,如互斥锁和信号量等。可以说这是最有用的进程间通信方式。
- 套接字(Sockets) : 此方法主要用于在客户端和服务器之间通过网络进行通信。套接字是支持 TCP/IP 的网络通信的基本操作单元,可以看做是不同主机之间的进程进行双向通信的端点,简单的说就是通信的两方的一种约定,用套接字中的相关函数来完成通信过程。
线程间的同步的方式
- 互斥量(Mutex) :采用互斥对象机制,只有拥有互斥对象的线程才有访问公共资源的权限。因为互斥对象只有一个,所以可以保证公共资源不会被多个线程同时访问。比如 Java 中的 synchronized 关键词和各种 Lock 都是这种机制。
- 信号量(Semphares) :它允许同一时刻多个线程访问同一资源,但是需要控制同一时刻访问此资源的最大线程数量。
- 事件(Event) :Wait/Notify:通过通知操作的方式来保持多线程同步,还可以方便的实现多线程优先级的比较操作。
线程和进程的区别
进程:是并发执行的程序在执行过程中分配和管理资源的基本单位,是一个动态概念,竞争计算机系统资源的基本单位。
线程:是进程的一个执行单元,是进程内科调度实体。比进程更小的独立运行的基本单位。线程也被称为轻量级进程。
一个程序至少一个进程,一个进程至少一个线程。
死锁的四个条件
- 互斥:资源必须处于非共享模式,即一次只有一个进程可以使用。如果另一进程申请该资源,那么必须等待直到该资源被释放为止。
- 占有并等待:一个进程至少应该占有一个资源,并等待另一资源,而该资源被其他进程所占有。
- 非抢占:资源不能被抢占。只能在持有资源的进程完成任务后,该资源才会被释放。
- 循环等待:有一组等待进程
{P0, P1,..., Pn}
,P0
等待的资源被P1
占有,P1
等待的资源被P2
占有,......,Pn-1
等待的资源被Pn
占有,Pn
等待的资源被P0
占有。
volatile和synchronized的区别
- volatile本质是在告诉jvm当前变量在寄存器(工作内存)中的值是不确定的,需要从主存中读取; synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住。
- volatile仅能使用在变量级别;synchronized则可以使用在变量、方法、和类级别的
- volatile仅能实现变量的修改可见性,不能保证原子性;而synchronized则可以保证变量的修改可见性和原子性
- volatile不会造成线程的阻塞;synchronized可能会造成线程的阻塞。
- volatile标记的变量不会被编译器优化;synchronized标记的变量可以被编译器优化【’;‘
类加载器
Bean
JVM内存泄漏
top
top -Hp
jinfo
jstack
jstat
jmap -dump:format
遍历HashMap
Map<Integer, String> map = new HashMap();
for(Integer key : map.keySet()) {
System.out.println(key);
System.out.println(map.get(key));
}
堆排序
public int[] sortArray(int[] nums) {
return sort(nums,0,nums.length-1);
}
public int[] sort(int[] nums,int start,int end){
for(int i=(end-1)/2;i>=0;i--){
adjust(nums,i,end);
}
for(int i=end;i>=1;i--){
swap(nums,i,0);
adjust(nums,0,i-1);
}
return nums;
}
public void adjust(int[] nums,int start,int end){
int temp=nums[start];
int index=start;
for(int i=start*2+1;i<=end;i=i*2+1){
if(i+1<=end&&nums[i+1]>nums[i]){
i++;
}
if(nums[i]>temp){
nums[index]=nums[i];
index=i;
}else{
break;
}
}
nums[index]=temp;
return ;
}
public void swap(int[] nums,int x,int y){
int t=nums[x];
nums[x]=nums[y];
nums[y]=t;
}
快排序
class Solution {
public int findKthLargest(int[] nums, int k) {
sort(nums,0,nums.length-1);
return nums[nums.length-k];
}
public void sort(int[] nums,int left,int right){
if(left<right){
int t=adjust(nums,left,right);
sort(nums,left,t-1);
sort(nums,t+1,right);
}
}
public int adjust(int[] nums,int left,int right){
int tmp=nums[left];
int index=left+1;
for(int i=left+1;i<=right;i++){
if(nums[i]<tmp){
swap(nums,i,index);
index++;
}
}
swap(nums,left,index-1);
return index-1;
}
public void swap(int[] nums,int x,int y){
int t=nums[x];
nums[x]=nums[y];
nums[y]=t;
}
}
归并排序
public void merge(int[] nums,int start1,int end1,int start2,int end2){
int []temp = new int[nums.length];
int x=start1;
int y=start2;
int t=0;
while(x<=end1&&y<=end2){
if(nums[x]>nums[y]){
temp[t++]=nums[y++];
}else{
temp[t++]=nums[x++];
}
}
while(x<=end1){
temp[t++]=nums[x++];
}
while(y<=end2){
temp[t++]=nums[y++];
}
for(int i=start1;i<=end2;i++){
nums[i]=temp[i-start1];
}
}
public void swap(int[] nums,int x,int y){
int t=nums[x];
nums[x]=nums[y];
nums[y]=t;
}