JavaScript实现数据结构与算法
学习笔记:coderwhy的JavaScript实现数据结构与算法的课程
其他章节笔记链接
1. 重要性
2. 线性结构【本文】
3. 哈希表
4. 树结构
5. 图结构
6. 排序和搜索
(二)线性结构
1. 线性结构
1.1 数组的特点
1.1.1 普通数组
优点:通过下标取元素效率非常高。
缺点:常见语言数组不能存放不同类型的数据,所以一般都先封装一个Object类型
1.1.2 底层数组
扩容方法:新建一个大的数组,把原有数组写进去,再添加新元素。
特点:插入和删除性能(效率)都很低。
1.2 栈(受限的线性结构)
1.2.1 定义
栈(stack)是一种受限的线性表,后进先出(LIFO)
1.2.2 应用举例
函数调用栈:
栈溢出:无限次数递归
1.2.3 栈结构面试题
答案:C
1.2.4 栈结构的实现
(1)基于数组
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script>
// Method:和某一个对象实例有联系
// 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[this.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 () {
// 20 10 12 8 7
var resultString = '';
for (var i = 0; i < this.items.length; i++){
resultString += this.items[i] + ' '
}
return resultString
}
};
// 栈的使用
var s = new Stack();
s.push(20);
s.push(10);
s.push(100);
s.push(99);
alert(s);
s.pop();
s.pop();
alert(s);
alert(s.peek())
alert(s.isEmpty())
alert(s.size())
</script>
</body>
</html>
(2)基于链表–JS暂无
1.2.5 栈的相关操作
例子:十进制转换成二进制
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script>
// Method:和某一个对象实例有联系
// 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[this.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 () {
// 20 10 12 8 7
var resultString = '';
for (var i = 0; i < this.items.length; i++){
resultString += this.items[i] + ' '
}
return resultString
}
};
// 函数:将十进制转成二进制
function dec2bin(decNum) {
// 1. 定义栈对象
var stack = new Stack();
// 2. 循环操作
while(decNum > 0){
// 2.1 获取余数,并且放入到栈中
stack.push(decNum % 2);
// 2.2 获取整除后的结果,进行下一次运算
decNum = Math.floor(decNum / 2); // 向下取整
}
// 3. 从栈中取出0和1
var binaryString = '';
while(!stack.isEmpty()){
binaryString += stack.pop();
};
return binaryString;
}
// 测试十进制转成二进制的函数
alert(dec2bin(8));
</script>
</body>
</html>
1.3 队列(受限的线性结构)
1.3.1 队列的定义
先进先出(Frist)
1.3.2 队列的应用
1.3.3 队列的实现
(1)基于数组
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>封装队列</title>
</head>
<body>
<script>
// 封装队列
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("adv");
queue.enqueue("bfff");
queue.enqueue("cbaaa");
queue.enqueue("dbasda");
alert(queue.toString());
// 从队列中删除元素
queue.dequeue()
alert(queue.toString());
queue.dequeue()
alert(queue.toString());
// front方法
alert(queue.front());
// 验证其他方法
alert(queue.isEmpty());
alert(queue.size());
</script>
</body>
</html>
(2)基于链表(性能更高)
1.3.4 队列的相关操作
1.3.5 队列结构面试题–击鼓传花
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>击鼓传花--面试题</title>
</head>
<body>
<script>
// 封装队列
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();
// 面试题:击鼓传花
// 参数:玩家名单,淘汰的数字
function passGame(nameList,num) {
// 1. 创建一个队列结构
var queue = new Queue();
// 2. 将所有人都依次加入到队列中
for (var i = 0; i < nameList.length; i++){
queue.enqueue(nameList[i]);
};
// 3. 开始数数字
// 不是num的时候,重新加入嗷队列的末尾
// 是num这个数字的时候,将其从队列中删除
while (queue.size() > 1){
// 3.1 num数字之前的人重新放入到队列末尾
for (var i = 0; i < num-1; i++){
queue.enqueue(queue.dequeue());
};
// 3.2 num对应这个人,直接从队列删除
queue.dequeue();
};
// 4. 获取剩下的那个人
alert(queue.size());
var endName = queue.front();
alert("最终剩下的人:"+endName);
return nameList.indexOf(endName);
}
// 测试击鼓传花
names = ['诸葛大力','张伟','胡一菲','曾小贤','关谷神奇'];
alert(passGame(names,3));
</script>
</body>
</html>
1.3.6 优先级队列
(1)特点
(2)考虑的问题
(3) 应用
(4)实现
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>优先级队列</title>
</head>
<body>
<script>
// 封装优先级队列
function PriorityQueue() {
// 在PriorityQueue中重新创建了一个类:可以理解为内部类
function QueueElement(element,priority) {
this.element = element;
this.priority = priority;
};
// 封装属性
this.items = [];
// 实现插入方法
PriorityQueue.prototype.enqueue = function (element,priority) {
// 1. 创建QueueElement对象
var queueElement = new QueueElement(element,priority);
// 2. 判断队列是否为空
if (this.items.length == 0){
this.items.push(queueElement);
}else{
var added = false;
for (var i = 0; i < this.items.length ; i++){
if(queueElement.priority<this.items[i].priority){
this.items.splice(i,0, queueElement);
added = true;
break;
};
};
if(!added){
this.items.push(queueElement);
}
}
};
// 2. 从队列中删除前端元素
PriorityQueue.prototype.dequeue = function () {
return this.items.shift(); // 删除数组中的第一个元素
};
// 3. 查看前端的元素
PriorityQueue.prototype.front = function () {
return this.items[0];
};
// 4. 查看队列是否为空
PriorityQueue.prototype.isEmpty = function () {
return this.items.length == 0;
};
// 5. 查看元素中的个数
PriorityQueue.prototype.size = function () {
return this.items.length;
}
// 6. toString
PriorityQueue.prototype.toString = function () {
var resultString = '';
for(var i = 0; i < this.items.length;i++){
resultString += this.items[i].element + "-" + this.items[i].priority + " ";
}
return resultString;
}
};
// 测试代码
var pq = new PriorityQueue();
pq.enqueue('abc',111);
pq.enqueue('bweqw',222);
pq.enqueue('dede',444);
pq.enqueue('csaq3ew',333);
alert(pq.toString());
</script>
</body>
</html>
1.4 链表
1.4.1 单向链表
(1)特点
(2) 优点
(3)缺点
(4)单向链表实现代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>单向链表</title>
</head>
<body>
<script>
// 封装链表类
function LinkedList(){
// 内部类:节点类
function Node(data) {
this.data = data;
this.next = null;
};
// 属性
this.head = null;
this.length = 0; // 记录链表的长度
// 1. 追加方法
LinkedList.prototype.append = function (data) {
// 1.1 创建新的节点
var newNode = new Node(data);
// 1.2 判断添加的是不是第一个节点
if(this.length == 0){ // 1.2.1 是第一个节点
this.head = newNode;
}else { // 1.2.2 不是第一个节点
// 找到最后一个节点
var current = this.head;
while (current.next){
current = current.next;
};
// 最后节点的next指向新的节点
current.next = newNode;
};
// 1.3 length+1
this.length += 1;
};
// 2. toString 方法
LinkedList.prototype.toString = function () {
// 2.1 定义变量
var current = this.head;
var listString = "";
// 2.2 循环获取变量
while(current){
listString += current.data + " ";
current = current.next;
}
return listString;
};
// 3. insert方法
LinkedList.prototype.insert = function (position,data) {
// 3.1 对position进行越界判断
if (position < 0 || position > this.length){
return false;
};
// 3.2 根据data创建newNode
var newNode = new Node(data);
// 3.3 判断插入的位置是否是第一个
if (position == 0){
newNode.next = this.head;
this.head = newNode;
}else{
var index = 0;
var current = this.head;
var previous = null;
while(index++ < position){
previous = current;
current = current.next;
}
newNode.next = current;
previous.next = newNode;
}
// 3.4 length+1
this.length += 1;
return true;
};
// 4. get方法
LinkedList.prototype.get = function (position) {
// 4.1 对position进行越界判断
if (position < 0 || position >= this.length){
return null;
};
// 4.2 获取对应的data
var current = this.head;
var index = 0;
while (index++ < position ){
current = current.next;
}
return current.data;
};
// 5. indexOf方法
LinkedList.prototype.indexOf = function (data) {
// 5.1 定义变量
var current = this.head;
var index = 0;
// 5.2 开始查找
while(current){
if(current.data == data){
return index;
}else{
index += 1;
current = current.next;
}
}
// 5.3 找到最后没有找到,返回-1
return -1;
};
// 6. update方法
LinkedList.prototype.update = function (position,newData) {
// 6.1 对position进行越界判断
if (position < 0 || position >= this.length){
return false;
};
// 6.2 查找正确的节点
var current = this.head;
var index = 0;
while(index++ < position){
current = current.next;
}
// 6.3 将postion位置的node的data,修改成newData
current.data = newData;
return true;
};
// 7. removeAt方法
LinkedList.prototype.removeAt = function (position) {
//7.1 对position进行越界判断
if (position < 0 || position >= this.length){
return false;
};
// 7.2 判断删除的是否是第一个节点
if (position == 0){
this.head = this.head.next;
}else{
var index = 0;
var current = this.head;
var previous = null;
while (index++ < position){
previous = current;
current = current.next;
};
previous.next = current.next;
};
this.length -= 1;
};
// 8. remove方法
LinkedList.prototype.remove = function (data) {
// 8.1 获取data在列表中的位置
var position = this.indexOf(data);
// 8.2 根据位置信息,删除节点
this.removeAt(position);
};
// 9. isEmpty方法
LinkedList.prototype.isEmpty = function () {
return this.length == 0;
};
// 10. size方法
LinkedList.prototype.size = function () {
return this.length;
};
};
// 测试代码
// 1. 创建LinkedList
var list = new LinkedList();
// 2. 测试append方法
list.append('abc');
list.append('bsad');
list.append('csad');
//alert(list)
// 3. 测试insert方法
list.insert(0,'zzzs');
list.insert(3,'dqewq');
list.insert(5,'edasd');
//alert(list);
// 4. 测试insert方法
// alert(list.get(2));
//alert(list.get(1));
// 5. 测试indexOf方法
// alert(list.indexOf('zzzs'));
// alert(list.indexOf('zzzasdsas'));
// 6. 测试update方法
// alert(list);
list.update(2,'xiaozhan')
alert(list);
// 7. 测试removeAt方法
// list.removeAt(1);
// alert(list);
// 8. 测试remove方法
list.remove("xiaozhan");
alert(list);
list.remove("abc");
alert(list);
// 9. 测试isEmpty方法
alert(list.isEmpty());
// 10. 测试size方法
alert(list.size());
</script>
</body>
</html>
1.4.2 双向链表
(1)特点
![](https://img-blog.csdnimg.cn/20200210170047717.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzU0NTIyNQ==,size_16,color_FFFFFF,t_70
(2)优点
(3)缺点
(4)双向链表实现代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>双向链表</title>
</head>
<body>
<script>
// 封装双向链表
function DoublyLinkedList() {
// 属性
this.head = null;
this.tail = null;
this.length = 0;
// 内部类:节点类
function Node(data) {
this.data = data;
this.prev = null;
this.next = null;
};
// 常见的操作:方法
// 1. append方法
DoublyLinkedList.prototype.append = function (data) {
// 1. 根据data创建节点
var newNode = new Node(data);
// 2.判断添加的是否是第一个节点
if(this.length == 0){
this.head = newNode;
this.tail = newNode;
}else{
newNode.prev = this.tail;
this.tail.next = newNode;
this.tail = newNode;
};
// 3. length+1
this.length += 1;
};
// 2. 将链表转化成字符串的形式
// 2.1 toString方法
DoublyLinkedList.prototype.toString = function () {
return this.backwardString();
};
// 2.2 forwardString方法
DoublyLinkedList.prototype.forwardString = function () {
// 1. 定义变量
var current = this.tail;
var resultString = "";
// 2. 依次向前遍历,获取每一个节点
while(current){
resultString += current.data + " " ;
current = current.prev;
}
return resultString;
};
// 2.3 backString方法
DoublyLinkedList.prototype.backwardString = function () {
// 1. 定义变量
var current = this.head;
var resultString = "";
// 2. 依次向后遍历,获取每一个节点
while (current) {
resultString += current.data + " ";
current = current.next;
}
return resultString;
};
// 3. insert方法
DoublyLinkedList.prototype.insert = function (position,data) {
// 3.1 越界判断
if (position < 0 || position > this.length){
return false;
}
// 3.2 根据data创建新的节点
var newNode = new Node(data);
// 3.3 根据position判断原来的列表是否为空
if (this.length == 0){
this.head = newNode;
this.tail = newNode;
}else{
// 判断position是不是0
if(position == 0){
this.head.prev = newNode;
newNode.next = this.head;
this.head = newNode;
}else if(position == this.length){
this.append(data);
}else{
var current = this.head;
var index = 0;
while(index++<position){
current = current.next;
};
// 修改指针
newNode.next = current;
newNode.prev = current.prev;
current.prev.next = newNode;
current.prev = newNode;
};
};
// 3.4 length+1
this.length += 1;
return true;
};
// 4.get方法
DoublyLinkedList.prototype.get = function (position) {
// 4.1 越界判断
if (position < 0 || position >= this.length){
return null;
};
// 4.2 获取元素
// this.length / 2 > position: 从头向后遍历
if (this.length / 2 > position){
var current = this.head;
var index = 0;
while(index++ < position){
current = current.next;
}
}else{
// this.length / 2 < position: 从后向头遍历
var current = this.tail;
var index = this.length -1 ;
while(index-- > position){
current = current.prev;
}
}
return current.data;
};
// 5. indexOf方法
DoublyLinkedList.prototype.indexOf = function (data) {
// 5.1 定义变量
var current = this.head;
var index = 0;
// 5.2 查找和data相同的节点
while (current){
if(current.data == data){
return index;
}
index += 1;
current = current.next;
};
return -1;
};
// 6. update方法
DoublyLinkedList.prototype.update = function (position,data) {
// 6.1 越界判断
if (position < 0 || position >= this.length){
return false;
};
// 6.2 寻找正确的节点
var current = this.head;
var index = 0;
while (index++ < position){
current = current.next;
};
// 6.3 正确的节点的data
current.data = data;
return true;
};
// 7. removeAt方法
DoublyLinkedList.prototype.removeAt = function (position) {
// 7.1 越界判断
if (position < 0 || position >= this.length){
return null;
};
var current = this.head;
// 7.2 判断是否只有一个节点
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 ){// 判断是否删除的是最后一个节点
this.tail.prev.next = null;
this.tail = this.tail.prev;
}else{
var index = 0;
while (index++ < position){
current = current.next;
};
current.prev.next = current.next;
current.next.pre = current.prev;
};
};
this.length -= 1;
return current.data;
};
// 8.remove方法
DoublyLinkedList.prototype.remove = function (data) {
// 1. 根据data获取下标值
var index = this.indexOf(data);
// 2. 根据index删除对应位置的节点
return this.removeAt(index);
};
// 9. isEmpty方法
DoublyLinkedList.prototype.isEmpty = function () {
if(this.length == 0){
return true;
}else{
return false;
}
}
// 10. size方法
DoublyLinkedList.prototype.size = function () {
return this.length;
};
// 11. 获取链表的第一个元素
DoublyLinkedList.prototype.getFrist = function () {
return this.head.data;
};
// 12. 获取链表的最后个元素
DoublyLinkedList.prototype.getLast = function () {
return this.tail.data;
};
};
// 测试代码
var list = new DoublyLinkedList();
// 1. 测试append方法
list.append("abc");
list.append("def");
list.append("gre");
// 2. 测试转成字符串的方法
// alert(list);
// alert(list.backwardString());
// alert(list.forwardString());
// 3. 测试插入insert方法
list.insert(0,"swing");
list.insert(4,"bbbbb");
list.insert(2,"sad");
alert(list);
// 4. 测试get方法
// alert(list.get(0));
// alert(list.get(2));
// alert(list.get(5));
// alert(list.get(6));
// 5. 测试indexOf方法
// alert(list.indexOf("swing"));
// alert(list.indexOf("bbbb"));
// alert(list.indexOf("bbbbb"));
// 6. 测试update方法
list.update(0,"sssssss");
list.update(5,"zzzzzzz");
alert(list);
// 7. 测试removeAt方法
list.removeAt(0);
alert(list)
// 8.测试remove方法
list.remove("abc");
alert(list);
</script>
</body>
</html>
—————————————————————————————————————————
如果我的文章能帮你节约20秒,就请你为我的文章点个赞吧!