一、冒泡排序
排序思想:列表每两个相邻的数进行比较,如果前面的数比后面的数大,则交换这两个数,一轮排序完成后,则无序区减少一个数,有序区增加一个数。
function sort(list) {
for (let i= 0; len = list.length-1; i list[j+1]) {
// 交换两个数
const m = list[j];
list[j] = list[j+1];
list[j+1] = m;
exchange = true;
}
}
// 如果在一轮冒泡过程中没有交换过,说明此时的列表已经是排序好的了,直接结束循环
if (!exchange) {
return;
}
}
}
二、选择排序
排序思想:刚开始将整个数组看作一个无序区,每一轮拿无序区的第一个数与无序区其他数值依次进行比较,遇到更小的数则交换,每一轮排序完成后有序区增加一个数,无序区减少一个数。
function sort (list) {
for (let i=0,len=list.length; i list[j]) {
// 交换两个数
const m = list[i];
list[i] = list[j];
list[j] = m;
}
}
}
}
三、插入排序
排序思想:刚开始将整个数组看作一个无序区,每一轮拿无序区的第一数与有序区的数从后往前依次进行比较,遇到更大的数则交换,每一轮排序完成后,有序区增加一个数,无序区减少一个数。
function sort(list) {
for (let i=1,len=list.length; i=0; j--) {
if (list[j+1] < list[j]) {
// 交换两个数
const m = list[j+1];
list[j+1] = list[j];
list[j] = m;
}
}
}
}
四、快速排序
排序思想:取一个元素p(第一个数),使元素p归位,此时列表会被p分成两部分,左边都比p小,右边都比p大,然后左边和右边再分别进行递归使元素归位,最终完成排序。
function sort(list, left, right) {
if (left < right) {
const mid = homing(list, left, right);
sort(list, left, mid-1);
sort(list, mid+1, right);
}
}
/**
* 归位函数
* 首先将第一个元素缓存起来,左指针刚开始指向第一个元素
* 然后右指针依次往左走,直到找到比缓存起来的元素小的元素,将该元素赋值给当前左指针指向的元素
* 然后左指针依次往右走,直到找到比缓存起来的元素大的元素,将该元素赋值给当前右指针指向的元素
* 重复以上操作,当左指针和右指针重合时,便找到了要归位的地方,将缓存起来的元素赋值给左指针指向的元素,完成归位
*/
function homing(li, left, right) {
const tmp = li[left];
while (left < right) {
while (left < right && li[right] >= tmp) {
right--;
}
li[left] = li[right];
while(left < right && li[left] <= tmp) {
left++;
}
li[right] = li[left];
}
li[left] = tmp;
return left;
}
五、归并排序
排序思想:假设有两个排序好的数组,如何将他们合并成一个排序好的数组,首先创建一个空的新数组,将两个有序的数组依次进行比较,每次将较小的数放到新数组中,最终得到一个排序好的新数组,此过程称为归并。数组为空或者只有一个元素时就是有序的,就可以进行归并,归并排序就是先两两进行归并,再利用递归,将归并后的两个数组再进行归并,最终得到排序好的数组。
function sort(list) {
if (list && list.length > 1) {
const mid = Math.floor(list.length / 2);
const left = list.slice(0, mid);
const right = list.slice(mid);
return merge(sort(left), sort(right));
}
return list;
}
/**
* 归并函数
* 首先创建一个空的新数组
* 将两个有序数组中的元素依次进行比较,每次将较小的数放到新数组中
* 最终得到一个排序好的新数组
*/
function merge(leftList, rightList) {
const newList = [];
const leftLength = leftList && leftList.length;
const rightLength = rightList && rightList.length;
let i = 0;
let j = 0;
while (i < leftLength && j < rightLength) {
if (leftList[i] < rightList[j]) {
newList.push(leftList[i++]);
} else {
newList.push(rightList[j++]);
}
}
while (i < leftLength) {
newList.push(leftList[i++]);
}
while (j < rightLength) {
newList.push(rightList[j++]);
}
return newList;
}
六、堆排序
排序思想:二叉树的存存储方式分为链式存储和顺序存储,这里堆排序使用顺序存储。堆是一种特殊的完全二叉树,堆分为大根堆和小根堆,满足任一节点都比其孩子节点大的一个完全二叉树就是大根堆,满足任一节点都比其孩子节点小的一个完全二叉树就是小根堆,这里堆排序使用大根堆。假设根节点的左右子树都是堆,但是根节点不满足堆的性质,可以通过一次向下调整来将其变成一个堆,这就是堆的向下调整性质。堆排序的排序过程是,首先构造一个大根堆(此时整个堆是无序区),然后将堆顶的元素取出放到有序区(也就是数组的最后),然后将堆的最后一个元素(也就是无序区的最后一个元素)放到堆顶,堆就少了一个元素,此时通过一次向下调整重新使堆有序,调整后的堆顶就是整个数组的第二大元素,然后重复之前的操作依次将元素放到有序区,直到堆变空,便可得到排序好的数组。
function sort(list) {
if (list && list.length > 1) {
const len = list.length;
// 首先构造大根堆,从最后一个不是叶子节点的节点开始遍历,从后往前依次进行向下调整
for (let i = Math.floor((len-2)/2); i>=0; i--) {
sift(list, i, len-1);
}
// 然后将堆的第一元素与有序区的第一个元素进行交换,此时有序区增加一个,无序区减少一个,再进行一次堆的向下调整,然后重复上述操作,最终使整个数组有序
for(let i = len-1; i>=0; i--){
const m = list[0];
list[0] = list[i];
list[i] = m;
sift(list, 0, i-1);
}
}
}
/**
* 堆的向下调整
* 先从根节点开始,如果孩子节点比父节点大,则将该孩子节点赋值给父节点
* 然后指针指向下一层,重复上面的操作,直到找到孩子节点没有比父节点大的节点,终止循环
* 最后将原始的根节点赋值给当前父节点
*/
function sift(li, low, high) {
const tmp = li[low]; // 缓存根节点
let i = low; // 当前的父节点,最开始指向根节点
let j = i*2+1; // 当前的孩子节点,最开始指向根节点的左孩子节点
while (j <= high) {
// 如果有右孩子节点且比左孩子节点大,则j指向右孩子节点
if (j+1 <= high && li[j+1] > li[j]) {
j++;
}
if (li[j] > tmp) {
li[i] = li[j]; // 将较大的孩子节点赋值给父节点
i = j; // i指向下一层
j = i*2 +1;
} else {
break; // 如果当前子节点没有比原始根节点大,结束循环
}
}
li[i] = tmp; // 最后将原始的根节点赋值给当前父节点
}
总结
更多个人文章
- 两个跨域页面进行跳转传参的终极方案
- 深入理解Event Loop的运行机制
- hashHistory和browserHistory的区别
- 面试秘籍之手写系列