使用快速选择算法找出无序数组里的中位数
该方法不需要暴力整体排序,而是找出某个数按大小排列时的下标 先介绍大致步骤,以arr=[4,2,6,9,11]为例
arr.length为5,那我们要找到按大小排列时下标为
Math.floor(5/2)=2
的那个数即为中位数
Let's begin:
s1:随机选择一个数,比如4,把它交换到末尾,此时
arr=[11,2,9,6,4]
s2:找出4在数组中按大小排列时的下标i
//过程是遍历数组,最终下标<i的都<4,假设遍历过程的下标为j,
j=0,i=0,arr[j]=11,而11>4,数组不变
j++
j=1,i=0,arr[j]=2,2<4,此时把arr[j]和arr[i]交换,i++
变更后 arr=[2,11,9,6,4],i=1
j++
j=2,i=1,arr[j]=6,6>4,数组不变,i不变,
arr=[2,11,9,6,4],i=1
j++
j=3,i=1,arr[j]=9,9>4,数组不变,i不变
arr=[2,11,9,6,4],i=1
j++
j=4,此时遍历到最后一个,而一开始我们就把4放到了最后一个,所以不用比较,结束遍历
最终arr[i]和4交换,
变更后 arr=[2,4,9,6,11],i=1
说明4在下标为1的位置上,而我们要找的是下标为3的那个数
s3:此时4之后的数都>4,而下标2>1,说明要继续在9,6,11里随机选一个数,执行于s2类似的步骤,将<这个数的排到它之前,将>这个数的排到它之后,找出它按顺序排列的下标i,并判断i===2?
//这次我们选择6,6要换到最后面
此时,arr=[2,4,9,11,6],i为4的顺序下标1加一,即i=2,遍历范围为[9,11,6],故j从2开始
j=2,arr[j]=9,9>6,i不变,i=2
j++
j=3,arr[j]=11,11>6,i不变,i=2
j++
j=4,此时遍历到最后一个,而一开始我们就把6放到了最后一个,所以不用比较,结束遍历
最终arr[i]与6交换
变更后arr=[2,4,6,9,11],i=2,找的就是它!
中位数为6!
上代码,把上面三个步骤搞懂后,代码就很好懂了
function findMedian(arr) {
let length = arr.length;
let k = Math.floor(length / 2);
if (length % 2 === 0) {
// 如果数组长度是偶数,返回中间两个元素的平均值
let x = quickSelect(arr.slice(), 0, length - 1, k - 1);
let y = quickSelect(arr.slice(), 0, length - 1, k);
return (x + y) / 2;
} else {
// 如果数组长度是奇数,返回中间元素
return quickSelect(arr.slice(), 0, length - 1, k);
}
}
function quickSelect(arr, low, high, k) {
if (low <= high) {
//随机选一个下标
let pivotIndex = Math.floor(Math.random() * (high - low + 1) + low);
//拿到这个下标的值
let pivot = arr[pivotIndex];
//把它放到数组最后面
swap(arr, pivotIndex, high);
let i = low; //i最终为pivot的顺序排列时的下标
for (let j = low; j < high; j++) {
if (arr[j] < pivot) {
swap(arr, i, j);
i++;
}
}
//把pivot从最后面换到它顺序排列时该呆的位置
//此时pivot前面的数都<它,后面的数都>它
swap(arr, i, high);
//判断pivot是不是我们要找的数
if (i === k) {
return pivot;
} else if (i < k) {
//说明要从比pivot大的数里面找
return quickSelect(arr, i + 1, high, k);
} else {
//说明要从比pivot小的数里面找
return quickSelect(arr, low, i - 1, k);
}
}
}
function swap(arr, i, j) {
[arr[i], arr[j]] = [arr[j], arr[i]];
}
let myArray = [4, 2, 7, 1, 9, 5, 99, 6];
//顺序排列为[1,2,4,5,6,7,9,99],中位数为5.5
let median = findMedian(myArray);
console.log("中位数是:" + median); //5.5