常用排序:
- 冒泡排序 : 复杂度 O(N^2) 真实交换次数: N(N-1)/4
- 选择排序 : 复杂度 O(N^2) 真实交换次数: N-1
- 插入排序 : 复杂度 O(N^2)
- 希尔排序(升级版 插入排序) : 最坏为 O(N^2) 通常好于 O(N^2)
- 快速排序 (升级版 冒泡排序) : 平均效率 O(N * logN)
// 创建列表类
function ArrayList() {
// 属性
this.array = [];
// 方法
// 1 插入数据
ArrayList.prototype.insert = function (valueItem) {
this.array.push(valueItem);
}
// 2 toString方法
ArrayList.prototype.toString = function () {
return this.array.join(' -> ');
}
// 交换数据
ArrayList.prototype.swap = function (m, n) {
let temp = this.array[m];
this.array[m] = this.array[n];
this.array[n] = temp;
}
// 3 冒泡排序 : 复杂度 O(N^2) 真实交换次数: N(N-1)/4
ArrayList.prototype.bubbleSort = function () {
// 获取数组的长度
let length = this.array.length;
// 两层循环,进行冒泡排序
for (let i = length-1; i >= 0 ; i--) {
// 此处的i为需要冒泡的长度
for (let j = 0; j < i; j++) {
// 判断是否需要互换
if (this.array[j] > this.array[j+1]) {
// 元素互换
this.swap(j, j+1)
}
}
}
}
// 4 选择排序 : 复杂度 O(N^2) 真实交换次数: N-1
ArrayList.prototype.selectSort = function () {
let length = this.array.length;
for (let i = 0; i < length - 1; i++) {
// 寻找当前最小值
let mid = i;
for (let j = i +1; j <length; j++) {
if (this.array[mid] > this.array[j]) {
mid = j;
}
}
// 最小值与起始位置进行交换
this.swap(i, mid);
}
}
// 5 插入排序 : 复杂度 O(N^2)
ArrayList.prototype.insertSort = function () {
let length = this.array.length;
for (let index = 1; index < length; index++) {
// 内存循环:获取i位置的元素,和前面的数据进行比较
let temp = this.array[index];
let i = index;
while (this.array[i-1] > temp && i>0) {
this.array[i] = this.array[i-1];
i--;
}
// 此处i已经做了i--,遍历到了最后需要更改地方
this.array[i] = temp;
}
}
// 7 希尔排序(升级版插入排序) : 最坏为 O(N^2) 通常好于 O(N^2)
ArrayList.prototype.shellSort = function () {
let length = this.array.length;
// 获取初始间距
let gap = Math.floor(length/2);
// 当间隔为0时,停止遍历
while (gap >= 1) {
for (let index = gap; index < length; index++) {
// 记录小分组中初始插入排序位置
let temp = this.array[index];
let i = index;
// 小分组中开始插入排序
while (temp < this.array[i - gap] && i >= gap) {
// this.swap(i, i-gap); // 并不高效
this.array[i] = this.array[i-gap];
i -= gap;
}
this.array[i] = temp;
}
gap = Math.floor(gap/2);
}
}
// 8 快速排序 (升级版 冒泡排序) : 平均效率 O(N * logN)
// 1 选择枢纽
ArrayList.prototype.median = function (left, right) {
// 1 取出中间值
let center = Math.floor((left + right)/2);
// 三数排序
if (this.array[left] > this.array[center]) {
this.swap(left, center)
}
if (this.array[center] > this.array[right]) {
this.swap(center, right)
}
if (this.array[left] > this.array[center]) {
this.swap(left, center)
}
// 将中间值与倒数第二个进行互换
this.swap(center, right-1)
// 此时倒数第二个为中间枢纽,将枢纽返回
return this.array[right-1]
}
// 2 递归遍历
ArrayList.prototype.quickItem = function (left, right) {
// 1 结束条件 左指针大于右指针或者重合,则结束遍历
if (left >= right) return
// 2 获取枢纽
let pivot = this.median(left, right);
// 定义子指针变量
let i = left;
let j = right - 1; //倒数第二个,故减一
// 开始互换
while (true) {
// 左子指针开始移动
while (this.array[++i] < pivot) { }
// 右子指针开始移动
while (this.array[--j] > pivot) { }
// 在数组内则互换,若是指针重合或是指针错位,则直接将指针较大的数(或是重合)与枢纽互换(此时较大为i).
if (i < j) {
// 符合条件则互换
this.swap(i, j);
} else {
// 退出遍历,将枢纽与较大值互换
break
}
}
// 枢纽互换,即将枢纽放回正确的位置
this.swap(i, right -1);
// 分而治之,开始递归
this.quickItem(left, i-1); // 左半部分
this.quickItem(i + 1, right) //右半部分
}
// 封装快速遍历
ArrayList.prototype.quickSort = function () {
this.quickItem(0, this.array.length - 1)
}
}
// 测试
let list = new ArrayList();
let value = [99, 2, 3, 4, 35, 12, 44, 23, 33, 44, 7, 54, 99, 3, 35];
// 插入元素
for (let index = 0; index < value.length; index++) {
list.insert(value[index]);
}
console.log("初始化列表:" + list.toString());
// list.bubbleSort();
// console.log("冒泡排序: " + list.toString());
// list.selectSort();
// console.log("选择排序: " + list.toString());
// list.insertSort();
// console.log("插入排序: " + list.toString());
// list.shellSort();
// console.log("希尔排序: " + list.toString());
list.quickSort();
console.log("快速排序: " + list.toString());