1.冒泡排序(o(n^2))
冒泡排序基本思想:两两比较相邻记录的关键字,如果反序则交换位置,直到没有反序记录为止。
过程:遍历每个元素,每次与其后的元素比较,顺序错误就交换位置,重复进行直到不需要交换。这个算法名字的由来是因为越小的元素经由交换慢慢“浮”到数列的顶端。
代码实现如下:
function bubbleSort(arr){
var len = arr.length;
var temp = [];//创建一个临时数组
for(var i=0;i<len;i++){
for(var j=0;j<len-i-1;j++){
if (arr[j] > arr[j+1]) {//遍历的元素与相邻的后一个元素进行比较
//注意:这里赋值运算符的右边是值,左边是数组索引号
temp = arr[j+1];//把小的元素值存进临时数组里
arr[j+1] = arr[j];//把大的元素值赋值给索引值大的
arr[j] = temp;//把临时数组里的元素值赋值给索引值小的
}
}
}
return arr;
}
console.log(bubbleSort([1,4,9,4,7,2,4,7,6]))//[ 1, 2, 4, 4, 4, 6, 7, 7, 9 ]
2.插入排序(o(n^2))
插入排序基本思想:将一个记录插入到已排好序的有序表中,从而得到一个新的、记录数增1的有序表。
过程:第一个元素默认已排序,每次遍历下一元素,在已排序的元素序列中从后向前扫描每个元素并与之一一比较,如果遍历元素小于已排序元素,则将遍历元素插入到已排序元素的位置。
代码实现如下:
function insertionSort(arr){
var len = arr.length;
var preIndex,current;
for(var i=1;i<len;i++){
preIndex = i - 1;//已排序的最后一个元素的索引值
current = arr[i];//将取到的当前元素值赋给current,而不是赋上索引值!
//先将已排序的最后一个元素值与取到的当前元素值进行比较(第一次比较)
while(preIndex >= 0 && arr[preIndex] > current){
//如果大于,则将已排序的最后一个元素值赋给在其后面的一个元素(把值互换位置,索引不变)
arr[preIndex + 1] = arr[preIndex];
//因为是将取出的当前元素通过在已排序的元素序列中从后向前扫描(所以要递减)的元素进行比较,如果前一个
preIndex--;
//如果一直大于,那么就比较值互换之后的元素与其前一个元素进行比较(第二次比较),值再次互换,直到不大于
}
arr[preIndex + 1] = current;//如果不是前一个元素大于其后一个元素,那么前一个元素值与其后一个元素值不互换
}
return arr;
}
console.log(insertionSort([1,4,9,4,7,2,4,7,6]))//[ 1, 2, 4, 4, 4, 6, 7, 7, 9 ]
3.选择排序(o(n^2))
选择排序基本思想:是通过n-i次关键字间的比较,从n-i个记录中选出关键字最小的额记录,并和第i个记录交换位置。
过程:先在未排序序列中找到最小(大)元素,存放在已排序序列的起始位置;然后从剩余未排序元素中继续寻找最小(大)元素,然后放在已排序的末尾,直到所有元素排序完毕。
代码实现如下:
function selectionSort(arr){
var len = arr.length;
var minIndex,temp;
for(var i=0;i<len;i++){
minIndex = i;//将i的值赋给最小索引变量
for(var j=i+1;j<len;j++){
if(arr[minIndex] > arr[j]){//当前元素与其后所有元素逐一比较
//当前元素值一旦大于其后某一元素的值,那么便把小元素所在的索引值赋给minIndex
//接下来进行比较的时候,就是将上一次比较得到的小元素再与在其之后的元素逐一比较,再次寻找较小元素
//直到找到最小的元素,并把最小元素的索引赋值给minIndex
minIndex = j;
}
}
//每次找到最小元素后便把这个小元素放在每次循环的第一个位置
temp = arr[i];//将当前元素对应的值存进temp
arr[i] = arr[minIndex];//把最小元素的值赋给当前元素索引对应的值
arr[minIndex] = temp;//把当前元素的值赋给最小元素所在的位置(索引)
//也就是说上述三行代码的意思是将两个比较的元素的值互换
}
return arr;
}
console.log(selectionSort([1,4,9,4,7,2,4,7,6]))//[ 1, 2, 4, 4, 4, 6, 7, 7, 9 ]
4.快速排序(o(nlog2(n)))
快速排序基本思想(过程):
第一,在数据集之中选择一个元素作为基准;
第二,数组(除开基准值)里的每个元素与基准进行比较,所有小于基准的元素都放在左边的数组里;所有大于基准的元素都放在右边的数组里;
第三,对左边和右边的两个子集不断的重复第一步和第二步,直到所有子集只剩下一个元素为止;
第四,将所有子集拼接形成一个已排序的数组。
代码实现如下:
function quickSort(arr){
if (arr.length <= 1) {
return arr;
}
var pivotIndex = Math.floor(len/2);//为了取数组基准(中间值)而准备的索引
var pivot = arr.splice(pivotIndex,1)[0];//选出基准值(中间元素)
var left = [],right = [],len = arr.length;
for(var i=0;i<len;i++){
if(arr[i] < pivot){//数组的各个元素(除了基准元素,这个时候的arr是删除了基准的数组)与基准元素比较,小的元素放在左边数组
left.push(arr[i]);
}else{
right.push(arr[i]);//大的元素放在右边数组里
}
}
return quickSort(left).concat([pivot],quickSort(right));//重复上述操作,直至排序完成,最后拼接左边数组+基准值+右边数组
}
console.log(quickSort([1,4,9,4,7,2,4,7,6]))//[ 1, 2, 4, 4, 4, 6, 7, 7, 9 ]
5.希尔排序(o(n^1.3))
希尔排序基本思想:它是第一个突破O(n^2)的排序算法,是简单插入排序的改进版。它与插入排序的不同之处在于,它会优先比较距离较远的元素。实质是分组插入排序,希尔排序又称缩小增量排序。
过程:把记录按步长(两元素间隔)gap分组,对每组记录采用直接插入排序方法进行排序;随着步长逐渐减小,每个分组包含的记录越来越多;当步长的值减小到1时,整个数据合成一组,构成一组有序记录,则完成排序。
代码实现如下:
function shellSort(arr) {
var len = arr.length,
gap = Math.floor(len/2);//两个数的间隔向下取整
while(gap >= 1){//两个数的间隔必须不小于1
for(var i=gap;i<len;i++){
var j,temp=arr[i];//把遍历的值(靠后)暂时存进temp
for(j=i-gap;j>=0&&temp<arr[j];j=j-gap){//前面的元素与间隔后的数做比较
arr[j+gap]=arr[j];//如果后面的数比前面的数小,那么两个数互换位置
}
arr[j+gap]=temp;//否则位置不变
}
gap = Math.floor(gap/2);//继续重复操作,知道gap<1跳出循环
}
return arr;
}
console.log(shellSort([1,4,9,4,7,2,4,7,6]))//[ 1, 2, 4, 4, 4, 6, 7, 7, 9 ]
6.归并排序(o(nlog2(n)))
归并排序的基本思想:该算法是采用分治法的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若有两个有序表合并成一个有序表,称为2-路归并。
过程:第一,把长度为n的输入序列分成两个长度为n/2的子序列;第二,对这两个子序列分别采用归并排序;第三,将两个排序好的子序列合并成一个最终的排序序列。
代码实现如下:
function mergeSort(arr){
var len = arr.length;
if (len < 2) {
return arr;
}
var middle = Math.floor(len/2),
left = arr.slice(0,middle),
right = arr.slice(middle);//将数组分成左右两个子序列
return merge(mergeSort(left),mergeSort(right));//将排序好的两个大的子数组进行最后一轮比较并合并
}
function merge(left,right){//将左右两个子序列作为merge函数(辅助函数)的两个参数
var result = [];
while(left.length > 0 && right.length > 0){
if(left[0] <= right[0]){//左边序列的第一个元素和右边序列的第一个元素比较,小的元素则被放入到result中
//这里要着重理解为什么要用shift,shift是删除数组第一个元素并返回
//如果数组中有多个元素,删除第一个,那么接下来第二个就变成第一个,然后继续选出来被比较,直到数组的长度为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(mergeSort([1,4,9,4,7,2,4,7,6]))//[ 1, 2, 4, 4, 4, 6, 7, 7, 9 ]
7. 二分查找算法(O(log2n))
二分查找,也称为折半查找,是指在有序的数组里找出指定的值,返回该值在数组中的索引。查找步骤如下:
(1)从有序数组的最中间元素开始查找,如果该元素正好是指定查找的值,则查找过程结束。否则进行下一步;
(2)如果指定要查找的元素大于或者小于中间元素,则在数组大于或小于中间元素的那一半区域查找,然后重复第一步的操作;
(3)重复以上过程,直到找到目标元素的索引,查找成功;或者直到子数组为空,查找失败。
一、非递归实现:
function binarySearch(arr, key) {
var low = 0,
high = arr.length - 1;
while(low <= high) {
var mid = parseInt((high + low) /2);
if(key == arr[mid]) {
return mid;
} else if(key > arr[mid]) {
low = mid + 1;
} else if(key < arr[mid]) {
high = mid -1;
} else {
return -1;
}
}
}
var arr = [1,2,3,4,5,6,7,8,9,10,11,23,44,86];
var result = binarySearch(arr, 10);
console.log(result);// 9
二、递归实现:
function binarySearch(arr, low, high, key) {
if(low > high) {
return -1;
}
var mid = parseInt((high + low) / 2);
if(arr[mid] == key) {
return mid;
} else if(arr[mid] > key) {
high =mid -1;
return binary_search2(arr, low, high, key);
} else if(arr[mid] < key) {
low = mid +1;
return binary_search2(arr, low, high, key);
}
}
var arr = [1,2,3,4,5,6,7,8,9,10,11,23,44,86];
var result = binarySearch(arr, 0, 13, 10);
console.log(result);// 9