【从蛋壳到满天飞】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 (一个一个的工程)git
所有源代码已上传 github,点击我吧,光看文章可以掌握两成,动手敲代码、动脑思考、画图才能够掌握八成。github
本文章适合 对数据结构想了解而且感兴趣的人群,文章风格一如既往如此,就以为手机上看起来比较方便,这样显得比较有条理,整理这些笔记加源码,时间跨度也算将近半年时间了,但愿对想学习数据结构的人或者正在学习数据结构的人群有帮助。算法
MyQueue
void enqueue(e)
E dequeue()
E getFront()
int getSize()
boolean isEmpty()
MyPriorityQueue
堆
来进行实现。O(1)
级别的操做O(n)
级别的操做,O(n)
级别的操做,O(1)
级别的操做,O(n)
级别的操做了,O(n)
的复杂度,O(1)
的复杂度,O(n)
的复杂度。O(n)
级别的操做,O(logn)
这种级别的操做,O(logn)
的时间复杂度,O(logn)
的时间复杂度,O(logn)
这样的时间复杂度
O(nlog(n))
这个级别的,堆这种结构自己也是一棵树数组
满二叉树与彻底二叉树数据结构
教材中对于彻底二叉树的定义很是的拗口架构
二叉堆知足的性质dom
实现二叉堆必须知足的要求,ide
以数组的方式表现一棵彻底二叉树的规律函数
parent(i) = i / 2
,left child (i) = 2 * i
,right child (i) = 2 * i + 1
。彻底二叉树的好处
在不少教科书中实现堆的时候,
// 原来是这样的 空了数组中索引为0的位置
parent(i) = i / 2
left child (i) = 2 * i
right child (i) = 2 * i + 1
// 偏移以后是这样的 没空数组中索引为0的位置
parent(i) = (i - 1) / 2
left child (i) = 2 * i + 1
right child (i) = 2 * i + 2
复制代码
(class: Myarray, class: MaxHeap)
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;
}
}
复制代码
MaxHeap
// 自定义二叉堆之最大堆
class MyMaxHeap {
constructor(capacity = 10) {
this.myArray = new MyArray(capacity);
}
// 辅助函数 计算出堆中指定索引位置的元素其父节点的索引 -
calcParentIndex(index) {
if (index === 0)
// 索引为0是根节点,根节点没有父亲节点,小于0就更加不能够了
throw new Error("index is 0. doesn't have parent.");
return Math.floor((index - 1) / 2);
}
// 辅助函数 计算出堆中指定索引位置的元素其左孩子节点的索引 -
calcLeftChildIndex(index) {
return index * 2 + 1;
}
// 辅助函数 计算出堆中指定索引位置的元素其右孩子节点的索引 -
calcRightChildIndex(index) {
return index * 2 + 2;
}
// 获取堆中实际的元素个数
getSize() {
return this.myArray.getSize();
}
// 返回堆中元素是否为空的判断值
isEmpty() {
return this.myArray.isEmpty();
}
}
复制代码
(class: Myarray, class: MaxHeap)
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);
// }
}
// 新增: 交换两个索引位置的变量 2018-11-6
swap(indexA, indexB) {
if (
indexA < 0 ||
indexA >= this.size ||
indexB < 0 ||
indexB >= this.size
)
throw new Error('Index is Illegal.'); // 索引越界异常
let temp = this.data[indexA];
this.data[indexA] = this.data[indexB];
this.data[indexB] = temp;
}
// @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;
}
}
复制代码
MaxHeap
// 自定义二叉堆之最大堆
class MyMaxHeap {
constructor(capacity = 10) {
this.myArray = new MyArray(capacity);
}
// 添加操做
add(element) {
// 追加元素
this.myArray.push(element);
// 将追加的元素上浮到堆中合适的位置
this.siftUp(this.myArray.getSize() - 1);
}
// 堆的上浮操做 -
siftUp(index) {
// this.nonRecursiveSiftUp(index);
this.recursiveSiftUp(index);
// 不管是递归仍是非递归都有一个
// 元素上浮后结束的条件 当前节点元素值 小于其父节点元素值
// 和
// 索引即将越界的终止条件 要上浮的元素索引 小于等于0
}
// 堆的上浮操做 递归算法 -
recursiveSiftUp(index) {
// 解决最基本的问题, 递归终止条件
if (index <= 0) return;
let currentValue = this.myArray.get(index);
let parentIndex = this.calcParentIndex(index);
let parentValue = this.myArray.get(parentIndex);
// 递归写法
if (this.compare(currentValue, parentValue) > 0) {
this.swap(index, parentIndex);
this.recursiveSiftUp(parentIndex);
}
}
// 堆的上浮操做 非递归算法 -
nonRecursiveSiftUp(index) {
if (index <= 0) return;
let currentValue = this.myArray.get(index);
let parentIndex = this.calcParentIndex(index);
let parentValue = this.myArray.get(parentIndex);
while (this.compare(currentValue, parentValue) > 0) {
// 交换堆中两个元素位置的值
this.swap(index, parentIndex);
// 交换了位置以后,元素上浮后的索引变量也要进行相应的变动
index = parentIndex;
// 若是索引小于等于0了 那就结束循环
if (index <= 0) break;
currentValue = this.myArray.get(index);
parentIndex = this.calcParentIndex(index);
parentValue = this.myArray.get(parentIndex);
}
}
// 堆中两个元素的位置进行交换
swap(indexA, indexB) {
this.myArray.swap(indexA, indexB);
}
// 辅助函数 计算出堆中指定索引位置的元素其父节点的索引 -
calcParentIndex(index) {
if (index === 0)
// 索引为0是根节点,根节点没有父亲节点,小于0就更加不能够了
throw new Error("index is 0. doesn't have parent.");
return Math.floor((index - 1) / 2);
}
// 辅助函数 计算出堆中指定索引位置的元素其左孩子节点的索引 -
calcLeftChildIndex(index) {
return index * 2 + 1;
}
// 辅助函数 计算出堆中指定索引位置的元素其右孩子节点的索引 -
calcRightChildIndex(index) {
return index * 2 + 2;
}
// 比较的功能 -
compare(elementA, elementB) {
if (elementA === null || elementB === null)
throw new Error("element is error. element can't compare.");
if (elementA > elementB) return 1;
else if (elementA < elementB) return -1;
else return 0;
}
// 获取堆中实际的元素个数
getSize() {
return this.myArray.getSize();
}
// 返回堆中元素是否为空的判断值
isEmpty() {
return this.myArray.isEmpty();
}
}
复制代码
O(logn)
,O(logn)
的,(class: Myarray, class: MaxHeap, 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);
// }
}
// 新增: 交换两个索引位置的变量 2018-11-6
swap(indexA, indexB) {
if (
indexA < 0 ||
indexA >= this.size ||
indexB < 0 ||
indexB >= this.size
)
throw new Error('Index is Illegal.'); // 索引越界异常
let temp = this.data[indexA];
this.data[indexA] = this.data[indexB];
this.data[indexB] = temp;
}
// @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;
}
}
复制代码
MaxHeap
// 自定义二叉堆之最大堆
class MyMaxHeap {
constructor(capacity = 10) {
this.myArray = new MyArray(capacity);
}
// 添加操做
add(element) {
// 追加元素
this.myArray.push(element);
// 将追加的元素上浮到堆中合适的位置
this.siftUp(this.myArray.getSize() - 1);
}
// 堆的上浮操做 -
siftUp(index) {
// this.nonRecursiveSiftUp(index);
this.recursiveSiftUp(index);
// 不管是递归仍是非递归都有一个
// 元素上浮后结束的条件 当前节点元素值 小于其父节点元素值
// 和
// 索引即将越界的终止条件 要上浮的元素索引 小于等于0
}
// 堆的上浮操做 递归算法 -
recursiveSiftUp(index) {
// 解决最基本的问题, 递归终止条件
if (index <= 0) return;
let currentValue = this.myArray.get(index);
let parentIndex = this.calcParentIndex(index);
let parentValue = this.myArray.get(parentIndex);
// 递归写法
if (this.compare(currentValue, parentValue) > 0) {
this.swap(index, parentIndex);
this.recursiveSiftUp(parentIndex);
}
}
// 堆的上浮操做 非递归算法 -
nonRecursiveSiftUp(index) {
if (index <= 0) return;
let currentValue = this.myArray.get(index);
let parentIndex = this.calcParentIndex(index);
let parentValue = this.myArray.get(parentIndex);
while (this.compare(currentValue, parentValue) > 0) {
// 交换堆中两个元素位置的值
this.swap(index, parentIndex);
// 交换了位置以后,元素上浮后的索引变量也要进行相应的变动
index = parentIndex;
// 若是索引小于等于0了 那就结束循环
if (index <= 0) break;
currentValue = this.myArray.get(index);
parentIndex = this.calcParentIndex(index);
parentValue = this.myArray.get(parentIndex);
}
}
// 找到优先级最大的元素 (查找元素)操做
findMax() {
if (this.myArray.isEmpty())
throw new Error('can not findMax when heap is empty.');
return this.myArray.getFirst();
}
// 提取优先级最大的元素(删除元素)操做
extractMax() {
// 获取堆顶的元素
let maxElement = this.findMax();
// 获取堆底的元素
let element = this.myArray.getLast();
// 让堆底的元素替换掉堆顶的元素
this.myArray.set(0, element);
// 移除堆底的元素
this.myArray.pop();
// 让堆顶的元素开始下沉,从而可以正常知足堆的性质
this.siftDown(0);
// 返回堆顶的元素
return maxElement;
}
// 堆的下沉操做 -
siftDown(index) {
// this.nonRecursiveSiftDown(index);
this.recursiveSiftDown(index);
}
// 堆的下沉操做 递归算法 -
recursiveSiftDown(index) {
// 递归终止条件
// 若是当前索引位置的元素没有左孩子就说也没有右孩子,
// 那么能够直接终止,由于没法下沉
if (this.calcLeftChildIndex(index) >= this.myArray.getSize()) return;
const leftChildIndex = this.calcLeftChildIndex(index);
const leftChildValue = this.myArray.get(leftChildIndex);
const rightChildIndex = this.calcRightChildIndex(index);
let rightChildValue = null;
// let maxIndex = 0;
// if (rightChildIndex >= this.myArray.getSize())
// maxIndex = leftChildIndex;
// else {
// rightChildValue = this.myArray.get(rightChildIndex);
// if (this.compare(leftChildValue, rightChildValue) > 0)
// maxIndex = leftChildIndex;
// else
// maxIndex = rightChildIndex;
// }
// 这段代码是上面注释代码的优化
let maxIndex = leftChildIndex;
if (rightChildIndex < this.myArray.getSize()) {
rightChildValue = this.myArray.get(rightChildIndex);
if (this.compare(leftChildValue, rightChildValue) < 0)
maxIndex = rightChildIndex;
}
let maxValue = this.myArray.get(maxIndex);
let currentValue = this.myArray.get(index);
if (this.compare(maxValue, currentValue) > 0) {
// 交换位置
this.swap(maxIndex, index);
// 继续下沉
this.recursiveSiftDown(maxIndex);
}
}
// 堆的下沉操做 非递归算法 -
nonRecursiveSiftDown(index) {
// 该索引位置的元素有左右孩子节点才能够下沉,
// 在彻底二叉树中 若是一个节点没有左孩子必然没有右孩子
while (this.calcLeftChildIndex(index) < this.myArray.getSize()) {
let leftChildIndex = this.calcLeftChildIndex(index);
let leftChildValue = this.myArray.get(leftChildIndex);
let rightChildIndex = this.calcRightChildIndex(index);
let rightChildValue = null;
let maxIndex = leftChildIndex;
if (rightChildIndex < this.myArray.getSize()) {
rightChildValue = this.myArray.get(rightChildIndex);
if (this.compare(leftChildValue, rightChildValue) <= 0)
maxIndex = rightChildIndex;
}
let maxValue = this.myArray.get(maxIndex);
let currentValue = this.myArray.get(index);
if (this.compare(maxValue, currentValue) > 0) {
this.swap(maxIndex, index);
index = maxIndex;
continue;
} else break;
}
}
// 堆中两个元素的位置进行交换
swap(indexA, indexB) {
this.myArray.swap(indexA, indexB);
}
// 辅助函数 计算出堆中指定索引位置的元素其父节点的索引 -
calcParentIndex(index) {
if (index === 0)
// 索引为0是根节点,根节点没有父亲节点,小于0就更加不能够了
throw new Error("index is 0. doesn't have parent.");
return Math.floor((index - 1) / 2);
}
// 辅助函数 计算出堆中指定索引位置的元素其左孩子节点的索引 -
calcLeftChildIndex(index) {
return index * 2 + 1;
}
// 辅助函数 计算出堆中指定索引位置的元素其右孩子节点的索引 -
calcRightChildIndex(index) {
return index * 2 + 2;
}
// 比较的功能 -
compare(elementA, elementB) {
if (elementA === null || elementB === null)
throw new Error("element is error. element can't compare.");
if (elementA > elementB) return 1;
else if (elementA < elementB) return -1;
else return 0;
}
// 获取堆中实际的元素个数
size() {
return this.myArray.getSize();
}
// 返回堆中元素是否为空的判断值
isEmpty() {
return this.myArray.isEmpty();
}
}
复制代码
Main
// main 函数
class Main {
constructor() {
this.alterLine('MyMaxHeap Area');
const n = 100;
const maxHeap = new MyMaxHeap();
const random = Math.random;
// 循环添加随机数的值
for (let i = 0; i < n; i++) maxHeap.add(random() * n);
console.log('MaxHeap maxHeap size:' + maxHeap.size());
this.show('MaxHeap maxHeap size:' + maxHeap.size());
// 使用数组取值
let arr = [];
for (let i = 0; i < n; i++) arr[i] = maxHeap.extractMax();
console.log(
'Array arr size:' +
arr.length +
',MaxHeap maxHeap size:' +
maxHeap.size()
);
this.show(
'Array arr size:' +
arr.length +
',MaxHeap maxHeap size:' +
maxHeap.size()
);
console.log(arr, maxHeap);
// 检验一下是否符合要求
for (let i = 1; i < n; i++)
if (arr[i - 1] < arr[i]) throw new Error('error.');
console.log('test maxHeap completed.');
this.show('test maxHeap completed.');
}
// 将内容显示在页面上
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(logn)
级别的
logn
这个级别的关系,O(logn)
O(logn)