javascript中使用的经典算法示例

javascript中使用的经典算法示例
本文介绍了常用的Javascript算法,供大家参考

入门级的算法-线性查找的时间复杂度为O(n)的算法——圈HelloWorld等效
线性搜索(HelloWorld /入境)
是数组,而x是要搜索的值。
线性函数(a,x){
对于(var i = 0;i < a.length;i++){
如果({ = x = x){
还我;
}
}
返回- 1;
}

两个搜索(也称折半查找)-行线性结构阶的时间复杂度为O(logN)
二/搜索
是一个以升序排列的数组。
返回目标元素的索引。
功能二(a,x){
Var Low = 0,高= a.length - 1;
当(低高度){
var = math.floor(MID(低+高) / / / 2);下一轮
如果(x = a = { }){
回中;
}
如果(x < { }){
高= 1年年中;
}
{其他
低=中+ 1;
}
}
返回- 1;
}

冒泡排序——时间复杂度o(n 2)
冒泡排序
功能(一){冒泡
对于(var i = 0;i < a.length;i++){
var =真的;
注:内部循环是反向的。
对于(var j = a.length - 1;J >我;J—){
如果({ < { { 1 } }){
交换(a,j,j - 1);
排序= false;
}
}
如果(排序){
返回;
}
}
}

排序的选择——时间复杂度o(n 2)
选择类
找到你的想法:最低标记下来,然后交换。
函数的选择排序(一){
对于(var i = 0;i < a.length - 1;i++){
var = i;
对于(var j = i + 1;J < a.length;j++){
如果({ < } < }){
k = j;
}
}
If (k! = i){
var;
{ { } };
{ };
println();
}
}
返回一个;
}

插入排序——时间复杂度o(n 2)
插入排序
假设当前元素元素已经排序,将自己的位置空出,
然后,前/旧元素依次返回,直到空坑
然后,目标元素插入 坑
函数插入排序(一){
对于(var i = 1;i < a.length;i++){
var = { };
对于(var j = i 1;j = 0;
{ 1 + } = { };
}
如果({ + 1 })!= x){
{ + 1 } = x;
println();
}
}
返回一个;
}

字符串的反转,时间复杂度为O(logN)
字符串反转(例如:abc)
函数反(s){
var arr = s.split(' ');
var i = 0,J = arr.length - 1;
当(i < j){
var t = ARR {我};
ARR {我} = { }度{J}.;
ARR { } = T J;
++;
J;
}
返回arr.join('');
}

关于稳定阶的一个结论:

基于比较的简单排序算法,即时间复杂度O(n = 2)的排序算法通常被认为是一种稳定的排序。

其他先进的排序算法,如归并排序、堆排序、桶排序等(通常,时间复杂度的算法可以优化n logn),一般可认为是不稳定的排序。

单链表实现
函数打印(MSG){
document.write(MSG);
}
函数println(MSG){
打印(MSG +);
}
节点类
var节点=函数(v){
this.data = V; / /节点的值
this.next = null; / /后继节点
}
单一列表
VaR链接=功能(){()
this.head =新的节点(空); / /同意头结点,没有价值
插入节点
this.insert =功能(v){
var p = this.head;
而(p.next!= NULL){
P = p.next;
}
p.next =新的节点(V);
}
节点删除指定位置
this.removeat =功能(N){
如果(n=0){
返回;
}
无功prenode = this.getnodebyindex(n - 1);
prenode.next = prenode.next.next;
}
n / /节点位置(为零的头节点一致的位置)
当 n大于列表元素的数目时,它返回最后一个元素。
this.getnodebyindex =功能(N){
var p = this.head;
var I=0;
而(p.next!=空i <){
P = p.next;
++;
}
返回p;
}
查询节点的值,
如果链接列表中有同一个节点的多个值,
返回第一次找到
this.getnodebyvalue =功能(v){
var p = this.head;
而(p.next!= NULL){
P = p.next;
如果(p.data = = v){
返回p;
}
}
返回null;
}
打印所有节点
this.print =函数(){
var p = this.head;
而(p.next!= NULL){
P = p.next;
打印(p.data + );
}
println();
}
}
在单链表L中测试是否有重复的元素
功能hassamevaluenode(链接){
var i = singlelink.head;
而(i.next!= NULL){
我= i.next;
var j = i;
而(j.next!= NULL){
J = j.next;
如果(i.data = = j.data){
返回true;
}
}
}
返回false;
}
单列表反转元素
功能reversesinglelink(链接){
VaR ARR =新的数组();
var p = singlelink.head;
要再次启动数组中的所有节点
而(p.next!= NULL){
P = p.next;
Arr.push(p.data);
}
VaR NewLink =新的链接();
从前向遍历数组中添加新列表
对于(var i = arr.length - 1;我> = 0;我--){
NewLink.insert(ARR {我});
}
返回NEWLINK;
}
无功linktest =新的链接();
LinkTest.insert(A);
LinkTest.insert(B);
LinkTest.insert(C);
LinkTest.insert(会);
linktest.print(); / / A B C D
VaR NewLink = reversesinglelink(linktest);
newlink.print(); / / D C B
关于邻接矩阵和邻接表的选择:

