【从蛋壳到满天飞】JS 数据结构解析和算法实现,所有文章大概的内容以下: Arrays(数组)、Stacks(栈)、Queues(队列)、LinkedList(链表)、Recursion(递归思想)、BinarySearchTree(二分搜索树)、Set(集合)、Map(映射)、Heap(堆)、PriorityQueue(优先队列)、SegmentTree(线段树)、Trie(字典树)、UnionFind(并查集)、AVLTree(AVL 平衡树)、RedBlackTree(红黑平衡树)、HashTable(哈希表)html
源代码有三个:ES6(单个单个的 class 类型的 js 文件) | JS + HTML(一个 js 配合一个 html)| JAVA (一个一个的工程)node
所有源代码已上传 github,点击我吧,光看文章可以掌握两成,动手敲代码、动脑思考、画图才能够掌握八成。git
本文章适合 对数据结构想了解而且感兴趣的人群,文章风格一如既往如此,就以为手机上看起来比较方便,这样显得比较有条理,整理这些笔记加源码,时间跨度也算将近半年时间了,但愿对想学习数据结构的人或者正在学习数据结构的人群有帮助。github
class Node {
e; //Element
next; //Node
}
复制代码
O(1)
的复杂度取出这个元素,数组面试
scores[2]
链表算法
对比数组
要清楚何时使用数组这样的静态数据结构,数据结构
简单的代码示例MyLinkedList
ide
class MyLinkedListNode {
constructor(element = null, next = null) {
this.element = element;
this.next = next;
}
// toString 2018-10-20-jwl
toString() {
return this.element.toString();
}
}
class MyLinkedList {
constructor() {}
}
复制代码
MyLinkedList
中node.next = head
,head = node
,node.next = prev.next
,prev.next = node
,node.next = prev.next
和prev.next = node
prev.next = node
在前,node.next = prev.next
在后,这样一来逻辑就不成立了,(class: MyLinkedList)
MyLinkedList函数
class MyLinkedListNode {
constructor(element = null, next = null) {
this.element = element;
this.next = next;
}
// toString 2018-10-20-jwl
toString() {
return this.element.toString();
}
}
class MyLinkedList {
constructor() {
this.head = null;
this.size = 0;
}
// 获取链表中实际的节点个数
getSise() {
return this.size;
}
// 判断链表是否为空
isEmpty() {
return this.size === 0;
}
// 在链表头添加节点
addFirst(element) {
let node = new MyLinkedListNode(element, null);
node.next = this.head;
this.head = node;
this.size++;
}
// 在链表指定索引处插入节点
insert(index, element) {
if (index < 0 || index > this.size) {
throw new Error('add error. index < 0 or index > size');
}
if (index === 0) {
this.addFirst(element);
} else {
// 第一个prev就是head
let prev = this.head;
// 变量i(索引)之因此要从 1 开始,由于索引为0的那个节点就是head,循环就不须要从0开始了,
// 小于index是由于要找到指定索引位置的前一个节点
// 循环是由于 要继续找到指定索引处的节点的前一个节点
for (var i = 1; i < index; i++) {
// 不停的切换引用,直到找到对应索引处节点的下一个节点
prev = prev.next;
}
let node = new MyLinkedListNode(element, null);
node.next = prev.next;
prev.next = node;
this.size++;
}
}
// 扩展:在链表最后一个节点的位置添加节点
addLast(element) {
this.insert(this.size, element);
}
}
复制代码
node.next = prev.next
和prev.next = node
了,node.next = head
和head = node
,node.next = dummyHead.next
、dummyHead.next = node
,node.next = head.next;head = node;
。node.next = dummyHead.next; dummyHead.next = node;
,dummyHead.next
才是链表中第一个实际记录的节点,(class: MyLinkedList)
MyLinkedList
class MyLinkedListNode {
constructor(element = null, next = null) {
this.element = element;
this.next = next;
}
// toString 2018-10-20-jwl
toString() {
return this.element.toString();
}
}
class MyLinkedList {
constructor() {
this.dummyHead = new MyLinkedListNode(null, null);
this.size = 0;
}
// 获取链表中实际的节点个数
getSise() {
return this.size;
}
// 判断链表是否为空
isEmpty() {
return this.size === 0;
}
// 在链表头添加节点
addFirst(element) {
// let node = new MyLinkedListNode(element, null);
// node.next = this.head;
// this.head = node;
// this.size ++;
// 改用虚拟头节点
insert(0, element);
}
// 在链表指定索引处插入节点
insert(index, element) {
if (index < 0 || index > this.size) {
throw new Error('add error. index < 0 or index > size');
}
// 第一个prev就是dummyHead
let prev = this.dummyHead;
// 以前变量i(索引)之因此要从 1 开始,由于索引为0的那个节点就是head,循环就不须要从0开始了,
// 如今索引之因此要从 0 开始, 由于初始化时 多增长了一个虚拟的头节点
// (由于这个索引为0的节点并非dummyHead,dummyHead这个节点并不记录为链表中的实际节点),
// 小于index是由于要找到指定索引位置的前一个节点
// 循环是由于 要继续找到指定索引处的节点的前一个节点
for (var i = 0; i < index; i++) {
// 不停的切换引用,直到找到对应索引处节点的下一个节点
prev = prev.next;
}
let node = new MyLinkedListNode(element, null);
node.next = prev.next;
prev.next = node;
this.size++;
}
// 扩展:在链表最后一个节点的位置添加节点
addLast(element) {
this.insert(this.size, element);
}
}
复制代码
dummyHead
开始遍历,dummyHead.next
开始遍历。(class: MyLinkedList, class: Main)
MyLinkedList
class MyLinkedListNode {
constructor(element = null, next = null) {
this.element = element;
this.next = next;
}
// toString 2018-10-20-jwl
toString() {
return this.element.toString();
}
}
class MyLinkedList {
constructor() {
this.dummyHead = new MyLinkedListNode(null, null);
this.size = 0;
}
// 获取链表中实际的节点个数
getSize() {
return this.size;
}
// 判断链表是否为空
isEmpty() {
return this.size === 0;
}
// 在链表头添加节点
addFirst(element) {
// let node = new MyLinkedListNode(element, null);
// node.next = this.head;
// this.head = node;
// this.size ++;
// 改用虚拟头节点
this.insert(0, element);
}
// 在链表指定索引处插入节点
insert(index, element) {
if (index < 0 || index > this.size) {
throw new Error('add error. index < 0 or index > size');
}
// 第一个prev就是dummyHead
let prev = this.dummyHead;
// 以前变量i(索引)之因此要从 1 开始,由于索引为0的那个节点就是head,循环就不须要从0开始了,
// 如今索引之因此要从 0 开始, 由于初始化时 多增长了一个虚拟的头节点
// (由于这个索引为0的节点并非dummyHead,dummyHead这个节点并不记录为链表中的实际节点),
// 小于index是由于要找到指定索引位置的前一个节点
// 循环是由于 要继续找到指定索引处的节点的前一个节点
for (var i = 0; i < index; i++) {
// 不停的切换引用,直到找到对应索引处节点的下一个节点
prev = prev.next;
}
let node = new MyLinkedListNode(element, null);
node.next = prev.next;
prev.next = node;
this.size++;
}
// 扩展:在链表最后一个节点的位置添加节点
addLast(element) {
this.insert(this.size, element);
}
// 获取指定索引位置的元素
get(index) {
// 判断索引合法性
if (index < 0 || index >= this.size) {
throw new Error('get error. index < 0 or index >= size');
}
// 若是你要找指定索引节点的前一个节点 就使用dummyHead
// 若是你要找到指定索引节点 就使用dummyHead.next
// 由于duumyHead并非第一个节点,由于它是一个虚拟节点,
// dummyHead.next才是真正被记录的第一个节点。
let node = this.dummyHead.next;
for (var i = 0; i < index; i++) {
node = node.next;
}
return node.element;
}
// 获取头节点的元素
getFirst() {
return this.get(0);
}
// 获取尾节点的元素
getLast() {
return this.get(this.size - 1);
}
// 设置指定索引位置的元素值
set(index, element) {
// 判断索引合法性
if (index < 0 || index >= this.size) {
throw new Error('get error. index < 0 or index >= size');
}
// 从第一个真正被记录的节点开始,从0开始
let node = this.dummyHead.next;
// 索引为 0 时,实际上切换到的节点 它的索引为 1
// i < index ,当索引为 index-1 时, 实际上切换到的节点 它的索引为index
for (let i = 0; i < index; i++) {
// 每一次切换 都只是改变引用
// 不的在链表中找下一个节点
node = node.next;
}
node.element = element;
}
// 全部节点中是否有包含该元素
contains(element) {
let node = this.dummyHead;
while (node.next !== null) {
if (node.next.element === element) return true;
// 不停的向下切换
node = node.next;
}
return false;
}
// 输出链表中的信息
// @Override toString 2018-10-21-jwl
toString() {
let arrInfo = `LinkedList: size = ${this.size},\n`;
arrInfo += `data = front [`;
let node = this.dummyHead.next;
while (node.next !== null) {
arrInfo += `${node.element}->`;
node = node.next;
}
arrInfo += 'NULL] tail';
// 在页面上展现
document.body.innerHTML += `${arrInfo}<br /><br /> `;
return arrInfo;
}
}
复制代码
Main
class Main {
constructor () {
/this.alterLine("MyLinkedList Area");
let mylinkedList = new MyLinkedList();
for (let i = 1; i <= 5 ; i++) {
mylinkedList.addFirst(i);
console.log(mylinkedList.toString());
}
mylinkedList.insert(2, 88888);
console.log(mylinkedList.toString());
}
// 将内容显示在页面上
show (content) {
document.body.innerHTML += `${content}<br /><br />`;
}
// 展现分割线
alterLine (title) {
let line = `--------------------${title}----------------------`
console.log(line);
document.body.innerHTML += `${line}<br /><br />`;
}
}
复制代码
prev.next = delNode.next
,delNode.next = null
就完成了删除,delNode = delNode.next
,(class: MyLinkedList, class: Main)
MyLinkedList
class MyLinkedListNode {
constructor(element = null, next = null) {
this.element = element;
this.next = next;
}
// toString 2018-10-20-jwl
toString() {
return this.element.toString();
}
}
class MyLinkedList {
constructor() {
this.dummyHead = new MyLinkedListNode(null, null);
this.size = 0;
}
// 获取链表中实际的节点个数
getSize() {
return this.size;
}
// 判断链表是否为空
isEmpty() {
return this.size === 0;
}
// 在链表头添加节点
addFirst(element) {
// let node = new MyLinkedListNode(element, null);
// node.next = this.head;
// this.head = node;
// this.size ++;
// 改用虚拟头节点
this.insert(0, element);
}
// 在链表指定索引处插入节点
insert(index, element) {
if (index < 0 || index > this.size) {
throw new Error('add error. index < 0 or index > size');
}
// 第一个prev就是dummyHead
let prev = this.dummyHead;
// 以前变量i(索引)之因此要从 1 开始,由于索引为0的那个节点就是head,循环就不须要从0开始了,
// 如今索引之因此要从 0 开始, 由于初始化时 多增长了一个虚拟的头节点
// (由于这个索引为0的节点并非dummyHead,dummyHead这个节点并不记录为链表中的实际节点),
// 小于index是由于要找到指定索引位置的前一个节点
// 循环是由于 要继续找到指定索引处的节点的前一个节点
for (var i = 0; i < index; i++) {
// 不停的切换引用,直到找到对应索引处节点的下一个节点
prev = prev.next;
}
let node = new MyLinkedListNode(element, null);
node.next = prev.next;
prev.next = node;
this.size++;
}
// 扩展:在链表最后一个节点的位置添加节点
addLast(element) {
this.insert(this.size, element);
}
// 获取指定索引位置的元素
get(index) {
// 判断索引合法性
if (index < 0 || index >= this.size) {
throw new Error('get error. index < 0 or index >= size');
}
// 若是你要找指定索引节点的前一个节点 就使用dummyHead
// 若是你要找到指定索引节点 就使用dummyHead.next
// 由于duumyHead并非第一个节点,由于它是一个虚拟节点,
// dummyHead.next才是真正被记录的第一个节点。
let node = this.dummyHead.next;
for (var i = 0; i < index; i++) {
node = node.next;
}
return node.element;
}
// 获取头节点的元素
getFirst() {
return this.get(0);
}
// 获取尾节点的元素
getLast() {
return this.get(this.size - 1);
}
// 设置指定索引位置的元素值
set(index, element) {
// 判断索引合法性
if (index < 0 || index >= this.size) {
throw new Error('get error. index < 0 or index >= size');
}
// 从第一个真正被记录的节点开始,从0开始
let node = this.dummyHead.next;
// 索引为 0 时,实际上切换到的节点 它的索引为 1
// i < index ,当索引为 index-1 时, 实际上切换到的节点 它的索引为index
for (let i = 0; i < index; i++) {
// 每一次切换 都只是改变引用
// 不的在链表中找下一个节点
node = node.next;
}
node.element = element;
}
// 全部节点中是否有包含该元素
contains(element) {
let node = this.dummyHead;
while (node.next !== null) {
if (node.next.element === element) return true;
// 不停的向下切换
node = node.next;
}
return false;
}
// 删除指定索引位置的节点
remove(index) {
// 验证索引的合法性
if (index < 0 || index >= this.size) {
throw new Error('remove error. index < 0 or index > this.size');
}
let node = this.dummyHead;
for (let i = 0; i < index; i++) {
node = node.next;
}
// 待删除的节点
let delNode = node.next;
// 给待删除那个节点的前一个的节点的next引用替换为
// 但删除的这个节点的next
node.next = delNode.next;
// 或者这样也行
// node.next = node.next.next;
// 临时存储待删除的那个节点里的元素
let element = delNode.element;
// 清空 待删除的节点
delNode = null;
this.size--;
return element;
}
// 扩展:移除链表头的元素
removeFirst() {
return this.remove(0);
}
// 扩展:移除链表尾部的元素
removeLast() {
return this.remove(this.size - 1);
}
// 输出链表中的信息
// @Override toString 2018-10-21-jwl
toString() {
let arrInfo = `LinkedList: size = ${this.size},\n`;
arrInfo += `data = front [`;
let node = this.dummyHead.next;
while (node.next !== null) {
arrInfo += `${node.element}->`;
node = node.next;
}
arrInfo += 'NULL] tail';
// 在页面上展现
document.body.innerHTML += `${arrInfo}<br /><br /> `;
return arrInfo;
}
}
复制代码
Main
class Main {
constructor() {
this.alterLine('MyLinkedList Area');
let mylinkedList = new MyLinkedList();
for (let i = 1; i <= 5; i++) {
mylinkedList.addFirst(i);
console.log(mylinkedList.toString());
}
mylinkedList.insert(2, 88888);
console.log(mylinkedList.toString());
mylinkedList.remove(2);
console.log(mylinkedList.toString());
mylinkedList.removeFirst();
console.log(mylinkedList.toString());
mylinkedList.removeLast();
console.log(mylinkedList.toString());
}
// 将内容显示在页面上
show(content) {
document.body.innerHTML += `${content}<br /><br />`;
}
// 展现分割线
alterLine(title) {
let line = `--------------------${title}----------------------`;
console.log(line);
document.body.innerHTML += `${line}<br /><br />`;
}
}
复制代码
O(n)
:在只对链表头进行操做时为O(1)
O(n)
:在只对链表头进行操做时为O(1)
O(n)
O(n)
:只查链表头的元素时为O(1)
### 添加操做 O(n)
addLast(e)
:O(n)
addFirst(e)
:O(1)
insert(index, e)
:O(n/2) = O(n)
removeLast()
:O(n)
removeFirst()
:O(1)
remove(index)
:O(n/2) = O(n)
set(index, e)
:O(n)
get(index)
:O(n)
contains(e)
:O(n)
find(e)
:O(n)
O(n)
,O(1)
O(n)
,O(1)
O(n)
,O(1)
MyLinkedListStack
的接口。
void push(E e)
:添加一个元素E pop()
:移除一个元素E peek()
:查看栈顶的元素int getSize()
:获取栈中实际的元素个数boolean isEmpty()
:判断栈是否为空(class: MyLinkedList,class: MyLinkedListStack, class: Main)
MyLinkedList
class MyLinkedListNode {
constructor(element = null, next = null) {
this.element = element;
this.next = next;
}
// toString 2018-10-20-jwl
toString() {
return this.element.toString();
}
}
class MyLinkedList {
constructor() {
this.dummyHead = new MyLinkedListNode(null, null);
this.size = 0;
}
// 获取链表中实际的节点个数
getSize() {
return this.size;
}
// 判断链表是否为空
isEmpty() {
return this.size === 0;
}
// 在链表头添加节点
addFirst(element) {
// let node = new MyLinkedListNode(element, null);
// node.next = this.head;
// this.head = node;
// this.size ++;
// 改用虚拟头节点
this.insert(0, element);
}
// 在链表指定索引处插入节点
insert(index, element) {
if (index < 0 || index > this.size) {
throw new Error('add error. index < 0 or index > size');
}
// 第一个prev就是dummyHead
let prev = this.dummyHead;
// 以前变量i(索引)之因此要从 1 开始,由于索引为0的那个节点就是head,循环就不须要从0开始了,
// 如今索引之因此要从 0 开始, 由于初始化时 多增长了一个虚拟的头节点
// (由于这个索引为0的节点并非dummyHead,dummyHead这个节点并不记录为链表中的实际节点),
// 小于index是由于要找到指定索引位置的前一个节点
// 循环是由于 要继续找到指定索引处的节点的前一个节点
for (var i = 0; i < index; i++) {
// 不停的切换引用,直到找到对应索引处节点的下一个节点
prev = prev.next;
}
let node = new MyLinkedListNode(element, null);
node.next = prev.next;
prev.next = node;
this.size++;
}
// 扩展:在链表最后一个节点的位置添加节点
addLast(element) {
this.insert(this.size, element);
}
// 获取指定索引位置的元素
get(index) {
// 判断索引合法性
if (index < 0 || index >= this.size) {
throw new Error('get error. index < 0 or index >= size');
}
// 若是你要找指定索引节点的前一个节点 就使用dummyHead
// 若是你要找到指定索引节点 就使用dummyHead.next
// 由于duumyHead并非第一个节点,由于它是一个虚拟节点,
// dummyHead.next才是真正被记录的第一个节点。
let node = this.dummyHead.next;
for (var i = 0; i < index; i++) {
node = node.next;
}
return node.element;
}
// 获取头节点的元素
getFirst() {
return this.get(0);
}
// 获取尾节点的元素
getLast() {
return this.get(this.size - 1);
}
// 设置指定索引位置的元素值
set(index, element) {
// 判断索引合法性
if (index < 0 || index >= this.size) {
throw new Error('get error. index < 0 or index >= size');
}
// 从第一个真正被记录的节点开始,从0开始
let node = this.dummyHead.next;
// 索引为 0 时,实际上切换到的节点 它的索引为 1
// i < index ,当索引为 index-1 时, 实际上切换到的节点 它的索引为index
for (let i = 0; i < index; i++) {
// 每一次切换 都只是改变引用
// 不的在链表中找下一个节点
node = node.next;
}
node.element = element;
}
// 全部节点中是否有包含该元素
contains(element) {
let node = this.dummyHead;
while (node.next !== null) {
if (node.next.element === element) return true;
// 不停的向下切换
node = node.next;
}
return false;
}
// 删除指定索引位置的节点
remove(index) {
// 验证索引的合法性
if (index < 0 || index >= this.size) {
throw new Error('remove error. index < 0 or index > this.size');
}
let node = this.dummyHead;
for (let i = 0; i < index; i++) {
node = node.next;
}
// 待删除的节点
let delNode = node.next;
// 给待删除那个节点的前一个的节点的next引用替换为
// 但删除的这个节点的next
node.next = delNode.next;
// 或者这样也行
// node.next = node.next.next;
// 临时存储待删除的那个节点里的元素
let element = delNode.element;
// 清空 待删除的节点
delNode = null;
this.size--;
return element;
}
// 扩展:移除链表头的元素
removeFirst() {
return this.remove(0);
}
// 扩展:移除链表尾部的元素
removeLast() {
return this.remove(this.size - 1);
}
// 输出链表中的信息
// @Override toString 2018-10-21-jwl
toString() {
let arrInfo = `LinkedList: size = ${this.size},\n`;
arrInfo += `data = front [`;
let node = this.dummyHead.next;
while (node.next !== null) {
arrInfo += `${node.element}->`;
node = node.next;
}
arrInfo += 'NULL] tail';
// 在页面上展现
document.body.innerHTML += `${arrInfo}<br /><br /> `;
return arrInfo;
}
}
复制代码
MyLinkedListStack
class MyLinkedListStack {
constructor() {
this.myLinkedList = new MyLinkedList();
}
// 入栈
push(element) {
this.myLinkedList.addFirst(element);
}
// 出栈
pop() {
return this.myLinkedList.removeFirst();
}
// 查看栈顶元素
peek() {
return this.myLinkedList.getFirst();
}
// 查看栈中实际元素的个数
getSize() {
return this.myLinkedList.getSize();
}
// 判断栈是否为空
isEmpty() {
return this.myLinkedList.isEmpty();
}
// 输出栈中信息
// @Override toString 2018-10-21-jwl
toString() {
let arrInfo = `LinkedListStack: size = ${this.getSize()},\n`;
arrInfo += `data = stack top [`;
let node = this.myLinkedList.dummyHead.next;
for (var i = 1; i < this.getSize(); i++) {
arrInfo += `${node.element},`;
node = node.next;
}
if (!this.isEmpty()) {
arrInfo += `${node.element}`;
}
arrInfo += ']';
// 在页面上展现
document.body.innerHTML += `${arrInfo}<br /><br /> `;
return arrInfo;
}
}
复制代码
Main
class Main {
constructor() {
this.alterLine('MyLinkedListStack Area');
let myLinkedListStack = new MyLinkedListStack();
for (let i = 1; i <= 5; i++) {
myLinkedListStack.push(i);
console.log(myLinkedListStack.toString());
}
console.log(myLinkedListStack.peek());
this.show(myLinkedListStack.peek());
for (let i = 0; i < 5; i++) {
console.log(myLinkedListStack.toString());
myLinkedListStack.pop();
}
}
// 将内容显示在页面上
show(content) {
document.body.innerHTML += `${content}<br /><br />`;
}
// 展现分割线
alterLine(title) {
let line = `--------------------${title}----------------------`;
console.log(line);
document.body.innerHTML += `${line}<br /><br />`;
}
}
window.onload = function() {
// 执行主函数
new Main();
};
复制代码
O(1)
级别的,(class: MyLinkedList, class: MyLinkedListStack, class: MyArray, class: MyStack, class: Main)
MyLinkedList
class MyLinkedListNode {
constructor(element = null, next = null) {
this.element = element;
this.next = next;
}
// toString 2018-10-20-jwl
toString() {
return this.element.toString();
}
}
class MyLinkedList {
constructor() {
this.dummyHead = new MyLinkedListNode(null, null);
this.size = 0;
}
// 获取链表中实际的节点个数
getSize() {
return this.size;
}
// 判断链表是否为空
isEmpty() {
return this.size === 0;
}
// 在链表头添加节点
addFirst(element) {
// let node = new MyLinkedListNode(element, null);
// node.next = this.head;
// this.head = node;
// this.size ++;
// 改用虚拟头节点
this.insert(0, element);
}
// 在链表指定索引处插入节点
insert(index, element) {
if (index < 0 || index > this.size) {
throw new Error('add error. index < 0 or index > size');
}
// 第一个prev就是dummyHead
let prev = this.dummyHead;
// 以前变量i(索引)之因此要从 1 开始,由于索引为0的那个节点就是head,循环就不须要从0开始了,
// 如今索引之因此要从 0 开始, 由于初始化时 多增长了一个虚拟的头节点
// (由于这个索引为0的节点并非dummyHead,dummyHead这个节点并不记录为链表中的实际节点),
// 小于index是由于要找到指定索引位置的前一个节点
// 循环是由于 要继续找到指定索引处的节点的前一个节点
for (var i = 0; i < index; i++) {
// 不停的切换引用,直到找到对应索引处节点的下一个节点
prev = prev.next;
}
let node = new MyLinkedListNode(element, null);
node.next = prev.next;
prev.next = node;
this.size++;
}
// 扩展:在链表最后一个节点的位置添加节点
addLast(element) {
this.insert(this.size, element);
}
// 获取指定索引位置的元素
get(index) {
// 判断索引合法性
if (index < 0 || index >= this.size) {
throw new Error('get error. index < 0 or index >= size');
}
// 若是你要找指定索引节点的前一个节点 就使用dummyHead
// 若是你要找到指定索引节点 就使用dummyHead.next
// 由于duumyHead并非第一个节点,由于它是一个虚拟节点,
// dummyHead.next才是真正被记录的第一个节点。
let node = this.dummyHead.next;
for (var i = 0; i < index; i++) {
node = node.next;
}
return node.element;
}
// 获取头节点的元素
getFirst() {
return this.get(0);
}
// 获取尾节点的元素
getLast() {
return this.get(this.size - 1);
}
// 设置指定索引位置的元素值
set(index, element) {
// 判断索引合法性
if (index < 0 || index >= this.size) {
throw new Error('get error. index < 0 or index >= size');
}
// 从第一个真正被记录的节点开始,从0开始
let node = this.dummyHead.next;
// 索引为 0 时,实际上切换到的节点 它的索引为 1
// i < index ,当索引为 index-1 时, 实际上切换到的节点 它的索引为index
for (let i = 0; i < index; i++) {
// 每一次切换 都只是改变引用
// 不的在链表中找下一个节点
node = node.next;
}
node.element = element;
}
// 全部节点中是否有包含该元素
contains(element) {
let node = this.dummyHead;
while (node.next !== null) {
if (node.next.element === element) return true;
// 不停的向下切换
node = node.next;
}
return false;
}
// 删除指定索引位置的节点
remove(index) {
// 验证索引的合法性
if (index < 0 || index >= this.size) {
throw new Error('remove error. index < 0 or index > this.size');
}
let node = this.dummyHead;
for (let i = 0; i < index; i++) {
node = node.next;
}
// 待删除的节点
let delNode = node.next;
// 给待删除那个节点的前一个的节点的next引用替换为
// 但删除的这个节点的next
node.next = delNode.next;
// 或者这样也行
// node.next = node.next.next;
// 临时存储待删除的那个节点里的元素
let element = delNode.element;
// 清空 待删除的节点
delNode = null;
this.size--;
return element;
}
// 扩展:移除链表头的元素
removeFirst() {
return this.remove(0);
}
// 扩展:移除链表尾部的元素
removeLast() {
return this.remove(this.size - 1);
}
// 输出链表中的信息
// @Override toString 2018-10-21-jwl
toString() {
let arrInfo = `LinkedList: size = ${this.size},\n`;
arrInfo += `data = front [`;
let node = this.dummyHead.next;
while (node.next !== null) {
arrInfo += `${node.element}->`;
node = node.next;
}
arrInfo += 'NULL] tail';
// 在页面上展现
document.body.innerHTML += `${arrInfo}<br /><br /> `;
return arrInfo;
}
}
复制代码
MyLinkedListStack
class MyLinkedListStack {
constructor() {
this.myLinkedList = new MyLinkedList();
}
// 入栈
push(element) {
this.myLinkedList.addFirst(element);
}
// 出栈
pop() {
return this.myLinkedList.removeFirst();
}
// 查看栈顶元素
peek() {
return this.myLinkedList.getFirst();
}
// 查看栈中实际元素的个数
getSize() {
return this.myLinkedList.getSize();
}
// 判断栈是否为空
isEmpty() {
return this.myLinkedList.isEmpty();
}
// 输出栈中信息
// @Override toString 2018-10-21-jwl
toString() {
let arrInfo = `LinkedListStack: size = ${this.getSize()},\n`;
arrInfo += `data = stack top [`;
let node = this.myLinkedList.dummyHead.next;
for (var i = 1; i < this.getSize(); i++) {
arrInfo += `${node.element},`;
node = node.next;
}
if (!this.isEmpty()) {
arrInfo += `${node.element}`;
}
arrInfo += ']';
// 在页面上展现
document.body.innerHTML += `${arrInfo}<br /><br /> `;
return arrInfo;
}
}
复制代码
MyArray
class MyArray {
// 构造函数,传入数组的容量capacity构造Array 默认数组的容量capacity=10
constructor(capacity = 10) {
this.data = new Array(capacity);
this.size = 0;
}
// 获取数组中的元素实际个数
getSize() {
return this.size;
}
// 获取数组的容量
getCapacity() {
return this.data.length;
}
// 判断数组是否为空
isEmpty() {
return this.size === 0;
}
// 给数组扩容
resize(capacity) {
let newArray = new Array(capacity);
for (var i = 0; i < this.size; i++) {
newArray[i] = this.data[i];
}
// let index = this.size - 1;
// while (index > -1) {
// newArray[index] = this.data[index];
// index --;
// }
this.data = newArray;
}
// 在指定索引处插入元素
insert(index, element) {
// 先判断数组是否已满
if (this.size == this.getCapacity()) {
// throw new Error("add error. Array is full.");
this.resize(this.size * 2);
}
// 而后判断索引是否符合要求
if (index < 0 || index > this.size) {
throw new Error(
'insert error. require index < 0 or index > size.'
);
}
// 最后 将指定索引处腾出来
// 从指定索引处开始,全部数组元素所有日后移动一位
// 从后往前移动
for (let i = this.size - 1; i >= index; i--) {
this.data[i + 1] = this.data[i];
}
// 在指定索引处插入元素
this.data[index] = element;
// 维护一下size
this.size++;
}
// 扩展 在数组最前面插入一个元素
unshift(element) {
this.insert(0, element);
}
// 扩展 在数组最后面插入一个元素
push(element) {
this.insert(this.size, element);
}
// 其实在数组中添加元素 就至关于在数组最后面插入一个元素
add(element) {
if (this.size == this.getCapacity()) {
// throw new Error("add error. Array is full.");
this.resize(this.size * 2);
}
// size其实指向的是 当前数组最后一个元素的 后一个位置的索引。
this.data[this.size] = element;
// 维护size
this.size++;
}
// get
get(index) {
// 不能访问没有存放元素的位置
if (index < 0 || index >= this.size) {
throw new Error('get error. index < 0 or index >= size.');
}
return this.data[index];
}
// 扩展: 获取数组中第一个元素
getFirst() {
return this.get(0);
}
// 扩展: 获取数组中最后一个元素
getLast() {
return this.get(this.size - 1);
}
// set
set(index, newElement) {
// 不能修改没有存放元素的位置
if (index < 0 || index >= this.size) {
throw new Error('set error. index < 0 or index >= size.');
}
this.data[index] = newElement;
}
// contain
contain(element) {
for (var i = 0; i < this.size; i++) {
if (this.data[i] === element) {
return true;
}
}
return false;
}
// find
find(element) {
for (var i = 0; i < this.size; i++) {
if (this.data[i] === element) {
return i;
}
}
return -1;
}
// findAll
findAll(element) {
// 建立一个自定义数组来存取这些 元素的索引
let myarray = new MyArray(this.size);
for (var i = 0; i < this.size; i++) {
if (this.data[i] === element) {
myarray.push(i);
}
}
// 返回这个自定义数组
return myarray;
}
// 删除指定索引处的元素
remove(index) {
// 索引合法性验证
if (index < 0 || index >= this.size) {
throw new Error('remove error. index < 0 or index >= size.');
}
// 暂存即将要被删除的元素
let element = this.data[index];
// 后面的元素覆盖前面的元素
for (let i = index; i < this.size - 1; i++) {
this.data[i] = this.data[i + 1];
}
this.size--;
this.data[this.size] = null;
// 若是size 为容量的四分之一时 就能够缩容了
// 防止复杂度震荡
if (Math.floor(this.getCapacity() / 4) === this.size) {
// 缩容一半
this.resize(Math.floor(this.getCapacity() / 2));
}
return element;
}
// 扩展:删除数组中第一个元素
shift() {
return this.remove(0);
}
// 扩展: 删除数组中最后一个元素
pop() {
return this.remove(this.size - 1);
}
// 扩展: 根据元素来进行删除
removeElement(element) {
let index = this.find(element);
if (index !== -1) {
this.remove(index);
}
}
// 扩展: 根据元素来删除全部元素
removeAllElement(element) {
let index = this.find(element);
while (index != -1) {
this.remove(index);
index = this.find(element);
}
// let indexArray = this.findAll(element);
// let cur, index = 0;
// for (var i = 0; i < indexArray.getSize(); i++) {
// // 每删除一个元素 原数组中就少一个元素,
// // 索引数组中的索引值是按照大小顺序排列的,
// // 因此 这个cur记录的是 原数组元素索引的偏移量
// // 只有这样才可以正确的删除元素。
// index = indexArray.get(i) - cur++;
// this.remove(index);
// }
}
// @Override toString 2018-10-17-jwl
toString() {
let arrInfo = `Array: size = ${this.getSize()},capacity = ${this.getCapacity()},\n`;
arrInfo += `data = [`;
for (var i = 0; i < this.size - 1; i++) {
arrInfo += `${this.data[i]}, `;
}
if (!this.isEmpty()) {
arrInfo += `${this.data[this.size - 1]}`;
}
arrInfo += `]`;
// 在页面上展现
document.body.innerHTML += `${arrInfo}<br /><br /> `;
return arrInfo;
}
}
复制代码
MyStack
class MyStack {
constructor(capacity = 10) {
this.myArray = new MyArray(capacity);
}
// 入栈
push(element) {
this.myArray.push(element);
}
// 出栈
pop() {
return this.myArray.pop();
}
// 查看栈顶的元素
peek() {
return this.myArray.getLast();
}
// 栈中实际元素的个数
getSize() {
return this.myArray.getSize();
}
// 栈是否为空
isEmpty() {
return this.myArray.isEmpty();
}
// 查看栈的容量
getCapacity() {
return this.myArray.getCapacity();
}
// @Override toString 2018-10-20-jwl
toString() {
let arrInfo = `Stack: size = ${this.getSize()},capacity = ${this.getCapacity()},\n`;
arrInfo += `data = [`;
for (var i = 0; i < this.myArray.size - 1; i++) {
arrInfo += `${this.myArray.data[i]}, `;
}
if (!this.isEmpty()) {
arrInfo += `${this.myArray.data[this.myArray.size - 1]}`;
}
arrInfo += `] stack top is right!`;
// 在页面上展现
document.body.innerHTML += `${arrInfo}<br /><br /> `;
return arrInfo;
}
}
复制代码
Main
class Main {
constructor() {
this.alterLine('Stacks Comparison Area');
let myStack = new MyStack();
let myLinkedListStack = new MyLinkedListStack();
let performanceTest = new PerformanceTest();
let myStackInfo = performanceTest.testStack(myStack, 100000);
let myLinkedListStackInfo = performanceTest.testStack(
myLinkedListStack,
100000
);
this.alterLine('MyStack Area');
console.log(myStackInfo);
this.show(myStackInfo);
this.alterLine('MyLinkedListStack Area');
console.log(myLinkedListStackInfo);
this.show(myLinkedListStackInfo);
}
// 将内容显示在页面上
show(content) {
document.body.innerHTML += `${content}<br /><br />`;
}
// 展现分割线
alterLine(title) {
let line = `--------------------${title}----------------------`;
console.log(line);
document.body.innerHTML += `${line}<br /><br />`;
}
}
class Student {
constructor(studentName, studentScore) {
this.name = studentName;
this.score = studentScore;
}
toString() {
let studentInfo = `Student(name: ${this.name}, score: ${this.score})`;
return studentInfo;
}
}
window.onload = function() {
// 执行主函数
new Main();
};
复制代码
O(n)
,O(1)
,O(n)
O(n)
,O(1)
,O(n)
O(n)
,O(1)
,O(n)
O(n)
级别,O(1)
去删除尾部的节点,( class: MyArray, class: MyQueue, class: MyLoopQueue, class: MyLinkedListQueue, class: Main)
MyArray
class MyArray {
// 构造函数,传入数组的容量capacity构造Array 默认数组的容量capacity=10
constructor(capacity = 10) {
this.data = new Array(capacity);
this.size = 0;
}
// 获取数组中的元素实际个数
getSize() {
return this.size;
}
// 获取数组的容量
getCapacity() {
return this.data.length;
}
// 判断数组是否为空
isEmpty() {
return this.size === 0;
}
// 给数组扩容
resize(capacity) {
let newArray = new Array(capacity);
for (var i = 0; i < this.size; i++) {
newArray[i] = this.data[i];
}
// let index = this.size - 1;
// while (index > -1) {
// newArray[index] = this.data[index];
// index --;
// }
this.data = newArray;
}
// 在指定索引处插入元素
insert(index, element) {
// 先判断数组是否已满
if (this.size == this.getCapacity()) {
// throw new Error("add error. Array is full.");
this.resize(this.size * 2);
}
// 而后判断索引是否符合要求
if (index < 0 || index > this.size) {
throw new Error(
'insert error. require index < 0 or index > size.'
);
}
// 最后 将指定索引处腾出来
// 从指定索引处开始,全部数组元素所有日后移动一位
// 从后往前移动
for (let i = this.size - 1; i >= index; i--) {
this.data[i + 1] = this.data[i];
}
// 在指定索引处插入元素
this.data[index] = element;
// 维护一下size
this.size++;
}
// 扩展 在数组最前面插入一个元素
unshift(element) {
this.insert(0, element);
}
// 扩展 在数组最后面插入一个元素
push(element) {
this.insert(this.size, element);
}
// 其实在数组中添加元素 就至关于在数组最后面插入一个元素
add(element) {
if (this.size == this.getCapacity()) {
// throw new Error("add error. Array is full.");
this.resize(this.size * 2);
}
// size其实指向的是 当前数组最后一个元素的 后一个位置的索引。
this.data[this.size] = element;
// 维护size
this.size++;
}
// get
get(index) {
// 不能访问没有存放元素的位置
if (index < 0 || index >= this.size) {
throw new Error('get error. index < 0 or index >= size.');
}
return this.data[index];
}
// 扩展: 获取数组中第一个元素
getFirst() {
return this.get(0);
}
// 扩展: 获取数组中最后一个元素
getLast() {
return this.get(this.size - 1);
}
// set
set(index, newElement) {
// 不能修改没有存放元素的位置
if (index < 0 || index >= this.size) {
throw new Error('set error. index < 0 or index >= size.');
}
this.data[index] = newElement;
}
// contain
contain(element) {
for (var i = 0; i < this.size; i++) {
if (this.data[i] === element) {
return true;
}
}
return false;
}
// find
find(element) {
for (var i = 0; i < this.size; i++) {
if (this.data[i] === element) {
return i;
}
}
return -1;
}
// findAll
findAll(element) {
// 建立一个自定义数组来存取这些 元素的索引
let myarray = new MyArray(this.size);
for (var i = 0; i < this.size; i++) {
if (this.data[i] === element) {
myarray.push(i);
}
}
// 返回这个自定义数组
return myarray;
}
// 删除指定索引处的元素
remove(index) {
// 索引合法性验证
if (index < 0 || index >= this.size) {
throw new Error('remove error. index < 0 or index >= size.');
}
// 暂存即将要被删除的元素
let element = this.data[index];
// 后面的元素覆盖前面的元素
for (let i = index; i < this.size - 1; i++) {
this.data[i] = this.data[i + 1];
}
this.size--;
this.data[this.size] = null;
// 若是size 为容量的四分之一时 就能够缩容了
// 防止复杂度震荡
if (Math.floor(this.getCapacity() / 4) === this.size) {
// 缩容一半
this.resize(Math.floor(this.getCapacity() / 2));
}
return element;
}
// 扩展:删除数组中第一个元素
shift() {
return this.remove(0);
}
// 扩展: 删除数组中最后一个元素
pop() {
return this.remove(this.size - 1);
}
// 扩展: 根据元素来进行删除
removeElement(element) {
let index = this.find(element);
if (index !== -1) {
this.remove(index);
}
}
// 扩展: 根据元素来删除全部元素
removeAllElement(element) {
let index = this.find(element);
while (index != -1) {
this.remove(index);
index = this.find(element);
}
// let indexArray = this.findAll(element);
// let cur, index = 0;
// for (var i = 0; i < indexArray.getSize(); i++) {
// // 每删除一个元素 原数组中就少一个元素,
// // 索引数组中的索引值是按照大小顺序排列的,
// // 因此 这个cur记录的是 原数组元素索引的偏移量
// // 只有这样才可以正确的删除元素。
// index = indexArray.get(i) - cur++;
// this.remove(index);
// }
}
// @Override toString 2018-10-17-jwl
toString() {
let arrInfo = `Array: size = ${this.getSize()},capacity = ${this.getCapacity()},\n`;
arrInfo += `data = [`;
for (var i = 0; i < this.size - 1; i++) {
arrInfo += `${this.data[i]}, `;
}
if (!this.isEmpty()) {
arrInfo += `${this.data[this.size - 1]}`;
}
arrInfo += `]`;
// 在页面上展现
document.body.innerHTML += `${arrInfo}<br /><br /> `;
return arrInfo;
}
}
复制代码
MyQueue
class MyQueue {
constructor(capacity = 10) {
this.myArray = new MyArray(capacity);
}
// 入队
enqueue(element) {
this.myArray.push(element);
}
// 出队
dequeue() {
return this.myArray.shift();
}
// 查看队首的元素
getFront() {
return this.myArray.getFirst();
}
// 查看队列中实际元素的个数
getSize() {
return this.myArray.getSize();
}
// 查看 队列当前的容量
getCapacity() {
return this.myArray.getCapacity();
}
// 查看队列是否为空
isEmpty() {
return this.myArray.isEmpty();
}
// 输出队列中的信息
// @Override toString 2018-10-20-jwl
toString() {
let arrInfo = `Queue: size = ${this.getSize()},capacity = ${this.getCapacity()},\n`;
arrInfo += `data = front [`;
for (var i = 0; i < this.myArray.size - 1; i++) {
arrInfo += `${this.myArray.data[i]}, `;
}
if (!this.isEmpty()) {
arrInfo += `${this.myArray.data[this.myArray.size - 1]}`;
}
arrInfo += `] tail`;
// 在页面上展现
document.body.innerHTML += `${arrInfo}<br /><br /> `;
return arrInfo;
}
}
复制代码
MyLoopQueue
class MyLoopQueue {
constructor(capacity = 10) {
// 初始化新数组
this.data = new Array(capacity);
// 初始化 队首、队尾的值 (索引)
this.front = this.tail = 0;
// 队列中实际元素个数
this.size = 0;
}
// 扩容
resize(capacity) {
let newArray = new Array(capacity);
let index = 0;
for (let i = 0; i < this.size; i++) {
// 索引可能会越界,因而就要取余一下,
// 若是越界了,就从队首开始
index = (this.front + i) % this.getCapacity();
newArray[i] = this.data[index];
}
this.data = newArray;
this.front = 0;
this.tail = this.size;
}
// 入队
enqueue(element) {
// 判断队列中是否已满
if ((this.tail + 1) % this.getCapacity() === this.front) {
this.resize(this.getCapacity() * 2);
}
this.data[this.tail] = element;
this.tail = (this.tail + 1) % this.getCapacity();
this.size++;
}
// 出队
dequeue() {
// 判断队列是否为空
if (this.isEmpty()) {
throw new Error("can't dequeue from an empty queue.");
}
let element = this.data[this.front];
this.data[this.front] = null;
this.front = (this.front + 1) % this.getCapacity();
this.size--;
// 当size 为容量的四分之一时就缩容一倍
if (this.size === Math.floor(this.getCapacity() / 4)) {
this.resize(Math.floor(this.getCapacity() * 2));
}
return element;
}
// 查看队首的元素
getFront() {
if (this.isEmpty()) {
throw new Error('queue is empty.');
}
return this.data[front];
}
// 查看实际的元素个数
getSize() {
return this.size;
}
// 查看容量
getCapacity() {
return this.data.length;
}
// 队列是否为空
isEmpty() {
// return this.size === 0;
return this.front == this.tail;
}
// 输出循环队列中的信息
// @Override toString 2018-10-20-jwl
toString() {
let arrInfo = `LoopQueue: size = ${this.getSize()},capacity = ${this.getCapacity()},\n`;
arrInfo += `data = front [`;
for (var i = 0; i < this.myArray.size - 1; i++) {
arrInfo += `${this.myArray.data[i]}, `;
}
if (!this.isEmpty()) {
arrInfo += `${this.myArray.data[this.myArray.size - 1]}`;
}
arrInfo += `] tail`;
// 在页面上展现
document.body.innerHTML += `${arrInfo}<br /><br /> `;
return arrInfo;
}
}
复制代码
MyLinkedListQueue
class MyLinkedListQueue {
constructor() {
this.front = this.tail = null;
this.size = 0;
}
// 入队
enqueue(element) {
// 判断队尾是否为空
if (this.tail === null) {
// 第一个节点 便是尾也是头
this.tail = new MyLinkedListNode(element, null);
this.front = this.tail;
} else {
let node = new MyLinkedListNode(element, null);
this.tail.next = node;
this.tail = node;
}
this.size++;
}
// 出队
dequeue() {
// 判断队首是否为空
if (this.front === null) {
throw new Error('front is empty.');
}
let delNode = this.front;
let element = delNode.element;
this.front = this.front.next;
delNode = null;
if (this.front === null)
// 若是头为空了,那么尾部也为空
this.tail = null;
this.size--;
return element;
}
// 查看队首的元素
getFront() {
// 判断队首是否为空
if (this.front === null) {
throw new Error('front is empty.');
}
return this.front.element;
}
// 查看队列中实际元素的个数
getSize() {
return this.size;
}
// 判断队列是否为空
isEmpty() {
return this.size === 0;
}
// 输出队列中的信息
// @Override toString 2018-10-21-jwl
toString() {
let arrInfo = `LinkedListQueue: size = ${this.getSize()},\n`;
arrInfo += `data = front [`;
let node = this.front;
for (var i = 1; i < this.getSize(); i++) {
arrInfo += `${node.element},`;
node = node.next;
}
if (!this.isEmpty()) {
arrInfo += `${node.element}`;
}
arrInfo += '] tail';
// 在页面上展现
document.body.innerHTML += `${arrInfo}<br /><br /> `;
return arrInfo;
}
}
复制代码
Main
class Main {
constructor() {
this.alterLine('MyLinkedListQueue Area');
let myLinkedListQueue = new MyLinkedListQueue();
for (let i = 1; i <= 5; i++) {
myLinkedListQueue.enqueue(i);
console.log(myLinkedListQueue.toString());
}
console.log(myLinkedListQueue.getFront());
this.show(myLinkedListQueue.getFront());
for (let i = 0; i < 5; i++) {
console.log(myLinkedListQueue.toString());
myLinkedListQueue.dequeue();
}
}
// 将内容显示在页面上
show(content) {
document.body.innerHTML += `${content}<br /><br />`;
}
// 展现分割线
alterLine(title) {
let line = `--------------------${title}----------------------`;
console.log(line);
document.body.innerHTML += `${line}<br /><br />`;
}
}
class Student {
constructor(studentName, studentScore) {
this.name = studentName;
this.score = studentScore;
}
toString() {
let studentInfo = `Student(name: ${this.name}, score: ${this.score})`;
return studentInfo;
}
}
window.onload = function() {
// 执行主函数
new Main();
};
复制代码