JavaScript的数据结构与算法
栈常见的一些操作
| push(element): | 添加一个新元素到栈顶位置 |
| pop(): | 移除栈顶的元素,同时返回被移除的元素 |
| peek(): | 返回栈顶的元素,不对栈做任何的修改(这个方法不会移除栈顶的元素,仅仅返回他) |
| isEmpty(): | 如果栈里面没有任何元素,就返回true,否则返回false。 |
| size(): | 返回栈里面元素的个数,这个方法和数组的length属性很类似 |
| toString(): | 将栈结构的内容以字符形式返回 |
栈的两种实现方式
- 基于数组
- 基于链表
对象与方法的区分
Methods:和某一个对象实力有关系。
function:普通的函数
封装一个栈结构
//封装一个栈结构
//实现栈中各个函数的方法
function stack() {
this.items = [];
//1.将元素压入到栈
Stack.prototype.push = function (element) {
this.items.push(element);
};
//2.从栈中取出元素
Stack.prototype.pop = function () {
return this.items.pop();
};
//3.查看一下栈顶元素
Stack.prototype.peek = function () {
return this.items[items.length - 1];
};
//4.判断栈是否为空
Stack.prototype.isEmpty = function () {
return this.items.length === 0;
};
//5.获取栈中元素的个数
Stack.prototype.size = function () {
return this.items.length;
};
//6.toString方法
Stack.prototype.toString = function () {
var resultString = "";
for (var i = 0; i < this.items.length; i++) {
resultString += this.items[i] + "";
}
return resultString;
};
}
//栈的使用
var s = new stack();
栈的使用
// // 栈的使用
// var s = new Stack();
//函数:十进制转二进制
function dec2bin(decNumber) {
//1.定义对象
var stack = new Stack();
//2.循环操作
while (decNumber > 0) {
//获取余数,并且放入到栈中
stack.push(decNumber % 2);
//获取整除后的结果,并且放入到栈中,作为下一次运行的数字
decNumber = Math.floor(decNumber / 2);
}
//从栈中取出0和1
var binaryString = "";
while (!stack.isEmpty()) {
binaryString += stack.pop();
}
return binaryString;
}
//测试十进制转二进制的函数
alert(dec2bin(100))
队列结构
受限的线性结构:
先进先出
- 线结构
- 队列结构
队列:他是一种受限的线性表,先进先出进行删除操作
而在表的后端进行删除操作
打印队列:
- 有五份文档需要打印,这些文档会按照顺序放入到打印队列中
- 打印机会依次从队列中取出文档,优先放入的文档,优先被取出,并且对该文档进行打印
- 以此类推,知道队列中不再有新的文档
线程队列:
- 在开发中,为了让人物可以并行处理,通常会开启多个线程
- 但是我们不能让大量的线程同时运行处理任务(占用过多的资源)
- 这个时候,如果需要开启线程处理任务的情况,我们就会使用线程队列
- 线程队列会依照次序来启动线程,并处理对应的任务
队列的常见操作
| equeue(element) | 向队列尾部添加一个或多个新的项 |
| dequeue() | 移除队列的第一(即排在最前面的)项,并返回被移除的元素 |
| front() | 返回队列中第一个元素---最先被添加,也将是最先被移除的元素。队列不做任何的变动,(不移除元素,只返回元素信息--与Stack 的peek方法类似) |
| isEmpty() | 如果队列中不包含任何元素,返回true,否则返回false |
| size() | 返回队列包含的元素个数,与数组length属性类似 |
| toString() | 将队列中的内容,转换成字符串形式 |
方法的实现步骤
//封装队列
function Queue() {
this.items = [];
//1.将元素加入到队列中
Queue.prototype.enqueue = function (element) {
this.items.push(element);
};
//2.从队列中删除前端元素
Queue.prototype.dequeue = function () {
return this.items.shift();
};
//3.查看前端元素
Queue.prototype.front = function () {
return this.items[0];
};
//4.查看队列是否为空
Queue.prototype.isEmpty = function () {
return this.items.length == 0;
};
//5.查看队列中元素的个数
Queue.prototype.size = function () {
return this.items.length;
};
//6.toString方法
Queue.prototype.toString = function () {
var resultString = '';
for (var i = 0; i < this.items.length; i++) {
resultString += this.items[i] + '';
}
return resultString;
};
}
//使用队列
var queue = new Queue();
queue.enqueue('abc');
queue.enqueue('def');
queue.enqueue('ghi');
queue.enqueue('jkl');
alert(queue);
queue.dequeue()
alert(queue)
击鼓传花游戏
//面试题:击鼓传花的实现
function passGame(nameList, num) {
//1.首先创建一个栈队列
var queue = new Queue();
//2.将所有人一次加入到队列中
for (var i = 0; i < nameList.length; i++) {
queue.enqueue(nameList[i]);
}
//3.开始查数字
while (queue.size() > 1) {
//不是num的时候加入到队列的末尾
//是num这个数字的时候,将其从队列中山删除
//3.1num数字之前的人重新放在队列的末尾
for (var i = 0; i < num - 1; i++) {
queue.enqueue(queue.dequeue());
}
//3.2num对应这个人,直接从队列中删除
queue.dequeue();
//4.获取剩下的那个人
alert(queue.size());
var endName = queue.front();
alert("最终剩下的那个人:" + endName);
return nameList.indexOf(endName);
}
}
names = ["Lili", "Lucy", "tom", "Lilei", "why"];
alert(passGame(names, 3));
封装优先级队列
//封装优先级队列
function priorityQueue(){
//在priorityQueue重新小黄见了一个类,可以理解成内部类
function priorityElement(element,priority){
this.element=element
this.priority=priority
}
//封装属性
this.items=[]
//实现插入方法
priorityQueue.prototype.enqueue=function(element,priority){
//1.创建priorityElement对象
var queueElement=new priorityElement(element,priority)
//2.判断队列是否为空
if(this.items.length==0){
this.items.push(queueElement)
}else{
var added=falsefor(var i=0;i<this.items.length;i++){
if(queueElement.priority<this.iitems[i]){
this.items.splice(i,0,queueElement)
added=true
break
}
}
if(!added){
this.items.push(queueElement)
}
}
}
}
//测试代码
var pq=new PriorityQueue()
链表作用
链表和数组一样,可用于存储一系列的元素,但链表和数组的实现机制完全不同
数组:我们要存储多个多个元素,可能最常用的就是数组
数组 缺点:
- 数组的创建需要申请一段连续的内存空间,并且大小都是固定的,,所以当数组不能满足需求时,需要扩容
- 并且在数组的中间插入的成本很高,需要进行大量元素的位移
链表的优点:
- 内存空间不必是连续的,可以陈宏芬利用计算机的内存,实现灵活的内存动态管理
- 链表不必在创建时就确定大小,并且可以无限延伸下去
- 链表插入和删除数据时,时间复杂度可以达到O(1),相对数组效率高很多
链表的常见操作
| append(element) | 向链表尾部添加一个新的项 |
| insert(position,element) | 向链表的特定位置插入一个新的项 |
| get(position) | 获取对应位置元素 |
| indexOf(element) | 返回元素在列表中的索引。如果列表中没有该元素,则返回-1 |
| update(position) | 修改某个位置的元素 |
| removeAt(position) | 从列表的特定位置移除一项 |
| remove(element) | 从列表中移除一项 |
| isEmpty() | 如果链表中不包含任何的元素,返回true,如果链表的长度大于0则返回false |
| size() | 返回链表中包含元素个数,与数组的length属性相类似 |
| toString() | 由于列表使用了Node类,就需要重写继承自JavaScript对象默认的toString方法,让其输出元素的值 |
整体操作方法与数组非常类似,因为链表本身就是一种可以代替数组的结构
append方法的实现
<script>
//封装链表类
function LinkList() {
//内部的类:节点类
function Node(data) {
this.data = data;
this.length = 0;
//1.追加方法
LinkList.prototype.append = function (data) {
//1.创建新节点
var newNode = new Node(data);
//判断是否添加的是的第一个节点
if (this.length == 0) {
this.head = newNode;
} else {
var current = this.head;
while (current.next) {
current = current.next;
}
current.next = newNode;
}
//3.length+1
this.length += 1;
};
}
}
</script>
toString方法
//2.toString方法
LinkList.prototype.toString = function () {
//1.定义变量
var current = this.head;
var ListString = "";
//2.循环获取一个节点
while (current) {
listString += current.data + "";
current = current.next;
}
return listString;
};
}
insert方法
LinkList.prototype.insert = function (position, data) {
//1.对position进行越界判断
if (position < 0 || position > this.length) return false;
//2.根据data创建newNode
var newNode = new Node(data);
//3.判断插入的位置是否是第一个
if (position == 0) {
newNode.next = this.head;
this.head = newNode;
} else {
var index = 0;
var current = this.headvar;
previous = null;
while (index++ < position) {
previous = current;
current = current.next;
}
newNode.next = current;
previous.next = newNode;
}
};
get方法
//4.get方法
LinkedList.prototype.get=function(position){
//1.越界处理
if(position<0||position>=this.length)return null;
//2.获取对应的data
var current = this.head
var index=0
while(index++<position){
current=current.next
}
return current.data
}
}
update方法
//6.update方法
LinkedList.prototype.update = function (position, newData) {
//1.越界处理
if (position < 0 || position >= this.length) return false;
//2.查找正确的节点
var current = this.head;
var index = 0;
while (index++ < position) {
current = current.next;
}
//3.将position位置的node的data修改成newData
current.data = newData;
return true;
};
removeAt方法
//7.removeAt方法
LinkedList.prototype.removeAt = function (position) {
//1.越界处理
if (position < 0 || position >= this.length) return null;
//2.判断是否删除第一个节点
var current = this.head;
if (position == 0) {
this.head = this.head.next;
} else {
var index = 0;
var previous = null;
while (index++ < position) {
previous = current;
current = current.next;
}
//前一个节点的next指向,current的next即可
previous.next = current.next;
}
this.length -= 1;
return current.data;
};
remove的方法
//8.remove的方法
LinkList.prototype.remove = function (data) {
//1.获取data在列表中的位置
var position = this.indeOf(data);
//根据位置信息,删除节点
return this.removeAt(position);
};
isEmpty方法
//9.isEmpty方法
LinkList.prototype.isEmpty = function () {
return this.length==0;
};
size()方法
LinkList.prototype.size = function (data) {
return this.length
};
单向链表
缺点:
- 只能从头到尾遍历
- 链表的相连过程是单向的
- 实现原理也是上一个链表中有指向下一个的引用
比较明显的缺点:
我们可以轻松到达下一个节点,但是回到前一个节点是很难的
双向链表
- 既可以从头遍历到尾,又可以从尾遍历到头
- 链表相连的过程是双向的
- 一个节点既有向前连接的引用,也有向后连接的引用
双向链表的特点:
- 可以使用一个head和一个tail分别指向头部和尾部的节点
- 每个节点都由三部分组成:前一个节点的指针(prev)/保存的元素/后一个节点的指针(next)
- 双向链表的第一个节点的prev是null
- 双向链表的最后的节点的next是null
双向链表的常见操作
| append(element) | 向链表尾部添加一个新的项 |
|---|---|
| insert(position,element) | 链表的特定位置插入一个新的项 |
| get(position) | 获取对应位置元素 |
| indexOf(element) | 返回元素在列表中的索引。如果列表中没有该元素,则返回-1 |
| update(position,element) | 修改某个位置的元素 |
| removeAt(position) | 从列表的特定位置移除一项 |
| remove(element) | 从列表中移除一项 |
| isEmpty() | 如果链表中不包含任何的元素,返回true,如果链表的长度大于0则返回false |
| size() | 返回链表中包含元素个数,与数组的length属性相类似 |
| toString() | 由于列表使用了Node类,就需要重写继承自JavaScript对象默认的toString方法,让其输出元素的值 |
| forwardString() | 返回正向遍历的节点字符串形式 |
| backwordString() | 返回反向遍历的节点字符串形式 |
1.追加方法
//1.追加方法
LinkedList.prototype.append = function (data) {
//1.创建新的节点
var newNode = new Node(data);
//2.判断添加的是第一个节点
if (this.length == 0) {
this.head = newNode;
} else {
//找到最后一个节点
var current = this.head;
while (current.next) {
current = current.next;
}
//最后的next指向新的节点
current.next = newNode;
}
};
toString,forwardString,backwardString方法
//2.toString方法
//2.1
DoublyLinkList.prototype.toString = function () {
return this.backwardString();
};
//2.2forwardString方法
DoublyLinkList.prototype.forwardString = function () {
//1.定义变量
var current = this.tail;
var resultString = "";
//2.依次向前遍历,获取每一个节点
while (current) {
resultString += current.data + "";
current = current.next;
}
return resultString;
};
//2.3backwardString方法,向后
DoublyLinkList.prototype.backwardString = function () {
//1.定义变量
var current = this.head;
var resultString = "";
//2.依次向后遍历,获取每一个节点
while (current) {
resultString += current.data + "";
current = current.next;
}
return resultString;
}
};
indexOf方法
//indexOf方法
DoublyLinkList.prototype.indexOf = function (data) {
//1.定义变量
var current = this.head;
var index=0
//2.查找和data相同的节点
while(current){
if(current.data==data){
return index;
}
curent=current.next
index+=1
}
return -1
};
update方法
//6.update方法
DoublyLinkList.prototype.update = function (position, newData) {
//1.越界处理
if (position < 0 || position >= this.length) return false;
//2.寻找正确的节点
var current = this.head;
var index = 0;
while (index++ < position) {
current = current.next;
}
//3.修改找到节点的data信息
current.data = newData;
return true;
};
removeAt方法
//removeAt方法
DoublyLinkList.prototype.removeAt = function (data) {
if (position < 0 || position >= this.length) return null;
//2.判断是否只有一个节点
var current = this.head;
if (this.length == 1) {
this.head = null;
this.tail = null;
} else {
if (position == 0) {
this.head.next.prev = null;
this.head = this.head.next;
} else if (position == this.length - 1) {
current = this.tail;
this.tail.prev.next = null;
} else {
var index = 0;
while (index++ < position) {
currrent = current.next;
current.next.prev = current.prev;
}
}
}
};
remove方法
//remove方法
DoublyLinkList.prototype.remove = function (data) {
//1.获取data下标值
var index = this.indexOf(data);
//2.根据Index删除对应位置的节点
return this.removeAt(index);
};
isEmpty方法
//isEmpty方法
DoublyLinkList.prototype.isEmpty = function (data) {
return this.length==0
};
size方法
DoublyLinkList.prototype.size = function (data) {
return this.length==0
};
获取链表的第一个元素
//获取链表的第一个元素
DoublyLinkList.prototype.getHead = function (data) {
return this.head.data
};
获取链表的最后一个元素
DoublyLinkList.prototype.getTail = function (data) {
return this.tail.data
};
——本文学习于coderwhy老师的数据结构与算法