邻接矩阵和邻接表是图的基本存储方法

map实例(即不低于外围顶点的情况),用邻接表存储更适合(相对于n×n矩阵,邻接表只存储边值和顶点,不存储空值,更高的存储效率)

在稠密图的情况下,邻接矩阵更适合于数据存储。当数据较多时,应该遍历它。

堆:

几乎完全的两叉树:一棵二叉树,可能从右边的一个或几个叶子中消失。在物理存储中,它可以被存储在数组中。如果一个{ J }有左、右子节点的顶点,左节点是{ 2j }和右节点是{ 2J + 1 }。{ }的父顶点存储在一个{ 2 }中。

堆:本身是一个几乎完全的两叉树,父节点的值不小于子节点的值。应用程序场景:优先级队列,查找最大或次要最大值;在优先级队列中插入新元素。

注意:所有下列堆,常规索引的0个元素只被占用,有效元素从下标1开始。

根据堆的定义,下面的代码可以用来测试数组是否是堆。
测试h是否是堆数组
(合同开头/下标1中的有效成分)
时间复杂度是O(n)
功能isheap(H){
如果(h.length <= 1){ return false;}
无功半math.floor(h.length / / / 2);根据堆财产,圈只有一半就够了
对于(var i = 1;i <半;i + +){
如果父节点比任何一个节点都违反了堆的定义
如果(H { } { } < < h我h { 2 | | { 2 } H *我*我+ 1 }){
返回false;
}
}
返回true;
}

节点调整siftup起来

在某些情况下,如果在一堆改变元素的值(例如,当10,8,9,7成为10,8,9,20,20需要调整了),不再满足堆的定义。如果我们需要调整它,我们可以使用下面的代码来实现它。
堆中的节点
(合同开头/下标1中的有效成分)
功能siftup(H、I){
如果(i = 1){
返回;
}
对于(var j =我;J > 1;J = math.floor(J / 2)){
var k = math.floor(J / 2);
查找子节点比父节点,然后交换位置和父节点
如果(h){ { } }){
var;
h;
h=};
}
{其他
这与定义堆栈一致,调整结束,退出
返回;
}
}
}

节点调整siftdown下来(因为有一个向上的调整,也有一个向下的调整)
堆中的节点
(合同开头/下标1中的有效成分)
/ /时间复杂度为O(logN)
功能siftdown(H、I){
如果(2 *我> h.length){ / /叶节点,不再向下移动
返回;
}
对于(var j = 2 *我;J < h.length;j = 2 * j){
映射到两个较大的子节点(非常巧妙的方法)
如果(h + 1){ { } }){
++;
}
var k = math.floor(J / 2);
如果(h<})
var t = H K } {;
h = };
h = };
}
{其他
返回;
}
}
}

向堆中添加新元素
将元素添加到堆栈中。
/ /时间复杂度为O(logN)
函数插入(h,x){
:首先在数组x中添加目标元素
H.push(X);
然后向上推
SiftUp(H,h.length - 1);
}

