最近在做一些面试的工作,发现大家有个不好的习惯,没有对数据进行排序处理的习惯,这种意识有点差;而如果提前对数据进行排序,会使我们的逻辑更清晰,有时候一些面试题看似复杂难解决,有很大一部分原因是因为我们没有对数据进行排序;
排序!!!很重要!!!
排序我个人整理了一些;
1、冒泡
function bubble(arr){
for(let i = 0;i<arr.length-1;i++){ // 比较数组长度-1 轮就可以
for(let j = 0;j<arr.length-1-i;j++){ // -1 是防止arr[j+1]溢出,-i 是除去已经排好序的最后i个
if(arr[j] > arr[j+1]){
[arr[j],arr[j+1]] = [arr[j+1],arr[j]]
}
}
}
}
let arr = [9,4,7,3,1,0]
bubble(arr);
console.log(arr)
2、选择排序
工作原理:
初始时在未排序序列中找最小(最大)元素,放到序列的起始位置作为已排序序列;
然后再从剩余未排序元素中继续寻找最小(大)元素,放到已排序序列的末尾。
以此类推。直到所有元素均排序完毕。
function sort(arr) {
var len = arr.length;
//已排序序列的末尾
for (var i = 0; i < len; i++) {
var min = i;
//待排序序列
for (var j = i + 1; j < len; j++) {
//从待排序序列中找到最小值
if (arr[j] < arr[min]) {
min = j;
}
}
if (min != i) {
var temp = arr[i];
arr[i] = arr[min];
arr[min] = temp;
}
}
return arr;
}
3 、插入排序
插入排序稳定,最差时间复杂度待排序列是降序序列;最优时间复杂度即待排序列是升序
- 从第一元素开始,该元素可以默认已经被排序
- 取出下一个元素,在已经排序序列中从后向前扫描
- 如果已排序的元素大于新元素,将下一个位置设置成该元素 4.重复步骤3,直到找到已排序的元素小于或者等于新元素的位置 5.将新元素插入到该位置 6.重复2~5
function insertSort(arr) {
let len = arr.length;
let preIndex, current;
for (let i = 1; i < len; i++) {
preIndex = i - 1; //排好序的数组最后一个下标
current = arr[i]; //待排序的数值
//让待排序的值与排序好的数组值进行比较
while (preIndex >= 0 && arr[preIndex] > current) {
arr[preIndex + 1] = arr[preIndex];
preIndex--;
}
//待排序的数值大于等于排序好的值
arr[preIndex + 1] = current;
}
return arr;
}
4、快速排序
nlogn 主要是交换 比循环和插入要性能好 其实任何一个值都可以做支点 最差n*n www.bilibili.com/video/av478… a. 默认pirot 设为-1 pirot 左侧都是比 arr[right] 小的值,右侧都是比arr[right] 大的值
function quick_sort(arr,left,right) { //right 为长度-1
if (left >= right) {
return;
}
let p = partition(arr, left, right);
quick_sort(arr, left, p - 1);
quick_sort(arr, p + 1, right);
}
function partition(arr,left,right) {
let value = arr[right]; // 10
let pivot = left - 1; // -1
for(let i=left; i<right; i++) { // i=left
if(arr[i]<= value) {
pivot += 1;
// 交换值
[arr[i],arr[pivot]] = [arr[pivot],arr[i]];
}
}
// 此时 pivot左侧都小于arr[right] 右侧都大于arr[right] 需将arr[right] 和pivot+1 位置的数交换
pivot += 1;
[arr[right],arr[pivot]] = [arr[pivot],arr[right]];
return pivot;
}
let arr = [1,12,9,4,10];
quick_sort(arr,0,4);
console.log(arr);
5.归并排序
归并排序采用的是分治的思想,首先是“分”,将一个数组反复二分为两个小数组,直到每个数组只有一个元素;其次是“治”,从最小数组开始,两两按大小顺序合并,直到并为原始数组大小, “治”实际上是将已经有序的数组合并为更大的有序数组, 时间复杂度为 O(nlogn)。
function mergeSort(arr) {
const length = arr.length;
if (length === 1) { //递归算法的停止条件,即为判断数组长度是否为1
return arr;
}
const mid = Math.floor(length / 2);
const left = arr.slice(0, mid);
const right = arr.slice(mid, length);
return merge(mergeSort(left), mergeSort(right)); //要将原始数组分割直至只有一个元素时,才开始归并
}
function merge(left, right) { // 主要负责合并两个有序数组
const result = [];
let il = 0;
let ir = 0;
//left, right本身肯定都是从小到大排好序的
while (il < left.length && ir < right.length) {
if (left[il] < right[ir]) {
result.push(left[il]);
il++;
} else {
result.push(right[ir]);
ir++;
}
}
//不可能同时存在left和right都有剩余项的情况, 要么left要么right有剩余项, 把剩余项加进来即可
while (il < left.length) {
result.push(left[il]);
il++;
}
while (ir < right.length) {
result.push(right[ir]);
ir++;
}
return result;
}
6、具体的例子
班上有五个同学,分别考了 5分、3分、5分、2分、8分,满分为10分,需要用桶排序的方法实现分数从小到大排列。
只能排序大于0的 比较浪费内存 如果最大的数比较大的话 适合对数据比较了解 桶排序简单版 简单版不支持小数
let bucketSort = () => {
let arr = new Array(11);
let marks = [5, 3, 5, 2, 8];
let newArr = [];
for (init = 0; init < arr.length; init++) {
arr[init] = 0;
};
for (i = 0; i < marks.length; i++) {
arr[marks[i]]++;
};
for (j = 0; j < arr.length; j++) {
if (arr[j] == 0) {
continue;
} else {
for (l = 0; l < arr[j]; l++) {
newArr.push(j);
};
};
};
return newArr;
}
// 也可以用一个二维数组来放置相同的数
function Bucket_sort(arr,range) {
let buckets = Array.from({length: range},()=>[]);
// 下表算法
const indexFun = val => {
return Math.floor(val)
};
arr.forEach(item =>{
let idx = indexFun(item);
if(idx === undefined) {
throw new Error(`没有${item}下标`)
}
buckets[indexFun(item)].push(item);
});
let valueArr = buckets.filter((sub)=>{ // 过滤有值的
return sub.length > 0
})
valueArr.map(bucket=>{
// 桶里再加一次排序
return bucket.sort((x,y)=>x-y)
})
return valueArr.reduce((prev,cur)=>{
return prev.concat(cur)
},[])
}
let arr = Bucket_sort([2,1,9,0,6.1],10)
console.log(arr)
复杂版
计算并设置固定数量的空桶
将数据放入对应的桶中
对桶中的数据进行排序
把每个桶的数据进行合并
//插入排序
function insertion_sort(A){
for(let i=1; i<A.length; i++){
let p = i-1
const x = A[i]
while(p>=0 && A[p]>x){
A[p+1] = A[p]
p--
}
A[p+1] = x
}
}
执行函数的作用主要为 匿名 和 自动执行,代码在被解释时就已经在运行了。
function bucket_sort(A, k, s){ //A排序数组,k桶子数量,s桶子空间尺度
// es6 Array.from()方法就是将一个类数组对象或者可遍历对象转换成一个真正的数组。
const buckets = Array.from({length:k}, ()=>[]) //创建桶子
//把元素放入对应桶子
for(let i=0; i<A.length; i++){
//计算需要放入桶子序号
const idx = ~~(A[i]/s)
buckets[idx].push(A[i])
}
//对每个桶子进行排序
for(let i=0; i<buckets.length; i++){
//此处选取插入排序, 空间消耗少,元素少常数时间消耗短
insertion_sort(buckets[i])
}
//把每个桶子数据合并
return [].concat(...buckets)
}
7、二分查找
条件: 数组是排序的 非排序的直接for查找
function find(arr, num, start, end) {
if (!arr || !num) {
throw new Error('no illegal arguments');
}
if (arr.length == 0) {
return arr[0] === num;
}
if (!start) {
start = 0;
}
if (!end) {
end = arr.length;
}
let mid = Math.floor((start + end) / 2);
if (num === arr[mid]) {
return mid
}
if (num < arr[mid]) {
return find(arr, num, 0, mid); // 想得到下标这里不能用slice 新数组下标会变
} else {
return find(arr, num, mid, end);
}
return false;
}
let arr = [0, 4, 8, 9, 12, 16, 30];
console.log(find(arr, 16))