今天面试,我的数据结构真的烂,做一波小总结吧,哎,真难受啊~
面试中数据结构是真的很重要,不论是以后工作还是考研,哎,悔不当初
下面是对排序算法的一些总结,希望下次面试心里有个谱吧
//测试数据
let arr = [1, 2, 5, 1, 3, 6, 7, 1, 3, 4, 12, 41, 23, 2, 33, 23, 45, 0]
console.log(`原始数组:${arr}`)
/*
插入排序
1.直接插入排序
2.折半插入排序
3.希尔排序
*/
// 根据不同的查找方法来找到插入的位置把插入排序分为不同的类别
/**算法:直接插入排序
* 时间复杂度:最优O(n) 最差O(n^2)
* 空间复杂度:O(1)
* 稳定排序
*/
function insertSortDirect(arr) {
if (arr.length <= 1) {
return arr;
}
/**
* 整个过程,数组的前面部分一直是有序的状态
*/
for (let i = 1; i < arr.length; i++) {
// 对于每一个数,先将它存到一个变量中
let tmp = arr[i],
j = i - 1;
// 然后向左遍历
while (j >= 0 && arr[j] > tmp) {
//如果当前遍历到的数值比tmp值大,就将这个值移到后面的那个空
arr[j + 1] = arr[j];
j--;
}
//最后将停在j,j的位置就是arr[i]应该插入的位置
arr[j + 1] = tmp;
}
return arr;
}
/**
* 算法:折半插入排序
* 时间复杂度:O(n^2)
* 空间复杂度:O(1)
* 稳定排序
*/
function insertSortHalf(arr) {
if (arr.length <= 1) {
return arr;
}
/**同直接插入排序一样,它是直接插入排序算法的一种改良
* 直接插入排序算法前面的有序部分寻找j的位置的时候采用遍历的方式
* 如果将前面查找j的位置的过程采用折半查找的方式,在数据量大的时候可以很大程度地提高效率
*/
for (let i = 1; i < arr.length; i++) {
// 折半查找下标
let start = 0;
let end = i - 1;
let tmp = arr[i];
//折半查找,只要end的位置不在start前面,就一直查找
while (end >= start) {
//获取中间下标
let mid = Math.floor((start + end) / 2);
//如果前面有序部分的中间位置的值大于现在要插入的值,说明要插入的位置在左半部分,反之,一样
if (arr[mid] > tmp) {
//将尾部改为mid,为下次循环作准备
end = mid;
} else {
start = mid + 1;
}
}
//上面那个while循环完成以后,最终end + 1的位置就是要插入的位置,让end后面的部分都腾一个位置出来
for (let j = i - 1; j > end; j--) {
arr[j + 1] = arr[j];
}
arr[end + 1] = tmp;
}
return arr;
}
/**
* 算法:希尔排序
* 时间复杂度:最好O(n) 最差O(nlog2n)
* 空间复杂度:O(1)
* 不稳定排序
*/
function insertSortShell(arr) {
if (arr.length <= 1) {
return arr;
}
// 也是对直接插入排序的一种改进,又称为 缩小增量排序 => 三层for循环+if
//确定增量d
for (let d = Math.floor(arr.length / 2); d > 0; d = Math.floor(d / 2)) {
//对于每一遍循环,找到i和i-d元素进行比较,按照升序排列
for (let i = d; i < arr.length; i++) {
// j -= d是为了防止下面的数组下标越界
for (let j = i - d; j >= 0; j -= d) {
if (arr[j] > arr[j + d]) {
let tmp = arr[j];
arr[j] = arr[j + d];
arr[j + d] = tmp;
}
}
}
}
return arr;
}
console.log("\n插入排序:")
console.log(`1.直接插入排序后:${insertSortDirect(arr)}`)
console.log(`2.折半插入排序后:${insertSortHalf(arr)}`)
console.log(`3.希尔排序后:${insertSortShell(arr)}`)
/**
* 交换排序
* 1.冒泡排序
* 2.快速排序
*/
/**
* 算法:冒泡排序
* 时间复杂度:O(n^2)
* 空间复杂度:O(1)
* 稳定排序
*/
function switchSortBubble(arr) {
if (arr.length <= 1) {
return arr;
}
/**
* 过程像冒泡一样,依次比较前面和后面的元素的大小,并进行交换
*/
for (let i = 0; i < arr.length - 1; i++) {
for (let j = i + 1; j < arr.length; j++) {
if (arr[i] > arr[j]) {
let tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
}
}
return arr;
}
/**
* 算法:快速排序
* 时间复杂度:最好O(nlogn) 最差O(n^2)
* 空间复杂度:O(logn)
* 不稳定排序
*
*/
function switchSortQuick(arr, start, end) {
if (arr.length <= 1) {
return arr;
}
/*
*采用递归的思想 是对冒泡排序的一种改进
*右-->左找比基准值小的 左-->右找比基准值大的
*/
if (start < end) {
//基准值
let base = arr[start];
let i = start;
let j = end;
while (i < j) {
//从右边开始找,一直到小于base才停止
while (i < j && arr[j] > base) {
j--;
}
//小于base的时候进行交换
if (i < j) {
arr[i] = arr[j];
i++;
}
//同理,左边一样的
while (i < j && arr[i] < base) {
i++;
}
if (i < j) {
arr[j] = arr[i];
j--;
}
}
// 第一轮循环以后可以知道 基准值 最终的位置该在哪里(i)
arr[i] = base
//然后递归调用左边和右边,将数组的左右两边递归
switchSortQuick(arr, start, i - 1);
switchSortQuick(arr, i + 1, end);
}
return arr;
}
console.log("\n交换排序:")
console.log(`1.冒泡排序后:${switchSortBubble(arr)}`)
console.log(`2.快速排序后:${switchSortQuick(arr)}`)
/**
* 选择排序
* 1.简单选择排序
* 2.堆选择排序
*/
/**
* 算法:简单选择排序
* 时间复杂度:O(n^2)
* 空间复杂度:O(1)
* 不稳定排序
*/
function chooseSortSimple(arr) {
if (arr.length <= 1) {
return arr;
}
/**
* 找出数组中最小的数与第一个数交换位置
* 然后下轮在找到后面的最小的数,与第二个数交换位置
*
* 简单选择排序是 冒泡排序 的一种改版
*/
for (let i = 0; i < arr.length - 1; i++) {
// 最小元素的下标
let min = i;
for (let j = i + 1; j < arr.length; j++) {
if (arr[j] < arr[min]) {
min = j;
}
}
let tmp = arr[i];
arr[i] = arr[min];
arr[min] = tmp;
}
return arr;
}
/**
* 算法:堆排序
* 时间复杂度:O(nlogn)
* 空间复杂度:O(1)
* 不稳定排序
*/
function chooseSortHeap(arr){
if(arr.length <= 1){
return arr;
}
let len;
//构建一个顶堆
function bulidMaxHeap(array){
len = array.length;
for(let i = Math.floor(len / 2);i >= 0;i--){
//调整堆
heapify(array,i);
}
}
function heapify(array,i){
let left = 2 * i + 1;
let right = 2 * i + 2;
let largest = i;
if(left < len && array[left] > array[largest]){
largest = left;
}
if(right < len && arr[right] > array[largest]){
largest = right;
}
if(largest !== i){
swap(array,i,largest);
heapify(array,largest);
}
}
function swap(array, i, j) {
let tmp = array[i];
array[i] = array[j];
array[j] = tmp;
}
bulidMaxHeap(arr);
for(let i = arr.length - 1 ; i >= 0 ; i--){
swap(arr,0,i);
len --;
heapify(arr,0);
}
return arr;
}
console.log("\n选择排序:")
console.log(`1.简单选择排序后:${chooseSortSimple(arr)}`)
console.log(`2.堆排序后:${chooseSortHeap(arr)}`)
//归并排序
/**
* 算法:归并排序(JavaScript不推荐这种排序,递归层太多了,因为它的空间复杂度比较高)
* 时间复杂度:O(nlogn)
* 空间复杂度:O(n)
* 稳定排序
*/
function mergeSort(arr){
if(arr.length <= 1){
return arr;
}
/**归并排序采用了 分治 的思想,将数组一直等分,等分后的区间作排序,最后在合并 也就是先 “分” 后 “治“*/
let mid = Math.floor(arr.length / 2);
let left = arr.slice(0,mid);
let right = arr.slice(mid);
//递归操作
return merge(mergeSort(left),mergeSort(right));
//定义一个merge方法,用来合并 合并left数组和right数组的部分 核心部分
function merge(left,right){
//用来存放合并的结果
let result = [];
//由于左右的部分不一样长,所以先比较两者共有的部分
while(left.length && right.length){
//如果左边的第一个元素的值比右边的元素的第一个值小,就把左边的第一个值push到待返回数组中,
//反之一样,重复这个操作,直到有一边已经没有值了
if(left[0] <= right[0]){
result.push(left.shift());
}else{
result.push(right.shift());
}
//处理有一边还剩余的情况
while(left.length){
result.push(left.shift());
}
while(right.length){
result.push(right.shift());
}
return result;
}
}
}
console.log(`\n归并排序后:${mergeSort(arr)}`)
//基数排序
/**
* 算法:基数排序
* 时间复杂度:O(n x k)
* 空间复杂度:O(n + k)
* 稳定排序
*/
function radixSort(arr){
if(arr.length <= 1){
return arr;
}
/**思想:数字是0 ~ 9的,为每个数字建立一个桶 根据键值的每位数字来分配桶; */
//先找出数组中最大的数,确定最大的数是几位的
function getMaxDigit(){
let max = arr[0];
for(let i = 1 ; i < arr.length; i++){
if(arr[i] > max){
max = arr[i];
}
}
return max.toString().length;
}
let counter = [];
let mod = 10;
let dev = 1;
let maxDigit = getMaxDigit();
for (let i = 0; i < maxDigit; i++, dev *= 10, mod *= 10) {
for(let j = 0; j < arr.length; j++) {
let bucket = parseInt((arr[j] % mod) / dev);
if(counter[bucket]==null) {
counter[bucket] = [];
}
counter[bucket].push(arr[j]);
}
let pos = 0;
for(let j = 0; j < counter.length; j++) {
let value = null;
if(counter[j]!=null) {
while ((value = counter[j].shift()) != null) {
arr[pos++] = value;
}
}
}
}
return arr;
}
console.log(`\n基数排序后:${radixSort(arr)}`)
运行结果
总结