从堆中删除元素
h反应器中i/删除元素的位置
/ /时间复杂度为O(logN)
函数删除(h,i){
我的观点:首先,元素和元素的最终位置。
然后,数据长度减去1(要删除的i元素的位置,但是整个堆被销毁)
决定:N的最后一个元素需要调整向上或向下调整。
在基础上:例如,比元素的原始位置,然后调整向上,向下调整。
保护元素的原始i位置放上
进入我位置的最后一个元素
最后一个元素和删除(js语言的优越性体现了!)
H {我} = H.pop();
var n = h.length - 1;
如果(i = n = 1){
如果您将其删除,则只不过是最后两个元素之一,
无需调整。
返回;
}
如果(h x x x){ {
SiftUp(H、I);
}
{其他
siftdown(H、I);
}
}
从堆中删除最大值
返回最大值
/ /时间复杂度为O(logN)
功能deletemax(H){
var x = { 1 };
删除(h,1);
返回x;
}

堆排序:

这是排序算法,一个非常聪明的想法,其实质在于充分利用本身的堆数据结构特点(必要时,第一个元素,每个元素的值)的上下移动,再时间和相对较低的,只有O(logn),空间,没有需要使用额外的存储空间,你只能交换元素在数组本身。

思想:

1,第一个元素(即最大元素)和元素的结尾是最大的下沉,下一轮的重量不再负责。

2。1后,剩余的元素通常不再一堆。这时,只要新的第一要素是下调siftdown,调整后,新的最大元素自然上升到第一个元素的位置。

3, 1, 2个大重复,元素一个接一个沉到底,最后数组被排序。

时间复杂度分析:创建一个堆需要O(n)成本。的siftdown成本是O(logn)最多,和n-1元调整最多,所以总成本为O(N)+(n-1)O(logn),和最终的时间复杂度是O.
堆中的节点
(合同开头/下标1中的有效成分)
i是要调整的元素索引。
n是要处理的有效元素下标范围的上限。
/ /时间复杂度为O(logN)
功能siftdown(H,我,N){
如果(n = h.length){
n = h.length;
}
如果(2),叶节点,不再向下移动。
返回;
}
对于(var j = 2 * i;j n;j=2×j){
映射到两个较大的子节点(非常巧妙的方法)
如果(h + 1){ { } }){
++;
}
var k = math.floor(J / 2);
如果(h<})
var t = H K } {;
h = };
h = };
}
{其他
返回;
}
}
}
用于创建一个堆数组n个元素
功能makeheap(a,n){
如果(n = a.length){
n = a.length;
}
对于(var i = math.floor(n / 2);我> = 1;我--){
(A,I,N siftdown);
}
}
堆排序(非降序)
/ /时间复杂度为O(nlogn)
功能排序(H){
第一堆反应堆
makeheap(H,h.length);
对于(var j = h.length - 1;J > = 2;J—){
第一个元素是最大的/不可避免的。
最大元素和最后一个交换元素,
是最大的元素接收器,不再考虑下一轮。
var x = { 1 };
h 1 { };
h = j = x;
在交换其余元素之后,不再满足堆的定义,
新的向下的第一个元素(为了继续保持一堆形状)
在最大元素的其余部分必须浮在第一个元素之后再进行调整。
进入下一轮循环
siftdown(H,1,J 1);
}
返回H;
}

如果你理解建造一堆东西的原理,你可以把这个想法颠倒过来,然后轮流做。
功能makeheap2(a,n){
如果(n = a.length){
n = a.length;
}
对于(var i = math.floor(n / 2);我N;i++){
SiftUp(A,I);
}
}

