前端程序员面试分类真题(后附答案解析)

前端程序员面试分类真题(后附答案解析)

简答题

1、一群猴子排成一圈,按1,2,…,n依次编号。然后从第1只开始数,数到第m只,把它踢出圈,从它后面再开始数,数到第m只,再把它踢出去……,如此不停地进行下去,直到最后只剩下一只猴子为止,那只猴子就称为大王。要求编程模拟此过程,输入m、n输出最后那个大王的编号。

2、汉诺塔(又称河内塔)问题是印度的一个古老传说。开天辟地的神勃拉玛在一个庙里留下了三根金刚石棒,第一根上面套着64个圆的金片,最大的一个在底下,其余一个比一个小,依次叠上去。庙里的众僧不倦地把它们一个个从这根棒搬到另一根棒上,规定可利用中间的一根棒作为帮助,但每次只能搬一个,而且大的不能放在小的上面。经过运算移动圆片的次数为18446744073709551615,看来众僧们耗尽毕生精力也不可能完成金片的移动。
后来,这个传说就演变为汉诺塔游戏,游戏规则如下:
(1)有三根杆子A、B、C,A杆上有若干碟子。
(2)每次移动一块碟子,小的只能叠在大的上面。
(3)把所有碟子从A杆全部移到C杆上。
(4)经过研究发现,汉诺塔的破解很简单,就是按照移动规则向一个方向移动金片。
(5)如3阶汉诺塔的移动:A→C,A→B,C→B,A→C,B→A,B→C,A→C。
此外,汉诺塔问题也是程序设计中的经典递归问题。

3、据说著名犹太历史学家Josephus有过以下的故事。在罗马人占领乔塔帕特后,39个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人抓到,于是决定了一个自杀方式:41个人排成一个圆圈,由第1个人开始报数,每到第3个人该人就必须自杀,然后再由下一个重新开始报数,直到所有人都自杀身亡为止。
然而Josephus和他的朋友并不想遵从,Josephus要他的朋友先假装遵从,他将朋友与自己安排在第16个与第31个位置,于是逃过了这场死亡游戏。
约瑟夫问题可用代数分析来求解,假设现在你与m个朋友不幸参与了这个游戏,你要如何保护你与你的朋友?

4、在三位的整数中,例如153可以满足13+53+3^3=153,这样的数称之为Armstrong数,试写出一程序找出所有三位数的Armstrong数。

5、将一组数字、字母或符号进行排列,以得到不同的组合顺序,例如1 2 3这三个数的排列组合有:123、1 3 2、2 1 3、2 3 1、3 1 2和3 2 1。

6、古典问题:若有一只兔子每个月生一只小兔子,小兔子一个月后也开始生产。起初只有一只兔子,一个月后就有两只兔子,二个月后就有三只兔子,三个月后有五只兔子(小兔子投入生产),n个月后有多少只兔子?

7、请根据杨辉三角的规律,用JavaScript实现杨辉三角。

8、有一母牛,到4岁可生育,每年一头,假设所生均是一样的母牛,到15岁绝育,不再能生,20岁死亡,问n年后有多少头牛?

9、公鸡5文钱1只,母鸡3文钱1只,小鸡1文钱买3只,现在用100文钱共买了100只鸡,假设每种至少一只,问:在这100只鸡中,公鸡、母鸡和小鸡各是多少只?

10、假设某人有100,000现金,每经过一次路口需要进行一次交费。交费规则为当现金大于50,000时每次需要交现金的5%,当现金小于或等于50,000时每次交5,000。请写一程序计算此人可以经过多少次路口。

11、一个球从100米高度自由落下,每次落地后反跳回原高度的一半后再落下。求它在第10次落地时,共经过多少米?第10次反弹多高?

12、一个数如果恰好等于它的因子之和,这个数就称为“完数”,例如6=1+2+3。编程找出1000以内的所有完数。

13、猴子第一天摘了若干个桃子,当即吃了一半,还不解馋,又多吃了一个;第二天,吃剩下桃子的一半,还不过瘾,又多吃了一个;以后每天都吃前一天剩下的一半多一个,到第10天想再吃时,只剩下一个桃子了。问第一天共摘了多少个桃子?

14、请谈谈你对排序的理解。

