JavaScript中的数据结构及实战系列(1):队列
开题
张三丰教无忌太极剑法:
还记得吗?
全都记得.
现在呢?
已经忘却了一小半.
啊,已经忘了一大半.
不坏不坏,忘得真快,那么现在呢?
已经全都忘了,忘得干干净净.
好了,你上吧.
长时间写前端代码,将自己以前的积累都忘得一干二净,所以开了一个关于JS的数据结构系列,在业务型程序员的基础上,也要实时的优化自己的代码,让自己的代码有思想是每个程序员最自豪的事情。
本文目录
队列介绍:
相信任何有些编程基础的人都对队列不算陌生,队列是一种先进先出的接口,也就是FIFO(First Input First Output),它也是一种表结构。在队列的队尾插入数据,在队首删除数据,可以想象成我们每天早晨买豆浆时队伍。
队列只要有两个主要的功能:出队(push)和入队(pop)。入队操作在队尾插入新的元素,出队操作返回并删除队首的元素。有时候我们只需要获取队首和队尾但并不一定非要执行队列的出队和入队行为,所以我们又需要一个获取队首(getFirst)和队尾(getLast)的行为。有时我们也有全部清空本队列的动作,所以还要有clear方法。除了以上提到的我们还需要知道队列中有多少个元素,可以用length去获取。
JavaScript实现:
使用数组的push方法和shift方法可以基本完成队列的出队和入队操作,但是我们为了不让其他无关的数组属性去破坏队列结构还需要对其封装一下,所以就有了我们现在的代码:function Queue(){
var items = [];
//入队
this.push = function(obj){
items.push(obj);
};
//出队
this.pop = function(){
return items.shift();
};
//获取队首
this.getFirst = function(){
return items[0];
};
//获取队尾
this.getLast = function(){
return items[items.length-1];
};
//清空本队列
this.clear = function(){
items = [];
}
//获取队列指定位置
this.get = function(k){
return items[k];
};
//获取队列长度
this.length = function(){
return items.length;
};
//队列是否为空
this.empty = function(){
return items.length === 0;
};
}
var lazyQueue = new Queue();
lazyQueue.push({
a: 1,
b: 2
});
lazyQueue.push({
c: 3,
d: 4
});
console.log(lazyQueue.pop());
console.log(lazyQueue.length());
console.log(lazyQueue.clear());
console.log(lazyQueue.pop());
console.log(lazyQueue.length());
输出结果为:
{a: 1, b: 2}
1
undefined
undefined
0
队列的实践:
在我们的开发遇到很多队列的使用,且队列的应用也比较简单,只要在执行完上次任务后再执行本任务,直到队列为空为止。之前分享了PHP以及JavaScript的八大排序方式,但是对于基数排序的JavaScript实现方法没有搞清楚,现在可以补上了。
首先将“基数排序”的概念理解一下: 基数排序(radix sort)属于“分配式排序”(distribution sort),又称“桶子法”(bucket sort)或bin sort,顾名思义,它是透过键值的部份资讯,将要排序的元素分配至某些“桶”中,藉以达到排序的作用,基数排序法是属于稳定性的排序,其时间复杂度为O (nlog(r)m),其中r为所采取的基数,而m为堆数,在某些时候,基数排序法的效率高于其它的稳定性排序法。(取自百度百科)
我们同样以百度百科中的列子:假如有数字: 73, 22, 93, 43, 55, 14, 28, 65, 39, 81
经过基数排序第一次扫描之后按照个位数的大小排序,数字被分配大如下的盒子中:
第0个盒子:
第1个盒子:81
第2个盒子:22
第3个盒子:73, 93, 43
第4个盒子:14
第5个盒子:55, 65
第6个盒子:
第7个盒子:
第8个盒子:28
第9个盒子:39
根据盒子的顺序,对数字经行第一次排序的结果如下:
81, 22, 73, 93, 43, 14, 55, 65, 28, 39
然后根据十位上的数值再将上次排序结果分配到不同的盒子里
第0个盒子:
第1个盒子:14
第2个盒子:22, 28
第3个盒子:39
第4个盒子:43
第5个盒子:55
第6个盒子:65
第7个盒子:73
第8个盒子:81
第9个盒子:93
最后将盒子里的数字取出,组成一个新的列表,该列表即为排好顺序的数字:
14, 22, 28, 39 ,43, 55, 65, 73, 81, 93
使用队列代表盒子,可以实现这个算法,我们需要9个队列,每个对应一个数字。将所有队列保存在一个数组中,使用取余和出发操作决定各位和十位。算法的剩余部分将数字加入对应的队列,根据个位数值重新排序,然后再根据十位数值经行排序,结果即为排好顺序的数字。
function Queue(){
var items = [];
//入队
this.push = function(obj){
items.push(obj);
};
//出队
this.pop = function(){
return items.shift();
};
//获取队首
this.getFirst = function(){
return items[0];
};
//获取队尾
this.getLast = function(){
return items[items.length-1];
};
//清空本队列
this.clear = function(){
items = [];
}
//获取队列指定位置
this.get = function(k){
return items[k];
};
//获取队列长度
this.length = function(){
return items.length;
};
//队列是否为空
this.empty = function(){
return items.length == 0;
};
}
/********基数排序**********/
//初始化队列
var queues = [];
for (var i = 0; i <10; i++){
queues[i] = new Queue();
}
//随机产生10个二位的整数
var nums = [];
for (var i = 0; i < 10; i++) {
nums[i] = Math.floor(Math.random()*100);
}
console.log('初始化数组:', nums);
//按照个位数字入相应的队列
for (var i=0; i<10; i++) {
queues[nums[i]%10].push(nums[i]);
}
//收集队列中的数字放在数组nums中
var j=0;
var nums2 = [];
for (var i=0; i<10; i++) {
while (!queues[i].empty()){
nums2[j++] = queues[i].pop();
}
}
console.log('个位数字入队排序后的数组:', nums2);
//由于上面已经全部出队了,所以可以使用已经初始化的queue队列数组
//按照十位数字入相应的队列
for (var i = 0; i < 10; i++) {
!isNaN(Math.floor(nums2[i] / 10)) && queues[Math.floor(nums2[i] / 10)].push(nums2[i]);
}
//收集队列中的数字放在数组nums中
j=0;
var nums3 = []
for (var i=0; i<10; i++) {
while (!queues[i].empty()){
nums3[j++] = queues[i].pop();
}
}
console.log('十位数字入队排序后的数组:', nums3);
结果如下:
初始化数组: [15, 48, 46, 77, 23, 72, 93, 25, 0, 75]
个位数字入队排序后的数组: [0, 72, 23, 93, 15, 25, 75, 46, 77, 48]
十位数字入队排序后的数组: [0, 15, 23, 25, 46, 48, 72, 75, 77, 93]