不相交集搜索合并
定义节点节点
var =函数(v,p){
this.value = V; / /节点的值
this.parent = P; / /父节点
this.rank = 0; / /节点级别(默认为0)
}
根节点搜索节点包含
var =函数(x){
var;
而(y.parent!= NULL){
Y = y.parent;
}
var = y;
y=x;
沿着根/ X压缩路径
而(y.parent!= NULL){
存储的第一个父/父节点或调整后的下一行丢失。
无功W = y.parent;
指向根的目标节点挂起
y.parent =根;
指向父节点的工作指针,减少原始节点上的目标节点,
继续到/层压缩
Y = W
}
返回根;
}
x两个合并节点,对应的树y
时间复杂度是O(m)m是合并子集数。
var =函数(x,y){
x被设置为查找/根
var =查找(x);
要找到一组y/root
var =查找(y);
秩/小集秩集
如果(u.rank v.rank){
u.parent = V;
如果(u.rank = = v.rank){
两组等级与时间相等
赢的一面有点奖励,排名1。
v.rank = 1;
}
}
{其他
v.parent = U;
}
}

归纳:

首先看看两个排序的递归实现。
选择类的递归实现
/ /调用的例子:selectionSort({ 3,2,1 },0)
功能selectionsortrec(A,I){
var n = a.length - 1;
如果(i <){
var = i;
对于(var j = i + 1;j < n;j + +){
如果({ < } < }){
K = J
}
}
If (k! = i){
var;
{ { } };
{ };
}
SelectionSortRec(A,I + 1);
}
}
递归的插入排序
/ /调用的例子:insertsortrec({ 4,3,2,1 },3);
功能insertsortrec(A,I){
如果(i = 0){
var = { };
insertsortrec(一,我- 1);
var j = i - 1;
当(j = x = x = 0){
{ 1 + } = { };
J;
}
{ + 1 } = x;
}
}

递归程序通常很容易理解,而且代码很容易实现,并看两个小例子:

从数组中查找最大值
在数组中查找最大值(递归)
功能findmax(A,I){
如果(i = 0){
返回{ 0 };
}
var y = findmax(一,我- 1);
var = i - 1 };
返回y x x y;
}
var a = { 1,2,3,4,5,6,7,8,9 };
VaR测试= findmax(A,a.length);
警报(测试);返回9

有一个数组按升序排序,检查数组中是否有两个数字,它们的和是x
5.33的递归实现
是一个按{ 1…}的顺序排列的数组。
x是要测试的测试。
如果存在两个数字和x,则返回真值,否则返回false。
功能sumx(A,I,J,x){
如果(i = j){
返回false;
}
如果({ + })
返回true;
}
否则,如果(一个{ } + { } { x){
我后移
返回sumx(A,I + 1,J,X);
}
{其他
向前移动
返回sumx(A,I,J 1,X);
}
}
var = { 1, 2, 3,4, 5, 6,7, 8 };
var test1 = sumx(一,0,a.length-1,9);
警报(test1); / /返回true

递归程序虽然思路清晰,但通常效率低下。一般来说,递归实现可以重写为非递归实现,上面的代码也可以写成:
5.33的非递归实现
功能sumx2(a,x){
var i = 0,J = a.length - 1;
当(i < j){
如果({ + })
返回true;
}
否则,如果(一个{ } + { } { x){
我后移
++;
}
{其他
向前移动
J;
}
}
返回false;
}
var = { 1, 2, 3,4, 5, 6,7, 8 };
VaR test2 = sumx2(一,9);
警报(个); / /返回true

递归并不总是代表低效率。在某些情况下,递归的效率更高。例如,计算x的m功率。传统算法需要m倍乘法。下面的算法降低了时间复杂度为O(logN)。
x(递归)的功率计算
/ /时间复杂度为O(logN)
功能exprec(x,m){
如果(m=0){
返回1;
}
var y = exprec(x,math.floor(M / 2));
y=y;
如果(% 2)!= 0){
y=y
}
回到Y;
}