15、排序算法包含诸多术语,例如稳定性、内排序、时间复杂度等,请列举出你所知的术语并加以说明。

16、请列举出你所知的排序算法的平均时间复杂度、最好情况、最坏情况、空间复杂度和稳定性。

17、请用JavaScript实现插入排序。

18、请用JavaScript实现选择排序。

19、请用JavaScript实现堆排序。

20、请用JavaScript实现计数排序。

简答题详解答案:

1、首先将猴子从1到n编号存放在数组中,对猴子的总个数进行循环,循环时将数到编号的猴子从数组删除,而没有数到编号的猴子将调整位置,移动到数组末尾。只要判断该编号数组元素个数大于1就继续循环,直到数组最后只剩下一个编号,那么这个编号对应的猴子就是大王。实现代码为:
function monkeyKing(n, m){
//将各个编号放入数组中
var monkeys=new Array(n+1)
.join(“0”)
.split("")
.map(function(value, key){
return key+1;
});
//只有一个编号就直接返回
if(n1){
return monkeys[0];
}
var i=0;
//如果当前只有一个编号,那么其他位置中都不会存在编号
while(monkeys.length-2 in monkeys){
if((i+1)%m
0){
//数到m时,删除该编号,即踢出圈
delete monkeys[i];
}else{
//将当前编号放到数组末尾并且删除原来位置上的编号
monkeys,push(monkeys[i]);
delete monkeys[i];
}
i++;
}
//只有数组的最后位置存在编号
return monkeys[monkeys.length-1];
}
var monkey=monkeyKing(5, 2);
console.log(“最后当王的猴子编号是:”+monkey); //3
[考点] 经典算法题
2、如果柱子标为A、B、C,要由A搬至C,在只有一个盘子时,就将它直接搬至C,当有两个盘子,就将B当作辅助柱。如果盘数超过两个,将第三个以下的盘子遮起来,就很简单了,即每次处理两个盘子,也就是A->B、A->C、B->C这三个步骤,而被遮住的部分,其实就是进入程序的递归处理。事实上,若有n个盘子,则移动完毕所需次数为2n-1,所以当盘数为64时,所需次数为264-1=18446744073709551615,为5.05390248594782e+16年,如果对这个数字没什么概念,可假设每秒钟搬一个盘子,也要约5850亿年左右。实现代码为:
function hanou(n,x,y,Z){
if(n1){
console.log(“移动片 1 从”+x+“到”+z);
} else{
hanou(n-1,x,z,y);
console.log(“移动片”+n+“从”+x+“到”+z);
hanou(n-1,y,x,z);
}
}
hanou(3,“A”,“B”,“C”);
程序的运行结果为:
移动片1 从A到C
移动片2 从A到B
移动片1 从C到B
移动片3 从A到C
移动片1 从B到A
移动片2 从B到C
移动片1 从A到C
[考点] 经典算法题
3、实际上只要画两个圆圈就可以让自己与朋友免于死亡游戏,这两个圆圈中内圈是排列顺序,而外圈是自杀顺序,如下图所示。
http://www.yfzxmn.cn/newyfB12/tu/2002/j/cm/qd1.10BFF8B.jpg
排列顺序和自杀顺序
如果要使用公式来求解,那么只要将排列当作环状来处理,在阵列中由计数1开始,每三个数得到一个计数,直到计数达41为止;然后将阵列由索引1开始列出,就可以得知每个位置的自杀顺序,这就是约瑟夫排列。41个人报数的约琴夫排列如下所示。
14 36 1 38 15 2 24 30 3 16 34 4 25 17 5 40 31 6 18 26 7 37 19 8 35 27 9 20 32 10 41 21 11 28 39 12 22 33 13 29 23
由上可知,最后一个自杀的是在第31个位置,而倒数第二个自杀的要排在第16个位置,之前的人都死光了,所以他们也就不知道约琴夫与他的朋友并没有遵守游戏规则了。实现代码为:
var N=41,M=3,man=[],
count=1,i=0,pos=-1,
alive=3; //想救的人数
while(count<=N){
do{
pos=(pos+1)%N; //环状处理
if(!man[pos])
i++;
i=f(i
M){ //报数为3
i=0;
break;
}
} while (1);
man[pos]=count;
count++;
}
console.log(“约琴夫排列:”,man.join(""));
var txt=“L表示要救的”+alive+“个人要放的位置:”;
for(i=0;i<N;i++){
if(man[i]>(N-alive))
txt+=“L”;
else
txt+=“D”;
if((i+1)%5=0)
txt+="";
}
console.log(txt);
程序的运行结果为:
约琴夫排列:14 36 1 38 15 2 24 30 3 16 34 4 25 17 5 40 31 6 18 26 7 37 19 8 35 27 9 20 32 10 41 21 11 28 39 12 22 33 13 29 23
L表示要救的3个人要放的位置:DDDDD DDDDD DDDDD LDDDD DDDDD DDDDD LDDDL DDDDD D
[考点] 经典算法题
4、Armstrong数的寻找,其实是将一个数字分解为个位数、十位数、百位数……,只要使用除法与余数运算即可求出个、十、百位的数字,例如输入一个数字为abc,则:
百位:a=Math.floor(input/100)
十位:b=Math.floor((input%100)/10)
个位:c=input%10
实现代码为:
var a, b, c, x, y, txt=“Armstrong数: “;
for (var num=100; num<=999; num++) {
a=Math.floor(num/100);
b=Math.floor((num% 100)/10);
c=num% 10;
x=aaa+bbb+ccc;
y=num;
if(xnum)
txt+=num+"";
}
console.log(txt); //153 370 371 407
[考点] 经典算法题
5、可以使用递归将问题切割为较小的单元进行排列组合,例如1 2 3 4的排列可以分为1[2 3 4]、2[1 3 4]、3[1 2 4]、4[1 2 3]进行排列。这个过程可以使用旋转法来实现,即先将旋转间隔设为0,再将最右边的数字旋转至最左边,并逐步增加旋转的间隔,然后对后面的子数组使用递归的方式进行求解。例如:
1 2 3 4->旋转1->继续将右边2 3 4进行递归处理。
2 1 3 4->旋转1 2变为2 1->继续将右边1 3 4进行递归处理。
3 1 2 4->旋转1 2 3变为3 1 2->继续将右边1 2 4进行递归处理。
4 1 2 3->旋转1 2 3 4变为4 1 2 3->继续将右边1 2 3进行递归处理。
实现代码为:
var N=4,
num=[];
for(var i=1;i<=N;i++)
num[i]=i;
perm(num,1);
function perm(num,i){
var j,k,tmp;
if(i<N){
for(j=i;j<=N;j++){
tmp=num[j];
//旋转该区段最右边数字至最左边
for(k=j;k>i;k–)
num[k]=num[k-1];
num[i]=tmp;
perm(num,i+1);
//还原
for(k=i;k<j;k++)
num[k]=num[k+1];
num[j]=tmp;
}
} else{ //显示此次排列
var txt="";
for(j=1;j<=N;j++)
txt+=num[j]+"";
console.log(txt);
}
}
程序的运行结果为:
1 2 3 4
1 2 4 3
1 3 2 4
1 3 4 2
1 4 2 3
1 4 3 2
……(由于结果较长,此处省略罗列)
[考点] 经典算法题
6、兔子的出生规律数列为1,1,2,3,5,8,13,21,…实际上是求解斐波那契数列,公式为:S(n)=S(n-1)+S(n-2)。首先用k表示要求解多少个月,k1表示上个月的兔子数量,k2代表上上个月的兔子数量,sum为兔子的总数。然后从1月开始循环,通过斐波那契数列公式得到sum=k1+k2,之后,把k1(上个月的兔子数量)赋值给k2(上上个月的兔子数量),再把sum(当月的兔子总数)赋值给k1(上个月的兔子数量)。循环结束后输出的sum就是兔子的总数了。JavaScript代码实现为:
var k=12, //一共12个月
k1=1, //记录上个月兔子数量
k2=0, //记录上个月兔子数量
sum=0; //总和
for(var i=1;i<k;i++){
sum=k1+k2; //当月的兔子和
k2=k1; //上个月的兔子数量赋值给上上个月的记录
k1=sum; //当月的兔子数量赋值给上个月的记录
}
console.log(sum); //144
[考点] 经典算法题
7、杨辉三角是二项式系数在三角形中的一种几何排列,欧洲的帕斯卡在1654年发现这个规律,所以也叫帕斯卡三角形。杨辉三角具有以下规律:
(1)第n行的数字有n项。
(2)第n行的数字和为2^(n-1)。
(3)每行数字左右对称,由1逐渐增大。
(4)第n行的m个数可表示为C(n-1,m-1),即为从n-1个不同元素中取m-1个元素的组合数。
(5)每个数字等于上一行的左右两个数字之和,即第n+1行的第i个数等于第n行的第i-1个数和第i个数之和,这是杨辉三角组合数的性质之一。即C(n+1,i)=C(n,i)+C(n,i-1)。
根据杨辉三角的规律,可以通过一个二维数组,把第一位和最后一位的值存入数组,然后通过公式C(n+1,i)=C(n,i)+C(n,i-1)遍历二维数组求出每行其余的值。JavaScript代码实现为:
var a=[],
i,j,txt;
for(i=0;i<6;i++){
a[i]=[];
a[i][0]=1;
a[i][i]=1;
}
//将第一位和最后一位以外的值保存在数组中
for(i=2;i<6;i++){
for(j=1;j<i;j++){
a[i][j]=a[i-1][j-1]+a[i-1][j];
}
}
for(i=0; i<6; i++) {
txt="";
for(j=0;j<=i;j++){
txt+=a[i][j]+"";
}
console.log(txt);
}
程序的运行结果为:
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 10 10 5 1
[考点] 经典算法题
8、根据条件定义一个函数,参数n代表多少年,定义最开始牛的数量为1。在循环中,当母牛年龄大于4并且小于15时,每年可以生一头小牛(即牛的总数加1),然后递归调用这个函数,函数的参数为n减去已过去的年数。另外,函数内还要实现牛的年龄为20时,牛的数量减1。实现代码为:
var num=1;
function bull(n) {
for (var j=1; j<=n; j++) {
if (j>=4 &&j<15){
num++;
bull(n-j);
}
if(j
20){
num–;
}
}
return num;
}
console.log(bull(8)); //7
[考点] 经典算法题
9、根据百钱买百鸡的要求,可以设有i只公鸡,j只母鸡,k只小鸡,并且它们的总数为100,总价i×5+j×3+k×1为100文钱。依次对公鸡、母鸡、小鸡的总数进行循环来求出符合这两个公式的最优解。实现代码为:
var i,j,k;
for(i=1;i<100;i++){
for(j=1;j<100; j++){
for(k=1;k<100; k++){
if((i+j+k100)&&(i5+j3+k/3=100)){
console.log(“公鸡:”,i,‘只,母鸡:’,j,‘只,小鸡:’,k,‘只’);
}
}
}
}
程序的运行结果为:
公鸡: 4只, 母鸡: 18只, 小鸡: 78只
公鸡: 8只, 母鸡: 11只, 小鸡: 81只
公鸡: 12只, 母鸡: 4只, 小鸡: 84只
[考点] 经典算法题
10、初始条件为某人拥有的总现金为100,000,初始经过路口的次数为0,当金额低于5,000时,不能再过路口。所以可以通过循环来求次数,当现金大于50,000时,剩余金额为:总金额×(1-5%),当现金小于50,000时,剩余金额为:总金额-5000,依次循环累加过路口的次数,直到不符合条件退出循环。实现代码为:
var sum, num;
for (sum=100000, num=0; sum>=5000;) {
if(sum>=50000) {
sum=0.95*sum;
} else {
sum=sum-5000;
}
num++;
}
console,log(num); //23
[考点] 经典算法题
11、根据题目要求,设初始总高度为100米,已知每次下落后反弹回的高度为上一次的一半,循环10次,每次循环都对上次反弹后的高度除以2并且将结果累加到总高度中,从而求解出共经过多少米和第10次的反弹高度。实现代码为:
var k=100,
sum=100;
for (var i=1; i<=10; i++) {
k/=2;
sum+=k;
}
console.log(“共经过:”,sum,“米,第10次反弹高:”,k,“米”);
程序的运行结果为:
共经过: 199.90234375米, 第10次反弹高: 0.09765625米
[考点] 经典算法题
12、外层循环1000次,每次循环得到的i传入下个循环内,内部循环求解出能够整除i的数k,如果整除,即说明k是i的一个因子,再用sum累加k,直到sum+1=i的条件成立,说明i是一个完数。需要注意的是求解出的因子是不包括1的,所以还需要额外的加1到sum中,并且i的因子是不会大于i/2的,所以判断内部循环是否继续的条件为不大于i/2。实现代码为:
var i, sum, k, txt="";
for (i=2;i<=1000; i++) {
sum=0;
for (k=2; k<=i/2; k++) {
if(i%k
0){
sum+=k;
}
}
if(sum+1==i) {
txt+=i+””;
}
}
console.log(txt); //6 28 496
[考点] 经典算法题
13、采用逆向思维,从后往前推断,发现其中有相同的地方,即出现递推公式,因此,可以采用递归方法。令S10=1,可以得出S9=2(S10+1),简化罗列关系为:
S9=2S10+2
S8=2S9+2

