哈希表: hash 算法 + 数组/链表
解决 hash 冲突:
1 链地址法
2 开放地址法(空白元素偏移)
(线性探测、二次方探测、再哈希法)
hash 算法提升计算速度:霍纳法则/秦九韶算法
数组元素使其分布均匀:常量使用质数
function hashFunc(str, size) {
let hashcode = 0;
// 使用霍纳法则,37为一个较大的质数常量
str.split('').forEach(char => hashcode = 37 * hashcode + charCodeAt(char));
return hashcode % size;
}
排序:
function Sort(arr) {
this.arr = arr;
this.arrLength = arr.length;
}
// 希尔排序
/*
arr = [41, 36, 39, 25, 59, 47, 28, 35, 16],
length = 9
*/
Sort.prototype.shellSort = function() {
// 偏移量
var gap = Math.floor(this.arrLength / 2);
for(; gap > 0; gap = Math.floor(gap / 2)) {
var index = gap;
for(; index < this.arrLength; index += gap) {
var temp = this.arr[index];
var subIndex = index;
for(; subIndex - gap >= 0 && this.arr[subIndex - gap] > temp; subIndex -= gap ) {
this.arr[subIndex] = this.arr[subIndex - gap];
}
this.arr[subIndex] = temp;
}
}
}
// 快速排序
Sort.prototype.quickSort = function(initBeginIdx, initEndIdx) {
// console.log("quickSort", {initBeginIdx, initEndIdx});
var beginIdx = initBeginIdx || 0;
var endIdx = initEndIdx || (this.arrLength - 1);
// 设置中间元素 = arr[0], arr[Math.floor(len / 2)], arr[len -1] 的中位数
let midPosition = Math.floor((beginIdx + endIdx) / 2);
if(midPosition === 0) {
return this.arr;
}
var midElement = this.arr[midPosition];
if(this.arr[0] > this.arr[midPosition]) {
midElement = this.arr[0];
this.arr[0] = this.arr[midPosition];
}
if(this.arr[this.arrLength - 1] < this.arr[midPosition]) {
midElement = this.arr[this.arrLength - 1];
this.arr[this.arrLength - 1] = this.arr[midPosition];
}
this.arr[midPosition] = midElement;
// this.toString();
// 小于 中间数字 的元素放在 左边,
// 大于 中间数字 的元素放在 右边
while(beginIdx <= endIdx) {
while(this.arr[beginIdx] < midElement) {
beginIdx += 1;
}
if(beginIdx >= endIdx) {
break;
}
this.arr[midPosition] = this.arr[beginIdx];
midPosition = beginIdx;
// console.log("midPosition", midPosition);
while(this.arr[endIdx] > midElement) {
endIdx -= 1;
}
if(beginIdx >= endIdx) {
break;
}
this.arr[midPosition] = this.arr[endIdx];
midPosition = endIdx;
// console.log("midPosition", midPosition);
}
this.arr[midPosition] = midElement;
// 对前后数组分而治之,进行递归
if((midPosition - 1) > (initBeginIdx || 0)) {
this.quickSort(initBeginIdx || 0, midPosition - 1);
}
if((initEndIdx || (this.arrLength - 1)) > (midPosition + 1)) {
this.quickSort(midPosition + 1, initEndIdx || (this.arrLength - 1));
}
}
Sort.prototype.toString = function() {
console.log("arr", this.arr.join(','));
}
var arr = [22,21,63,1,12,54,3,23];
var sort = new Sort(arr);
sort.shellSort();
sort.toString();
// 堆排序
function Sort(arr) {
this.arr = arr;
this.arrLength = arr.length;
}
Sort.prototype.swap = function(firstIdx, secondIdx) {
var temp = this.arr[firstIdx];
this.arr[firstIdx] = this.arr[secondIdx];
this.arr[secondIdx] = temp;
}
Sort.prototype.heapify = function (parentIdx) {
var arrMaxIdx = this.arr.length - 1; // 最大下标
var lChildIdx = parentIdx * 2 + 1; // 左子节点坐标
var rChildIdx = parentIdx * 2 + 2; // 右子节点坐标
var maxIdx = parentIdx; // 默认父节点值最大
if(lChildIdx <= arrMaxIdx && this.arr[lChildIdx] > this.arr[maxIdx]) {
maxIdx = lChildIdx;
}
if(rChildIdx <= arrMaxIdx && this.arr[rChildIdx] > this.arr[maxIdx]) {
maxIdx = rChildIdx;
}
if(maxIdx !== parentIdx) {
// 交换数据
this.swap(parentIdx, maxIdx);
// 回调 heapify 交换后的子节点
this.heapify(maxIdx);
}
}
Sort.prototype.arrayHeapify = function() {
var arrMaxIdx = this.arr.length - 1; // 最大下标
let startIdx = Math.floor((arrMaxIdx - 1) / 2); // 找到完全二叉树的最后一个非子节点
for(var i = startIdx; i >= 0; i--) {
this.heapify(i);
// sort.toString();
}
}
// 堆排序
Sort.prototype.heapSort = function() {
let resultArr = [];
while(this.arr.length > 0) {
this.arrayHeapify();
// 将坐标为0(最大值)和最后一个元素值交换
this.swap(0, this.arr.length - 1);
resultArr.unshift(this.arr.pop());
}
this.arr = resultArr;
}
Sort.prototype.toString = function() {
console.log("arr", this.arr.join(','));
}
var arr = [22,21,63,1,12,54,3,23];
var sort = new Sort(arr);
sort.heapSort();
sort.toString();
排序比较:
排序算法 | 平均时间复杂度 | 最好情况 | 最差情形 | 稳定度 | 空间复杂度 | 排序方式 | 备注 |
冒泡排序 | O(n^2) | O(n) | O(n^2) | Y | O(1) | 交换 | n值小 时较好 |
插入排序 | O(n^2) | O(n) | O(n^2) | Y | O(1) | 插入 | 大部分有序 时较好 |
选择排序 | O(n^2) | O(n^2) | O(n^2) | N | O(1) | 选择 | n值小 时较好 |
希尔排序 | O(n^(3/2)) | O(nlog2^n) | O(n^2) | N | O(1) | 插入 | n值中等大小 时较好 |
快速排序 | O(nlog2^n) | O(nlog2^n) | O(n^2) | N | O(log2^n) | 交换 | n值大 时较好,有序时较差 |
堆排序 | O(nlog2^n) | O(nlog2^n) | O(nlog2^n) | N | O(1) | 选择 | n值中等大小 时较好 |
归并排序 | O(nlog2^n) | O(nlog2^n) | O(nlog2^n) | Y | O(n) | 归并 | n特别大 需要硬件存储 |
计数排序 | O(n + k) | O(n + k) | O(n + k) | Y | O(k) | n特别大 需要硬件存储 | |
桶排序 | O(n + k) | O(n + k) | O(n^2) | Y | O(n + k) | n特别大 需要硬件存储 | |
基数排序 | O(n * k) | O(n * k) | O(n * k) | Y | O(n + k) | n特别大 需要硬件存储 | |