js数据结构与算法
1.栈
栈是一种遵从后进先出(LIFO)原则的有序集合。新添加的或待删除的元素都保存在栈的 同一端,称作栈顶,另一端就叫栈底。在栈里,新元素都靠近栈顶,旧元素都接近栈底。
//栈
function Stack() {
//各种属性和方法的声明
let items = [];
//添加元素
this.push = function(element){
items.push(element);
};
//移除元素
this.pop = function(){
return items.pop();
};
//查看栈顶
this.peek = function(){
return items[items.length-1];
};
//查看栈是不是为空
this.isEmpty = function(){
return items.length == 0;
};
//清空栈元素
this.clear = function(){
items = [];
};
//打印栈元素
this.print = function(){
console.log(items.toString());
};
}
2.队列
队列是遵循FIFO(First In First Out,先进先出,也称为先来先服务)原则的一组有序的项。 队列在尾部添加新元素,并从顶部移除元素。最新添加的元素必须排在队列的末尾。
//队列
function Queue() {
//这里是属性和方法
let items = [];
//添加元素
this.enqueue = function(element){
items.push(element);
};
//移除元素
this.dequeue = function(){
return items.shift();//前删
};
//查看队头
this.front = function(){
return items[0];
};
//检查是否为空
this.isEmpty = function(){
return items.length == 0;
};
//打印队列
this.print = function(){
console.log(items.toString());
};
//队列的长度
this.size = function(){
return items.length
};
}
2.1优先队列
队列大量应用在计算机科学以及我们的生活中,我们在之前话题中实现的默认队列也有一些 修改版本。 其中一个修改版就是优先队列。元素的添加和移除是基于优先级的。一个现实的例子就是机 场登机的顺序。
头等舱和商务舱乘客的优先级要高于经济舱乘客。在有些国家,老年人和孕妇(或 带小孩的妇女)登机时也享有高于其他乘客的优先级。
另一个现实中的例子是医院的(急诊科)候诊室。医生会优先处理病情比较严重的患者。通 常,护士会鉴别分类,根据患者病情的严重程度放号。
//优先队列
function PriorityQueue() {
let items = [];
//构造函数
function QueueElement (element, priority){ // {1}
this.element = element;
this.priority = priority;//优先级顺序
}
//添加元素
this.enqueue = function(element, priority){
let queueElement = new QueueElement(element, priority);
let added = false;
for (let i=0; i<items.length; i++){
if (queueElement.priority < items[i].priority){ // {2}
items.splice(i,0,queueElement); // {3}
added = true;
break; // {4}
}
}
if (!added){
items.push(queueElement); //{5}
}
};
this.print = function(){
for (let i=0; i<items.length; i++){
console.log(`${items[i].element} -
${items[i].priority}`);
}
};
//其他方法和默认的Queue实现相同
}
2.2击鼓传花
还有另一个修改版的队列实现,就是循环队列。循环队列的一个例子就是击鼓传花游戏(Hot Potato)。在这个游戏中,孩子们围成一个圆圈,把花尽快地传递给旁边的人。某一时刻传花停止, 这个时候花在谁手里,谁就退出圆圈结束游戏。重复这个过程,直到只剩一个孩子(胜者)。
//击鼓传花
function hotPotato (nameList, num){
let queue = new Queue(); // {1}
//把传过来的值放到队列中
for (let i=0; i<nameList.length; i++){
queue.enqueue(nameList[i]); // {2}
}
let eliminated = '';//被淘汰的人
while (queue.size() > 1){
for (let i=0; i<num-1; i++){
queue.enqueue(queue.dequeue()); // {3}
}
eliminated = queue.dequeue();// {4}
console.log(eliminated + '在击鼓传花游戏中被淘汰。');
}
return queue.dequeue();// {5}
}
let names = ['John','Jack','Camila','Ingrid','Carl'];
let winner = hotPotato(names, 7);
console.log('The winner is: ' + winner);
3.链表
链表存储有序的元素集合,但不同于数组,链表中的元素在内存中并不是连续放置的。每个 元素由一个存储元素本身的节点和一个指向下一个元素的引用(也称指针或链接)组成。
//链表
function LinkedList(){
let Node = function(element){
this.element = element;
this.next = null;
}
this.length = 0;
this.head = null;
//添加元素
LinkedList.prototype.append = function(element){
let node = new Node(element);
let current = null;
if(this.length==0){
this.head = node
}else{
current = this.head;
while(current.next){
current = current.next
}
current.next = node;
}
this.length++;
}
//打印链表
LinkedList.prototype.toString = function (){
let current = this.head;
var listString = '';
while(current){
listString += current.element+' ';
current = current.next
}
return listString
}
//插入元素
LinkedList.prototype.insert = function (position,element){
if(position<0 || position>this.length) return;
let newNode = new Node(element);//创建一个新的节点
let current = this.head;//记入当前的元素
let previous = null;//记入上一个元素
//current变量是对列表中第一个元素的引用。我们需要做的是把node.next的值设为
//current(列表中第一个元素)。现在head和node.next都指向了current。接下来要做的就是
//把head的引用改为node,这样列表中就有了一个新元素。
if(position == 0){
node.next = current;
this.head = node;
}else{
//在列表中间或尾部添加一个元素。首先,我们需要循环访问列表,
//找到目标位置(行{3})。当跳出循环时,current变量将是对想要插入新元素的位置之后一个
//元素的引用,而previous将是对想要插入新元素的位置之前一个元素的引用。在这种情况下,
//我们要在previous和current之间添加新项。因此,首先需要把新项(node)和当前项链接起
//来(行{4}),然后需要改变previous和current之间的链接。我们还需要让previous.next
//指向node(行{5})。
let index = 0;
while(index++ < position){
previous = current;
current = current.next;
}
newNode.next = current;//新节点的
previous.next = newNode;
}
this.length++;
return true;
}
//indexOf方法接收一个元素的值,如果在列表中找到它,就返回元素的位置,否则返回-1
LinkedList.prototype.indexOf = function (element){
let current = this.head;
let index = 0;
while(current){
if(element === current.element){
return index;
}
index++;
current = current.next;
}
return -1;
}
//移除元素通过下标
LinkedList.prototype.removeAt = function (position){
if(position<0 || position>this.length) return null;
let current = this.head;
let previous = null;
let index = 0;
//删除第一项
if(position === 0){
this.head = current.next;
}else{
while(index++ < position){
previous = current;
current = current.next;
}
//将previous与current的下一项链接起来:跳过current,从而移除它
previous.next = current.next;
}
this.length--;
return current.element;
}
//移除元素
LinkedList.prototype.remove = function(element){
let index = this.indexOf(element);
return this.removeAt(index);
};
}
4.双向链表
双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱。所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点。一般我们都构造双向循环链表。
function DoubleLinkedList(){
// 内部类
function Node(element){
this.prev = null;
this.element = element;
this.next = null
}
this.head = null;
this.tail = null;
this.length = 0;
//追加元素
DoubleLinkedList.prototype.append = function(element){
let newNode = new Node(element)
if(this.length === 0){
this.head = newNode;
this.tail = newNode;
}else{
newNode.prev = this.tail;
this.tail.next = newNode;
this.tail = newNode
}
this.length++;
}
// 打印元素
DoubleLinkedList.prototype.toString = function(){
let current = this.head;
let result = '';
while(current){
result+=current.element+' ';
current = current.next;
}
return result
}
// 插入元素
DoubleLinkedList.prototype.insert = function(element,postion){
if(postion < 0 || postion > this.length) return false
//新的节点
let newNode = new Node(element);
if(this.length === 0){
this.head = newNode;
this.tail = newNode;
}else{
if(postion === 0){
this.head.prev = newNode;
newNode.next = this.head;
this.head = newNodw
}else if(postion === this.length){
newNode.prev = this.tail;
this.tail.next = newNode;
this.tail = newNode
}else{
let current = this.head;
let index = 0;
while(index++ < postion){
current = current.next
}
newNode.prev = current.prev;
newNode.next = current;
current.prev.next = newNode;
current.prev = newNode;
}
}
this.length++;
return true
}
}
let list = new DoubleLinkedList()
list.append('aaa')
list.append('bbb')
list.append('ccc')
console.log(list.insert('ddd',2))
console.log(list.toString())