Sn=2Sn+2
实现代码为:
var S=0,
n=1; //最后一天桃子的数量
for(var i=1; i<10; 1++){
s=(n+1)2;
n=s;
}
console.log(“第一天摘的桃子数量为”,s); //1534
[考点] 经典算法题
14、使一串记录,按照其中的某个或某些关键字的大小,递增或递减排列起来的操作叫作排序。排序算法就是如何使得记录按照要求进行排列的方法。排序算法在很多领域都得到了相当的重视,尤其是在大量数据的处理方面。一个优秀的算法可以节省大量的资源。在各个领域中考虑到数据的各种限制和规范,要得到一个符合实际的优秀算法,需要经过大量的推理和分析。
例如:随机输入一个包含n个数的序列:a1,a2,a3,…,an,通过算法输出n个数的顺序排列:a1’,a2’,a3’,…,an’,使得a1’≤a2’≤a3’≤…≤an’(也可以实现从大到小的排序,不唯一)。
[考点] 排序算法
15、术语如下所列。
(1)稳定性:如果a原本在b前面,而a=b,排序之后a仍然在b的前面。
(2)不稳定性:如果a原本在b的前面,而a=b,排序之后a可能会出现在b的后面。
(3)内排序:所有排序操作都在内存中完成。
(4)外排序:由于数据太大,因此把数据放在磁盘中,而排序通过磁盘和内存的数据传输才能进行。
(5)时间复杂度:一个算法执行所耗费的时间。
(6)空间复杂度:运行完一个程序所需内存的大小。
[考点] 排序算法
16、排序算法的参数对比如下表所列。
九种排序算法对比
排序算法 平均时间复杂度 最好情况 最坏情况 空间复杂度 排序方法 稳定性
冒泡排序 O(n2) O(n) O(n2) O(l) In-place 稳定
插入排序 O(n2) O(n) O(n2) O(l) In-place 稳定
归并排序 O(nlogn) O(nlogn) O(nlogn) O(n) Out-pace 稳定
快速排序 O(nlogn) O(nlogn) O(n2) O(logn) In-place 不稳定
选择排序 O(n2) O(n2) O(n2) O(l) In-place 不稳定
希尔排序 O(nlogn) O(nlog2n) O(nlog2n) O(l) In-place 不稳定
堆排序 O(nlogn) O(nlogn) O(nlogn) O(l) In-place 不稳定
计数排序 O(n+k) O(n+k) O(n+k) O(k) Out-place 稳定
桶排序 O(n+k) O(n+k) O(n2) O(n+k) Out-place 稳定
注释:n表示数据规模;k表示“桶”的个数;In-place表示占用常数内存,不占用额外内存;Out-place表示占用额外内存。
[考点] 排序算法
17、插入排序的基本思想是:对于给定的一组记录,初始时假设第一个记录自成一个有序序列,其余的记录为无序序列;接着从第二个记录开始,按照记录的大小依次将当前处理的记录插入之前的有序序列中,直至最后一个记录插入有序序列中为止。算法原理如下:
(1)设置监视哨r[0],将待插入记录的值赋值给r[0]。
(2)设置开始查找的位置j。
(3)在数组中进行搜索,搜索中将第j个记录后移,直至r[0]≥r[j]为止。
(4)将r[0]插入r[i+1]的位置上。
以数组[38,65,97,76,13,27,49]为例,直接插入排序具体步骤如下所示。
第一步插入38以后:[38] 65 97 76 13 27 49
第二步插入65以后:[38 65] 97 76 13 27 49
第三步插入97以后:[38 65 97] 76 13 27 49
第四步插入76以后:[38 65 76 97] 13 27 49
第五步插入13以后:[13 38 65 76 97] 27 49
第六步插入27以后:[13 27 38 65 76 97] 49
第七步插入49以后:[13 27 38 49 65 76 97]
根据以上的实现原理,具体的实现代码如下所示。
function insertSort(arr){
var tmp;
//已经间接将数组分成了两部分,下标小于当前位置的(左边的)是排好序的序列
for (vari=1,len=arr.length;i<len;i++){
//获得当前需要比较的元素值
tmp=arr[i];
//内层循环,控制比较并插入
for(var j=i-1;j>=0;j–){
if(tmp<arr[j]){
arr[j+1]=arr[j];
arr[j]=tmp;
} else{
break;
}
}
}
return arr;
}
可以对传统的插入排序算法进行改进,在查找插入位置时使用二分查找的方式。改进后的插入排序算法如下所示。
function insertSort2(arr) {
var key, left, right, middle;
for (var i=1; i<arr.length; i++) {
key=arr[i];
left=0;
right=i-1;
while left<=right) {
middle=Math.floor((left+right)/ 2);
if(key<arr[middle]) {
right=middle-1;
} else {
left=middle+1;
}
}
for (var j=i-1; j>=left; j–) {
arr[j+1]=arr[j];
}
arr[left]=key;
}
return arr;
}
[考点] 排序算法
18、选择排序是一种简单直观的排序算法,它的基本原理如下:对于给定的一组记录,经过第一轮比较后得到关键字最小的记录,然后将该记录与第一个记录的位置进行交换;接着对不第一个记录以外的其他记录进行第二轮比较,得到关键字最小的记录并将它与第二个记录进行位置交换;重复该过程,直到需要比较的记录只有一个时为止。算法原理如下。
每一轮在n-i+1(i=1,2,…,n-1)个记录中选择关键字最小的记录作为有序序列中第i个记录,其中最便捷的是简单选择排序,其过程如下:通过n-i次关键字间的比较,从(n-i+1)个记录中选择出关键字最小的记录,并与第i个记录交换位置。
以数组[38,65,97,76,13,27,49]为例,具体步骤如下。
第一轮排序后:13 [65 97 76 38 27 49]
第二轮排序后:13 27 [97 76 38 65 49]
第三轮排序后:13 27 38 [76 97 65 49]
第四轮排序后:13 27 38 49 [97 65 76]
第五轮排序后:13 27 38 49 65 [97 76]
第六轮排序后:13 27 38 49 65 76 [97]
最后排序结果:13 27 38 49 65 76 97
根据以上实现原理,具体的实现代码如下所示。
function selectSort(arr){
var len=arr.length,
p,tmp,
//外层控制轮数
for (vari=0;i<len-1; 1++){
p=i; //先假设最小值的位置
//内层控制比较次数,比较i后边的元素
for(var j=i+1;j<len;j++){
if(arr[p]>arr[j]){ //arr[p]是当前已知的最小值
p=j; //比较发现更小的值时,就记录下最小值的位置
}
}
//如果发现最小值的位置与当前假设的位置i不同,则位置互换
if(p!=i){
tmp=arr[p];
arr[p]=arr[i];
arr[i]=tmp;
}
}
return arr;
}
[考点] 排序算法
19、堆是一种特殊的树形数据结构,其每个结点都有一个值,通常提到的堆都是指一棵完全二叉树,其根结点的值小于(或大于)两个子结点的值,同时,根结点的两棵子树也分别是一个堆。
堆排序是一种树形选择排序,在排序过程中,将R[1,…,n]看成一棵完全二叉树顺序存储结构,利用完全二叉树中双亲结点和孩子结点之间的内在关系来选择最小的元素。
堆一般分为大顶堆和小顶堆两种不同的类型。对于给定n个记录的序列(r1),r(2),…,r(n)),当且仅当满足条件(r(i)≥r(2i)&&r(i)≥r(2i+1),i=1,2,…,n)时称之为大项堆,此时,堆顶元素必为最大值。对于给定n个记录的序列(r(1),r(2),…,r(n)),当且仅当满足条件(r(i)≤r(2i)&& r(i)≤r(2i+1),i=1,2,…,n)时称之为小顶堆,此时,堆顶元素必为最小值。
堆排序的思想是对于给定的n个记录,初始时把这些记录看作一棵顺序存储的二叉树,然后将其调整为一个大顶堆,再将堆的最后一个元素与堆顶元素(即二叉树的根结点)进行交换后,堆的最后一个元素即为最大记录;接着将前(n-1)个元素(即不包括最大记录)重新调整为一个大顶堆,再将堆顶元素与当前堆的最后一个元素进行交换后得到次大的记录,重复该过程直到调整的堆中只剩一个元素时为止,该元素即为最小记录,此时可得到一个有序序列。
堆排序主要包括两个过程:一是构建堆;二是交换堆顶元素与最后一个元素的位置。算法原理如下:
(1)将初始待排序关键字序列(R1,R2,…,Rn)构建成大顶堆,此堆为初始的无序区。
(2)将堆顶元素R[1]与最后一个元素R[n]交换,此时得到新的无序区(R1,R2,…,Rn-1)和新的有序区(Rn),且满足R[1,2,…,n-1]≤R[n]。
(3)由于交换后新的堆顶R[1]可能违反堆的性质,因此需要将当前无序区(R1,R2,…,Rn-1)调整为新堆,然后再次将R[1]与无序区最后一个元素交换,得到新的无序区(R1,R2,…,Rn-2)和新的有序区(Rn-1,Rn)。不断重复此过程直到有序区的元素个数为(n-1),则整个排序过程完成。
根据以上的实现原理,具体的实现代码如下所示。
function heapSort(arr){
var heapSize=arr.length,
temp;
//建堆
for (var i=Math.floor(heapSize/2)-1; i>=0; i–){
heapify(arr,i,heapSize);
}
//堆排序
for (var j=heapSize-1;j>=1;j–){
temp=arr[0];
arr[0]=arr[j];
arr[j]=temp;
heapify(arr,0,–heapSize);
}
return arr;
}
/
*
*维护堆的性质
*@param arr 数组
*@param x 数组下标
@param len 堆大小
/
function heapify(arr,x, len){
var 1=2
x+1,
r=2
x+2,
largest=x,
temp;
if(1<len && arr[1]>arr[largest]){
largest=1;
}
if (r<len && arr[r]>arr[largest]) {
largest=r;
}
if(largest!=x) {
temp=arr[x];
arr[x]=arr[largest];
arr[largest]=temp;
heapify(arr, largest, len);
}
}
[考点] 排序算法
20、计数排序是一种稳定的排序算法。计数排序使用一个额外的数组countArr,其中第i个元素是待排序数组arr中值等于i的元素个数。然后根据数组countArr来将arr中的元素排到正确的位置。注意,它只能对整数进行排序。算法原理如下:
(1)找出待排序数组中最大和最小的元素。
(2)统计数组中每个值为i的元素出现的次数,存入数组countArr的第i项,countArr[i]表示待排序数组中等于i的元素出现的次数。
(3)从待排序数组arr的第一个元素开始,将arr[i]放到正确的位置,即前面有几个元素小于或等于它,它就放在第几个位置。
根据以上的实现原理,具体的实现代码如下所示。
function countingSort(arr){
var count=arr.length,
countArr=[];
if(count<=1)
return arr;
var min=Math.min.apply(null, arr), //取出最小值
max=Math.max.apply(null, arr); //取出最大值
for(var i=min;i<=max;i++){
countArr[i]=0;
}
arr.forEach(function (value, key){
countArr[value]=countArr[value]+1;
});
var list=[];
//value是每个元素出现的次数
countArr.forEach(function (value, key){
for (var i=0;i<value;i++){
list.push(key);
}
});
return list;
}
[考点] 排序算法

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我慢慢地也过来了

谢谢大佬的投喂

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值