冒泡排序
思路:
- 比较相邻元素,如果第一个比第二个大,就交换顺序;
- 一轮之后,可以保证最后一个就是最大的;
- n-1轮之后,就可以完成排序;
实现:
Array.prototype.bubbleSort = function(){
for(let i = 0; i this[j+1]){
swap(this,j,j+1);
}
}
console.log(this);
}
}
//交换数组的两项
function swap(arr, i, j) {
const temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
const arr = [5,3,4,2,1];
arr.bubbleSort();
时间复杂度:O(n^2) 两层for循环
空间复杂度:O(1)
选择排序
思路:
- 找到数组中最小的值,选中它并将其放置在第一位;
- 接着找到第二小的值,选中它并将其放置在第二位;
- 以此类推,执行n-1轮;
实现:
Array.prototype.selectionSort = function () {
for(let i = 0; i < this.length; i++) {
let indexMin = i;
for(let j = i; j < this.length; j++) {
if(this[j] < this[indexMin]){
indexMin = j;
}
}
if(indexMin !== i) {
swap(this, i, indexMin);
}
console.log(this);
}
}
//交换数组的两项
function swap(arr, i, j) {
const temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
const arr = [5,3,4,2,1];
arr.selectionSort();
时间复杂度:O(n^2) 两层for循环
空间复杂度:O(1)
插入排序
思路:
- 从第二个数开始往前比;
- 比它大就往后排,然后把它放入到合适的位置;
- 以此类推进行到最后一项;
实现:
Array.prototype.insertionSort = function () {
for(let i = 1; i< this.length; i++) {
const temp = this[i];
let j = i;
while(j > 0){
if(this[j-1] > temp){
this[j] = this[j-1];
}else{
break;
}
j--;
}
this[j] = temp;
// console.log(this);
}
}
const arr = [5,3,4,2,1];
arr.insertionSort();
时间复杂度:O(n^2) 两层循环
空间复杂度: O(1)
归并排序
思路:
-
分:把数组分成两半,再递归地对子数组进行“分”操作,直到分成一个个单独的数;
-
合:把两个数组合并成有序数组,再对有序数组进行合并,直到全部子数组合并为一个完整的数组;
-
合并两个有序数组:
-
新建一个空数组res,用于存放最终排序后的数组;
-
比较两个有序数组的头部,较小者出队并推入res中;
-
如果两个数组还有值,就重复第二步;
实现:
Array.prototype.mergeSort = function () {
const rec = (arr) => {
if(arr.length === 1) {
return arr;
}
const mid = Math.floor(arr.length / 2);
const left = arr.slice(0, mid);
const right = arr.slice(mid, arr.length);
const orderLeft = rec(left);
const orderRight = rec(right);
const res = [];
while(orderLeft.length || orderRight.length){
if(orderLeft.length && orderRight.length) {
res.push(orderLeft[0] < orderRight[0] ? orderLeft.shift() : orderRight.shift());
}else if(orderLeft.length) {
res.push(orderLeft.shift());
}else if(orderRight.length) {
res.push(orderRight.shift());
}
console.log(res)
}
return res;
}
const res = rec(this);
//res数组拷贝到this数组
res.forEach((n,i) => this[i] = n);
}
const arr = [5,3,4,2,1];
arr.mergeSort();
I
时间复杂度:O(nlogn) (分的时间复杂度为O(logn) 合的时间复杂度为O(n))
空间复杂度:O(n) (归并的空间复杂度就是那个临时的数组和递归时压入栈的数据占用的空间:n + logn;所以空间复杂度为: O(n))
快速排序
思路:
- 分区:从数组中任意选择一个“基准”,所有比基准小的元素放在基准前面,比基准大的元素放在基准的后面;
- 递归:递归的对基准前后的子数组进行分区;
实现:
Array.prototype.quickSort = function () {
const rec = (arr) => {
if(arr.length <= 1) {
return arr;
}
const left = [];
const right = [];
const mid = arr[0];
for(let i = 1; i < arr.length; i++) {
if(arr[i] < mid) {
left.push(arr[i]);
}else {
right.push(arr[i]);
}
}
// console.log(left,mid,right)
return [...rec(left), mid, ...rec(right)];
}
const res = rec(this);
//res数组拷贝到this数组
res.forEach((n,i) => this[i] = n);
}
const arr = [5,3,4,2,1];
arr.quickSort();
// console.log(arr);
时间复杂度:O(nlogn) (递归的时间复杂度O(logn) 分区的时间复杂度为O(n))
空间复杂度:O(logn) (递归调用栈的空间)
时间复杂度说明
上文说的复杂度均为平均复杂度,下图包含最坏最好情况的时间复杂度
时间复杂度曲线:
(2^k = n则 k = logn)
二分搜索
二分搜索是在有序数组中进行查找目标值
思路:
- 从数组的中间元素开始,如果中间元素正好是目标,则搜索结束;
- 如果目标值大于或小于中间元素,则在大于或小于中间元素的那一半数组中搜索;
实现:
Array.prototype.binarySort = function (item) {
let low = 0;
let high = this.length - 1;
while(low <= high) {
const mid = Math.floor((low + high) / 2);
const ele = this[mid];
if(item > this[mid]) {
low = mid + 1;
}else if(item < this[mid]) {
high = mid - 1;
}else {
return mid;
}
}
return -1;
}
const res = [1,2,3,4,5].binarySort(3);
时间复杂度:O(logn)