日期: 2021 年 6 月 1 日

sip服务器搭建

写这篇博客,距离我上次提到yate已经有很长一段时间了,今天的主题是:在ubuntu 12.04下搭建sip服务器,并且实现与数据库的连接,从而开发一款互联网电话。

至于yate的简介等,这里免了!直接进入主题,下载源码并安装,下载地址:

http://yate.null.ro/pmwiki/index.php?n=Main.Download

也可以在ubuntu软件中心搜索yate,然后安装*个即可!

解压后,编译并安装:

./configure

make engine

make modules

make install-noapi

之后,进行配置,至于如何在regfile.conf里注册用户,我就不啰嗦了,文件末尾已经有了提示。(/etc/yate/是配置文件路劲)

这里假定你已经装好了mysql,如果没装好,那你自行安装吧!

接下来,在phpmyadmin里创建一个用户yate,数据库yate,至于密码,你自己决定,然后打开新建的yate表,创建table users:

CREATE TABLE `users` (
`username` varchar(50) NOT NULL,
`password` varchar(50) DEFAULT ‘9999’,
`location` varchar(255) DEFAULT NULL,
`expires` datetime DEFAULT NULL,
PRIMARY KEY (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

至此,数据库创建好了!接下来,需要修改两个文件:register.conf、mysqldb.conf,这两文件都在/etc/yate下
mysqldb.conf 改为下面的这样:

[yate];这个是连接引用名
host=localhost
port=3306
database=yate
user=yate
password=something

不过,请改为和你设置的相同,连接用名可以不改。
*后,修改register.conf

[general]
user.auth=yes
user.register=yes
user.unregister=yes
call.route=yes
fallback=yes
[default]
priority=50
;mysqldb.conf里的连接名,我你是yate
account=yate
[user.auth]
query=SELECT password FROM users WHERE username=’${username}’ AND password IS NOT NULL AND password<>”
result=password

[user.register]
;用户登陆后,更新数据库
query=UPDATE users SET location=’${data}’,expires=CURRENT_TIMESTAMP + INTERVAL ${expires} SECOND WHERE username=’${username}’

[user.unregister]
;用户注销后的操作
query=UPDATE users SET location=NULL,expires=NULL WHERE expires IS NOT NULL AND username=’${username}’

[call.route]
;通话路由
offlineauto=yes
query=SELECT location,(CASE WHEN location IS NULL THEN ‘offline’ ELSE NULL END) AS error FROM users WHERE username=’${called}’
result=location
priority=120

至此,就ok了,新建的那张users表是用户表,那里面的账号就可以登陆,然后写一个php接口,至此,互联网电话好了,你可以看下android的sipDemo!

SIP消息类型简介

1.请求消息:用于客户端为了激活按特定操作而发给服务器的SIP消息,包括INVITE, ACK,OPTIONS,BYE,CANCEL和REGISTER消息等。

请求消息 消息含义
INVITE 发起会话请求
ACK 证实已收到对于INVITE请求的*终响应。该消息仅和INVITE消息配套使用
BYE 结束会话。
CANCEL 取消未完成的请求,对于已完成的请求(既已收到*终响应的请求)则没影响。
REGISTER 注册
OPTIONS 查询服务器的能力

2. 响应消息: 用于对请求消息进行响应,指示呼叫的成功或失败状态。不同类的响应消息由状态码来区分。状态码包含三位整数,状态码的*位用于定义响应类型,另外两位用于进一步对响应进行更加详细的说明。

(1)1xx:信息响应(呼叫进展),表示已经接收到请求消息,正在对其进行处理;

序号 状态码 消息功能
1xx 100 正在处理中(Trying)
180 振铃(RINGING)
181 呼叫前转(call being forwarder)
182 排队(queue)
181* 会话进行(session progress)

(2)2xx:成功响应,表示请求已被成功接收、处理;

序号 状态码 消息功能
2xx 200 会话成功(OK)

(3)3xx:重定向响应,表示需要采取进一步动作,以完成该请求;

序号 状态码 消息功能
3xx 300 表示多重选择(multiple)
301 表示永久迁移(moved permanently)
302 表示临时迁移(moved temporaily)
303 见其它
305 用户代理(user proxy)
380 代换服务(alternative service)

(4)4xx:客户端出错,表示请求消息中包含语法错误或SIP服务器不能完成对该请求的处理;

序号 状态码 消息功能
4xx 400 错误请求(bad request)
401 无权(unauthorized)
402 要求付款(payment required )
403 禁止(forbidden)
404 没有发现(not found)
405                      不允许的方法(method no allowed)
406 不接受(not acceptable)
407 代理需要验证(proxy authentication required)
408 请求超时(request timeout)
410 消失(gone)
413 请求实体太大( request entity too large)
414 请求URI太大(request-url too long)
415 不支持的媒体类型(unsupported media type)
              416 不支持的URI方案(unsupported url scheme)
420 分机无人接听(bad extension)
421 要求转接( extension required)
423 间隔太短( interval too brief)
480 临时失效(temporarily unavailable)
481 呼叫/事务不存在(call/transaction does not exist)
482 发现环路(loop detected)
483 跳数太多(too many hops)
484 地址不完整(address incomplete)
485 不明朗(ambiguous)
486 这里忙(busy here)
487 请求终止(request terminated)
488 这里请求不可接受(not acceptable here)
491 未决请求(request pending)
493 不可辨识(undecipherable)

(5)5xx:服务器出错,表示SIP服务器故障不能完成对正确消息的处理。

序号 状态码 消息功能
5xx 500 服务器内部错误( server internal error)
501 不可执行(not implemented)
502 坏网关(bad gateway)
503 服务器无效(service unavailable)
504 服务器超时(server time-out)
505 版本不支持(version not supported)
513 消息太大(message too large)

(6)6xx:全局错误,表示请求不能在任何SIP服务器上实现。

序号 状态码 消息功能
6xx 600 全忙(busy everywhere)
603 丢弃(decline)
604 都不存在(does not exist anywhere)
606 不接受(not acceptable)

SIP服务器类型

有一些不同类型的SIP服务器。根据你的应用,你可以使用它们其中的一种或所有类型来解决你的问题。OpenSIPS可以作为代理服务器、重定向服务器、背靠背用户代理或者注册服务器。

The proxy server(代理服务器)
在 SIP 代理模式下,所有的 IP 消息都要经过 SIP 代理。这种行为在向诸如计费( billing)的过程中帮助很大,而且迄今为止,这也是一种*普遍的选择。但是它的缺点就是在会话建立过程中的所有的 SIP 交互中,服务器造成的额外开销也是客观的。要记住的是,即使服务器作为 SIP 代理在工作时, RTP 包也总是直接从一端传送到另一端,而不会经过服务器。
%title插图%num

The redirect server(重定向服务器)
SIP 代理可以运行在 SIP 重定向模式。在这种模式下, SIP 服务器的处理量是相当巨大的,因为它不需要保持事务处理的状态。在对 INVITE 消息进行初始化后,仅仅向 UAC 回复一条―302 Moved Temporarily‖消息就可以离开 SIP 对话( dialog)了。在这种模式下的 SIP 代理,即使只是利用非常少的资源也可以每小时传送上百万的通话。当你需要的规模很大并且不需要对通话计费的情况下,这种模式通常会被使用。

%title插图%num

The B2BUA server(背靠背用户代理服务器)
The server can also work as a Back-to-Back User Agent(B2BUA). B2BUAs are normally applied to hide the topology of the network(网络的拓扑结构). They are also useful to support buggy clients unable to route SIP requests correctly based on record routing. Many PBX systems such as Asterisk, FreeSwitch, Yate, and others work as B2BUAs.

想了解B2BUA的可以看《FreeSWITCH权威指南》一书,上面做了详细介绍。

%title插图%num

注意:图中左边的Call-ID和右边的Call-ID不同(B2BUA Two Legs)。

服务器分类方式和类型

1、服务器的分类方式包含两种:

(1)如何处理请求。包含两种处理方式:一种是并发处理,另一种是迭代处理。并发服务器进程在同一时间并发的处理多个客户机;迭代服务器每次仅处理一个客户机。

(2)有无连接。一种是面向连接的服务器使用TCP;另一种是无连接的服务器使用UDP。

2、服务器的四种类型

(1)迭代无连接服务器;属于*常见,*易实现的服务器。

(2)迭代面向连接服务器;每次处理一个客户机连接的单进程。

(3)并发无连接服务器;适用于拥有高请求量,但仍需要快速周转时间的服务器。

(4)并发面向连接服务器;(TCP三次握手实现)。

不同版本服务器之间的区别与联系

1.Tomcat
Tomcat 服务器是一个免费的开放源代码的Web 应用服务器,它是Apache 软件基金(Apache Software Foundation)的Jakarta 项目中的一个核心项目,由Apache、Sun 和其他一些公司及个人共同开发而成。由于有了Sun 的参与和支持,*新的Servlet 和JSP 规范总是能在Tomcat 中得到体现,Tomcat 5 支持*新的Servlet 2.4 和JSP 2.0 规范。因为Tomcat 技术先进、性能稳定,而且免费,因而深受Java 爱好者的喜爱并得到了部分软件开发商的认可,成为目前比较流行的Web 应用服务器。

2.JBoss
JBoss是一个管理EJB的容器和服务器,支持EJB 1.1、EJB 2.0和EJB3.0的规范。但JBoss核心服务不包括支持servlet/JSP的WEB容器,一般与Tomcat或Jetty绑定使用。2006年,Jboss公司被Redhat公司收购。

JBoss是全世界开发者共同努力的成果,一个基于J2EE的开放源代码的应用服务器。 因为JBoss代码遵循LGPL许可,你可以在任何商业应用中免费使用它,而不用支付费用。JBoss支持EJB 1.1和EJB 2.0 EJB3.0的规范,它是一个管理EJB的容器和服务器。类似于Sun’s J2SDK Enterprise Edition(J2EE),JBoss的目标是一个源代码开放的J2EE环境。但是JBoss核心服务仅是提供EJB服务器。JBoss不包括serverlers/JSP page 的WEB容器,当然可以和Tomcat或Jetty绑定使用。

JBoss具有的六大优点:

1、JBoss是免费的,开放源代码J2EE的实现,它通过LGPL许可证进行发布。

2、JBoss需要的内存和硬盘空间比较小。

3、安装非常简单。先解压缩JBoss打包文件再配置一些环境变量就可以了。

4、JBoss能够”热部署”,部署BEAN只是简单拷贝BEAN的JAR文件到部署路径下就可以了。如果没有加载就加载它;如果已经加载了就卸载掉,然后LOAD这个新的。

5、JBoss与Web服务器在同一个Java虚拟机中运行,Servlet调用EJB不经过网络,从而大大提高运行效率,提升安全性能。

6、用户可以直接实施J2EE-EAR,而不是以前分别实施EJB-JAR和Web-WAR,非常方便。

JBoss的安装和配置可以直接拷贝使用,但是要改动 %JBoss-HOME%\bin\run.bat里JAVA-HOME的设置,改成本机JDK的目录。运行run.bat来启动JBoss

关闭JBoss:关闭JBoss的 DOS 窗口或按”CTRL + C”。

3.JBoss与Tomcat的区别
通过上面两个的介绍就可以发现二者的区别:

Tomcat 只能做jsp和servlet的container《web服务器》

Jboss只能作为应用服务器。

Jboss可以通过内嵌Tomcat…来做web服务器。

4.WebLogic
WebLogic是美国bea公司出品的一个application server确切的说是一个基于j2ee架构的中间件。BEA WebLogic是用于开发、集成、部署和管理大型分布式Web应用、网络应用和数据库应用的Java应用服务器。将Java的动态功能和Java Enterprise标准的安全性引入大型网络应用的开发、集成、部署和管理之中。

5.应用服务器(WebLogic)与Tomcat的区别
应用服务器提供更多的J2EE特征,如EJB,JMS,JAAS等,同时也支持Jsp和Servlet.而Tomcat则功能没有那么强大,它不提供EJB等支持。但如果与JBoss(一个开源的应用服务器)集成到一块,则可以实现J2EE的全部功能。既然应用服务器具有Tomcat的功能,那么Tomcat有没有存在的必要呢?事实上,我们的很多中小应用不需要采用EJB等技术,Jsp和Servlet已经足够,这时如果用应用服务器就有些浪费了。而Tomcat短小精悍,配置方便,能满足我们的需求,这种情况下我们自然会选择Tomcat.

6.webSphere

WebSphere是 IBM 的集成软件平台。它包含了编写、运行和监视全天候的工业强度的随需应变 Web 应用程序和跨平台、跨产品解决方案所需要的整个中间件基础设施,如服务器、服务和工具。WebSphere 提供了可靠、灵活和健壮的集成软件。

7.综合分析

1.价位不同
JBoss与Tomcat的是免费的。

2.开源性不同
JBoss与Tomcat的是完全开源的,而其他两个不是。

3.对技术的支持
Tomcat不支持EJB,JBoss是实现了EJB容器,再集成了Tomcat。WebLogic与WebSphere都是对业内多种标准的全面支持,包括EJB、JSB、JMS、JDBC、XML和WML,使Web应用系统的实施更为简单,并且保护了投资,同时也使基于标准的解决方案的开发更加简便。

4.扩展性的不同
WebLogic和WebSphere都是以其高扩展的架构体系闻名于业内,包括客户机连接的共享、资源 pooling以及动态网页和EJB组件群集。

5.应用范围的区别
Tomcat 是一个小型的轻量级应用服务器,在中小型系统和并发访问用户不是很多的场合下被普遍使用,是开发和调试JSP 程序的首选。

WebLogic和WebSphere是商业软件,功能齐全强大,主要应用于大型企业的大型项目。

JBOSS 主要应用于EJB服务的中小型公司。

6.商业服务和技术支持的区别
因为JBoss和Tomcat都是开源免费的,所以它俩也就没有任何商业服务和技术支持,而WebLogic和WebSphere的技术文档和相关服务还是很到位,如果你的服务器哪一天出问题了,只要你能出的起钱,他们的技术工程师立刻就能出现在你面前。

7.安全性问题
因为JBoss和Tomcat都是开源的,所以它们的安全性相对来说比较低,万一应用服务器本身有什么漏洞,你是没办法向Apache索赔的。

而WebLogic和WebSphere其容错、系统管理和安全性能已经在全球数以千记的关键任务环境中得以验证。

8.与数据库的紧密结合性
如果硬件成本比软件成本高许多,那不如使用weblogic/Websphere。其中的道理太简单了,为什么电信/银行/移动之类的公司使用Oracle或DB2数据库,而不选用mysql。单靠tomcat是无法支持那么多的并发量,有钱的话还是选择商业产品。

9.简要概括
weblogic相当于tomcat和jboss结合在一起使用(因为weblogic支持servlet和jsp以及ejb,而tomcat仅支持servlet和jsp,jboss仅支持ejb)

6.另外简介一下EJB
做web开发的童鞋对EJB了解的可能不太多,下面通过EJB和Java Bean的对比来介绍一下:

Java Bean 是可复用的组件,对Java Bean并没有严格的规范,理论上讲,任何一个Java类都可以是一个Bean。但通常情况下,由于Java Bean是被容器所创建(如Tomcat)的,所以Java Bean应具有一个无参的构造器,另外,通常Java Bean还要实现Serializable接口用于实现Bean的持久性。Java Bean实际上相当于微软COM模型中的本地进程内COM组件,它是不能被跨进程访问的。Enterprise Java Bean 相当于DCOM,即分布式组件。它是基于Java的远程方法调用(RMI)技术的,所以EJB可以被远程访问(跨进程、跨计算机)。但EJB必须被布署在诸如Webspere、WebLogic这样的容器中,EJB客户从不直接访问真正的EJB组件,而是通过其容器访问。EJB容器是EJB组件的代理,EJB组件由容器所创建和管理。客户通过容器来访问真正的EJB组件。

209. 长度*小的子数组(JS实现)

209. 长度*小的子数组(JS实现)

1 题目
给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度*小的 连续 子数组,并返回其长度。如果不存在符合条件的子数组,返回 0。
示例:
输入:s = 7, nums = [2,3,1,2,4,3]
输出:2
解释:子数组 [4,3] 是该条件下的长度*小的子数组。

链接:https://leetcode-cn.com/problems/minimum-size-subarray-sum

2 思路
这道题我是用双指针的方法遍历整个数组来获取长度*小的子数组的

3代码
/**
* @param {number} s
* @param {number[]} nums
* @return {number}
*/
var minSubArrayLen = function(s, nums) {
if (nums.length === 0) return 0;
if (Math.min(…nums) >= s) return 1;
let len;
let low=0, high=1;
while(low < nums.length) {
let sum = 0;
for (let i=low; i<high; i++) {
sum += nums[i];
}

if (sum >= s) {
let tempLen = high – low;
len = len ? Math.min(len, tempLen) : tempLen;
if (tempLen === 1) break;
low++;
} else {
high++;
if (high > nums.length) break;
}
}

return len || 0;
};

课程表 II(JS实现)

课程表 II(JS实现)

1 题目
现在你总共有 n 门课需要选,记为 0 到 n-1。
在选修某些课程之前需要一些先修课程。 例如,想要学习课程 0 ,你需要先完成课程 1 ,我们用一个匹配来表示他们: [0,1]
给定课程总量以及它们的先决条件,返回你为了学完所有课程所安排的学习顺序。
可能会有多个正确的顺序,你只要返回一种就可以了。如果不可能完成所有课程,返回一个空数组。
示例 1:
输入: 2, [[1,0]]
输出: [0,1]
解释: 总共有 2 门课程。要学习课程 1,你需要先完成课程 0。因此,正确的课程顺序为 [0,1] 。
示例 2:
输入: 4, [[1,0],[2,0],[3,1],[3,2]]
输出: [0,1,2,3] or [0,2,1,3]
解释: 总共有 4 门课程。要学习课程 3,你应该先完成课程 1 和课程 2。并且课程 1 和课程 2 都应该排在课程 0 之后。
因此,一个正确的课程顺序是 [0,1,2,3] 。另一个正确的排序是 [0,2,1,3] 。

链接:https://leetcode-cn.com/problems/course-schedule-ii

2 思路
这道题还是考察图的拓扑排序,与之前课程表一的题相比,这道题多了输出序列的要求,不慌,在整个流程中添加几行保存序列的代码就可以了

3代码
/**
* @param {number} numCourses
* @param {number[][]} prerequisites
* @return {number[]}
*/
var findOrder = function(numCourses, prerequisites) {

const res = [];

if (prerequisites.length === 0) {
for (let i=0; i<numCourses; i++) {
res.push(i);
}
return res;
}

const vexs = [];

for (let arr of prerequisites) {
if (!vexs[arr[0]]) {
vexs[arr[0]] = new Node(arr[0]);
}

if (!vexs[arr[1]]) {
vexs[arr[1]] = new Node(arr[1]);
}

let firstEdge = vexs[arr[1]].firstEdge;
vexs[arr[1]].firstEdge = {vex: vexs[arr[0]], next: firstEdge};
vexs[arr[0]].in++;
}

const stack = [];

for (let i=0; i<numCourses; i++) {
if (vexs[i]) {
if (vexs[i].in === 0) stack.push(vexs[i]);
} else {
res.push(i); // 保存没有任何边连接的节点
}
}

while(stack.length > 0) {
let currentVex = stack.pop();
res.push(currentVex.val); //保存节点
let currentEdge = currentVex.firstEdge;
while(currentEdge) {
if (–currentEdge.vex.in === 0) {
stack.push(currentEdge.vex);
}
currentEdge = currentEdge.next;
}
}

return res.length < numCourses ? [] : res;
};

function Node(val) {
this.val = val;
this.firstEdge = null;
this.in = 0;
}

添加与搜索单词 – 数据结构设计(JS实现)

添加与搜索单词 – 数据结构设计(JS实现)

1 题目
设计一个支持以下两种操作的数据结构:
void addWord(word)
bool search(word)
search(word) 可以搜索文字或正则表达式字符串,字符串只包含字母 . 或 a-z 。 . 可以表示任何一个字母。
示例:
addWord(“bad”)
addWord(“dad”)
addWord(“mad”)
search(“pad”) -> false
search(“bad”) -> true
search(“.ad”) -> true
search(“b…”) -> true
说明:
你可以假设所有单词都是由小写字母 a-z 组成的。

链接:https://leetcode-cn.com/problems/add-and-search-word-data-structure-design

2 思路
这道题还是用字典树的方法来存储单词,每个节点维护一个指针数组,搜索时若遇到小数点,则递归遍历当前数组所有指针进行下一步搜索

3代码
/**
* Initialize your data structure here.
*/
var WordDictionary = function() {
this.startIndex = ‘a’.charCodeAt();
this.root = new Node();
};

/**
* Adds a word into the data structure.
* @param {string} word
* @return {void}
*/
WordDictionary.prototype.addWord = function(word) {
if (word.length === 0) return;

let p = this.root;
for (let letter of word) {
let index = letter.charCodeAt() – this.startIndex;
if (!p.list[index]) {
p.list[index] = new Node();
}

p = p.list[index];
}

p.word = word;
};

/**
* Returns if the word is in the data structure. A word could contain the dot character ‘.’ to represent any one letter.
* @param {string} word
* @return {boolean}
*/
WordDictionary.prototype.search = function(word) {
if (word.length === 0) return false;

return this.d(this.root, word);

};

WordDictionary.prototype.d = function(p, word) {
if (!p) return false;
if (word === ”) return !!p.word;

let letter = word[0];

if (letter === ‘.’) {
for (let i=0; i<p.list.length; i++) {
if (p.list[i] && this.d(p.list[i], word.slice(1))) {
return true;
}
}
return false;
} else {
let index = letter.charCodeAt() – this.startIndex;
p = p.list[index];
return this.d(p, word.slice(1));
}
}

function Node() {
this.list = [];
this.word = null;
}

/**
* Your WordDictionary object will be instantiated and called as such:
* var obj = new WordDictionary()
* obj.addWord(word)
* var param_2 = obj.search(word)
*/

打家劫舍 II(JS实现)

打家劫舍 II(JS实现)

1 题目
你是一个专业的小偷,计划偷窃沿街的房屋,每间房内都藏有一定的现金。这个地方所有的房屋都围成一圈,这意味着*个房屋和*后一个房屋是紧挨着的。同时,相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你在不触动警报装置的情况下,能够偷窃到的*高金额。
示例 1:
输入: [2,3,2]
输出: 3
解释: 你不能先偷窃 1 号房屋(金额 = 2),然后偷窃 3 号房屋(金额 = 2), 因为他们是相邻的。
示例 2:
输入: [1,2,3,1]
输出: 4
解释: 你可以先偷窃 1 号房屋(金额 = 1),然后偷窃 3 号房屋(金额 = 3)。
偷窃到的*高金额 = 1 + 3 = 4 。

链接:https://leetcode-cn.com/problems/house-robber-ii

2 思路
这道题考察动态规划,与打家劫舍一不同的是,这次的房屋围成了一个环状,如果偷了*家,就一定不能偷*后一家,因此我们考虑将环状拆分为两段,分别为1…n-1和2…n,分别用动态规划计算其能偷到的*大金额,然后两者取*大即可

3代码
/**
* @param {number[]} nums
* @return {number}
*/
var rob = function(nums) {
if (nums.length === 1 || nums.length === 2) return Math.max(…nums);
if (nums.length === 0) return 0;

const d = [nums[0]];
const d1 = [0, nums[1]];

for (let i = 1; i<nums.length-1; i++) {
if (i === 1) {
d[i] = Math.max(nums[0], nums[1]);
} else {
d[i] = Math.max(d[i-1], d[i-2] + nums[i]);
}
}

for (let i = 2; i<nums.length; i++) {
if (i === 2) {
d1[i] = Math.max(nums[1], nums[2]);
} else {
d1[i] = Math.max(d1[i-1], d1[i-2] + nums[i]);
}
}

return Math.max(d[d.length – 1], d1[d1.length – 1]);
};

. 数组中的第K个*大元素(JS实现)

. 数组中的第K个*大元素(JS实现)

1 题目
在未排序的数组中找到第 k 个*大的元素。请注意,你需要找的是数组排序后的第 k 个*大的元素,而不是第 k 个不同的元素。
示例 1:
输入: [3,2,1,5,6,4] 和 k = 2
输出: 5
示例 2:
输入: [3,2,3,1,2,4,5,5,6] 和 k = 4
输出: 4
说明:
你可以假设 k 总是有效的,且 1 ≤ k ≤ 数组的长度。

链接:https://leetcode-cn.com/problems/kth-largest-element-in-an-array

2 思路
这道题考察排序,根据题意,选择堆排序比较合适

3代码
/**
* @param {number[]} nums
* @param {number} k
* @return {number}
*/
var findKthLargest = function(nums, k) {
if (nums.length <= 1) return nums[k-1] || null;

nums.unshift(null); //由于孩子节点的索引为父节点索引的2倍,因此为了计算方便数组*项留空
let len = Math.floor((nums.length-1) / 2);

for (let i=len; i>=1; i–) {
adjust(i, nums.length-1);
}

for (let i=nums.length-1; i>=nums.length – k; i–) { //找到第k大的数后,结束循环
swap(1, i);
adjust(1, i-1);
}

return nums[nums.length – k];

function adjust(s, maxIndex) { //调整不满足的子树结构使其符合大顶堆,s为需要调整的数
let temp = nums[s];

for (let i=2*s; i<=maxIndex; i*=2) { //依次比较子节点,找到放置s的位置
if (i+1 <= maxIndex && nums[i] < nums[i+1]) i++;

if (temp > nums[i]) break;

nums[s] = nums[i];
s = i;
}

nums[s] = temp;
}

function swap(i,j) {
let temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
};

友情链接: SITEMAP | 旋风加速器官网 | 旋风软件中心 | textarea | 黑洞加速器 | jiaohess | 老王加速器 | 烧饼哥加速器 | 小蓝鸟 | tiktok加速器 | 旋风加速度器 | 旋风加速 | quickq加速器 | 飞驰加速器 | 飞鸟加速器 | 狗急加速器 | hammer加速器 | trafficace | 原子加速器 | 葫芦加速器 | 麦旋风 | 油管加速器 | anycastly | INS加速器 | INS加速器免费版 | 免费vqn加速外网 | 旋风加速器 | 快橙加速器 | 啊哈加速器 | 迷雾通 | 优途加速器 | 海外播 | 坚果加速器 | 海外vqn加速 | 蘑菇加速器 | 毛豆加速器 | 接码平台 | 接码S | 西柚加速器 | 快柠檬加速器 | 黑洞加速 | falemon | 快橙加速器 | anycast加速器 | ibaidu | moneytreeblog | 坚果加速器 | 派币加速器 | 飞鸟加速器 | 毛豆APP | PIKPAK | 安卓vqn免费 | 一元机场加速器 | 一元机场 | 老王加速器 | 黑洞加速器 | 白石山 | 小牛加速器 | 黑洞加速 | 迷雾通官网 | 迷雾通 | 迷雾通加速器 | 十大免费加速神器 | 猎豹加速器 | 蚂蚁加速器 | 坚果加速器 | 黑洞加速 | 银河加速器 | 猎豹加速器 | 海鸥加速器 | 芒果加速器 | 小牛加速器 | 极光加速器 | 黑洞加速 | movabletype中文网 | 猎豹加速器官网 | 烧饼哥加速器官网 | 旋风加速器度器 | 哔咔漫画 | PicACG | 雷霆加速