当然,这不仅仅是递归的功劳。它的效率的提高主要依赖于数学常识:x = x(2)2。还有另一种独特的思维方式,它不是递归的,它巧妙地利用了二进制的特性。
10 2十六进制十六进制数转换
函数托宾(DEC){
var;
无报酬股利= 12月;
var余数= 0;
当(股息> = 2){
余数=股息% 2;
Bits.push(剩余);
股利=(红利剩余) 2;
}
Bits.push(红利);
Bits.reverse();
返回bits.join();
}
x(非递归)的功率计算
解决方案是唯一的
函数(x,m){
var=1;
var =托宾(m)Split(');
将第一个m转换成2进制的形式。
对于(var j = 0;J < bin.length;j++){
y=y * 2;
如果j 2个十六进制数字是1,那么x
如果(bin { } = 1){
y=y
}
}
回到Y;
}
/ / println(exprec(2, 5));
/ / println(exp(2, 5));

看多项式求值的经典问题。

给定一个字符串的房,AN-1,A0,A1,…,一个实数x,该多项式Pn(x)的值计算。
著名的Horner公式:

如何计算:
显然有:
这只需要n次乘法加n加法。
多项式求值
n倍乘法加n,极大的改进!
函数的Horner(a,x){
var n = a.length - 1
var;
对于(var j=0;j < n;j + +){
p=x + p + a - j - 1 };
}
返回p;
}
/ /计算:Y(2)= 3 + 2 3x ^ ^ 2 + x 1;
var = { 1, 1, 2,3 };
var y = Horner(一,2);
警报(y);33

大多数问题:

一个包含n个数的元素数组,希望能快速找到一个比出现次数大的元素,例如n 2,(也称为大多数元素),通常用于选票系统中,以快速确定候选人的票数是否超过一半:
确定数组A中可能的大多数元素
函数候选(a,m){
VaR计算= 1,C =一个{米},N = a.length - 1;
当(m 0){
米+;
如果({ = }){
计数+;
}
{其他
计数—;
}
}
如果(m = n){
返回C;
}
{其他
返回候选(A,M + 1);
}
}
对于大多数元素
时间复杂度是O(n)
函数多数(a){
var(=,0);
var计数= 0;
大多数元素,可能不是,
必须再次计数,以确保结果正确
对于(var i = 0;i < a.length;i++){
如果({ = { = C){
计数+;
}
}
如果为一半,则为大多数元素确定。
如果(计数> math.floor(a.length / 2)){
返回C;
}
返回null;
}
var =多数({ 3, 2, 3,3, 4, 3 });
警报(M);

该算法基于以下结论:在删除原序列中的两个不同元素后,原序列中的大多数元素仍然是新序列中的大多数元素。

证明如下:

如果原始序列中的元素数是n,大多数元素的次数是x,那么x n > 1 2

去掉两个不同的元素后,

a)如果被删除的元素不包含元素,在新的序列的总新的元素数为X相同/(n-2),因为X / n>1 / 2,所以X /(n-2)也必然是> 1 / 2。

B)如果被删除的元素包含的元素,在新的序列的总新的元素的数目是一样的(X-1)/(n-2),因为X / n>1 / 2 = x > n / 2引入(X-1)/(n-2)。

(X-1)/(n-2)>(n / 2 - 1)/(n-2)= 2(n-2)/(n-2)= 1 / 2

下一个问题:全面安排
函数交换(a,i,j){
var = { };
{ { } };
{;
}
函数println(MSG){
document.write(味精+ );
}
完全置换算法
函数烫发(p,m){
var n = p.length - 1;
如果(m = n){
完成一个新的安排,输出
println(P);
返回;
}
对于(var j = m;j <;n;j + +){
交换的初始元素和每个元素
交换(p,j,m);
在第一行中,根据元素排成一行。
加上元素的新排列
Perm(P,M + 1);
在递归调用之前,站点的j和m返回,恢复站点
因为在递归调用之前,交换已经破坏了原来的顺序,
引导返回生成排序,可能生成重复
交换(p,j,m);
}
}
Perm({ 1, 2, 3 },0);
/ / 1,2,3
/ / 1
/ / 2,1,3
/ / 2,3,1
/ / 3,2,1
/ / 2,1,3

分治:

要点:当问题分为两个子问题时,尽量使子问题的大小大致相等,从而最大程度地反映问题的规模,分为二,缩小二元日志的优势。
(调试)打印输出
函数println(MSG){
document.write(味精+ );
}
数组i,j元素交换位置(辅助函数)
函数交换(a,i,j){
var = { };
{ { } };
{;
}
查找数组中的最大值和最小值(分而治之)
功能findminmaxdiv(A,低、高){
子问题的最小尺度解
如果(高=低= 1){
如果({低} < { }){
返回{低},{ } };
}
{其他
返回{高},{ } };
}
}
VaR中= math.floor((低+高) / 2);
在解决方案的元素的上半部分查找/子问题
VaR R1 = findminmaxdiv(A,低、中);
求解半个元素中的子问题
VaR(R2 = findminmaxdiv,中秋+ 1,高);
合并的两部分
var = 0 { } { 0 { } { 0 } }:{ { 0 };
var = 1 { } { 1 { } 1 } }:R2 { 1 };
返回{,y,};
}
VaR findminmaxdiv(R = { 1, 2, 3,4, 5, 6,7, 8 },0, 7);
println(R); / / 1,8
两个搜索(分而治之)
输入:数组的非降序排列。
x是要搜索的值。
,低,高搜索,开始和停止索引范围
返回:如果找到下标,则返回1。
功能binarysearchdiv(a,x,低、高){
如果(低>高){
返回- 1;
}
VaR中= math.floor((低+高) / 2);
如果(x = a = { }){
回中;
}
否则如果(x < { }){
返回binarysearchdiv(a,x,低,中1);
}
{其他
返回binarysearchdiv(a,x + 1,中,高);
}
}
var = binarysearchdiv({ 1, 2, 3,4, 5, 6,7 },4, 0, 6);
println(F); / / 3
将数组A转换为扇区的低位元素,分为两半
n是要处理的索引范围的上限。
函数分割(a,低,n){
If (n > = A.length {- 1)
a.length n = 1;
}
var I =低;
var = { };
上一次跟踪后的两个指针,
前面发现的指针元素比边界元素小,变成了前面。
然后在后面的指针/夫唱妇随,最后一路
用于(var =低+ 1;j n;j + +){
如果({ x } x){
++;
如果(i)!= j){
交换(a,i,j);
}
}
}
经过以上的折腾之后,除了低,其他元素都到位了。
最后,我们需要低 和最后一个元素的交换比低位置低,
将位置放在分水岭的低位
交换(a,低,i);
返回{,};
}
var = { 5, 1, 2,6, 3 };
var b =分裂(一,0,a.length - 1);
println(B { 0 }); / / 3,1,2,5,6
快速排序
功能快速排序(A,低、高){
var =高;
如果(低<高){
var =分裂(a,低,w);划分思想,先分成两半。
w = { { 1 };
上半场为
QuickSort(A,低,W - 1);
下半期为
QuickSort(A,W 1,高);
}
}
var = { 5, 6, 4,7, 3 };
QuickSort(一,0,a.length - 1);
println(); / / 3,4,5,6,7

分裂算法的应用:

设{ 1 }为整数元素集,给定数组A的重排算法,在所有非负整数左整数中,算法的运行时间应为(n)。
功能sort1(一){
var i = 0,J = a.length - 1;
当(i < j){
如果({ 0=0){ { }
J;
}
否则如果({ < { < 0 > { } > 0){
++;
}
否则如果({ 0 } { 0 }){
交换(a,i,j);
++;
J;
}
{其他
++;
J;
}
}
}
(一){函数SORT2
如果(a.length <= 1){ return };
var I=0;
对于(var j = i + 1;J < a.length;j++){
如果({ = } 0){
交换(a,i,j);
++;
}
}
}
var = { 1,- 2, 3,- 4, 5,- 6, 0 };
sort1(一);
println(); / / - 6、- 2、- 4,3,5,1,0
var = { 1,- 2, 3,- 4, 5,- 6, 0 };
SORT2(B);
println(B); / / - 2、- 4、- 6,1,5,3,0

希望本文能对javascript程序设计有所帮助。
免责声明:本网信息来自于互联网,目的在于传递更多信息,并不代表本网赞同其观点。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,并请自行核实相关内容。本站不承担此类作品侵权行为的直接责任及连带责任。如若本网有任何内容侵犯您的权益,请及时联系我们,本站将会在24小时内处理完毕。
相关文章
返回顶部