通过构建有序数组元素的存储,对未排序的数组元素,在已排序的数组中从最后一个元素向第一个元素遍历,找到相应位置并插入。具体原理就是,假设我们从小到大排序,即先从第二个元素开始,与第一个元素比较,把小的元素放到前面,然后继续循环,第三个元素与它前面的元素互相比较,如果第三个元素比某个元素小,则放在它前面,这样依次将每个元素与前面的元素进行比较,最后排序就完成了,效果等同于把一个个元素拿出来,插入到它该有的位置中。
var a = [1,2,3,43,21,5,0];
var str = '';
for(var k of a){
str += k + '\t';
}
console.log("排序前的数组为:",str);//将排序前的数组元素横向显示
//插入排序,从小到大排序
for(var i = 1;i < a.length;i++){
for(var j = i;j > 0;j--){
if(a[j] < a[j-1]){
let temp = a[j];
a[j] = a[j-1];
a[j-1] = temp;
}
}
}
console.log("排序后的数组为",a);
希尔排序也成为“缩⼩增量排序”,其基本原理是,现将待排序的数组元素分成多个⼦序列
,使得每个⼦序列的元素个数相对较少,然后对各个⼦序列分别进⾏直接插⼊排序
,待整个待排序列“基本有序”后,最后在对所有元素进⾏⼀次直接插⼊排序。因此,我们要采⽤跳跃分割
的策略:将相距某个“增量”的记录组成⼀个⼦序列,这样才能保证在⼦序列内分别进⾏直接插⼊排序后得到的结果是基本有序⽽不是局部有序。希尔排序是对直接插⼊排序算法的优化和升级。
所谓的基本有序,就是⼩的关键字基本在前⾯,⼤的基本在后⾯,不⼤不⼩的基本在中间,例如{2,1,3,6,4,7,5,8,9,}就可以称为基本有序了。但像{1,5,9,3,7,8,2,4,6}这样,9在第三位,2在倒数第三位就谈不上基本有序。
初始增量一般为长度/2。
实现代码如下:
//希尔排序
function shlle_sort(arr){
//初始增量为arr.length/2,每一趟之后再除以2,直到为1
for(let i = Math.floor(arr.length/2);i >= 1;i = Math.floor(i / 2)){
for(let j = i;j < arr.length;j++){
while(arr[j] < arr[j-i]){
let temp = arr[j];
arr[j] = arr[j-i];
arr[j-i] = temp;
j = j-i;
}
}
}
console.log(arr);
}
var arr = [4,8,-92,1,0,6,33];
shlle_sort(arr);
//选择排序
function select(arr){
for(let i = 0;i < arr.length-1;i++){
let min = i;
for(let j = i+1;j < arr.length;j++){
if(arr[min] > arr[j]){
min = j;
}
}
let temp = arr[min];
arr[min] = arr[i];
arr[i] = temp;
}
console.log(arr);
}
var arr = [4,8,-2,1,0,6,33];
select(arr);
原理是,假设我们要实现从小到大的排序,有n个数,则从第一个元素开始,往后相邻的数互相比较,创建一个变量,利用变量把两个数中较大的换到后面,这样第一轮过后,最大的数就会被放到后面,然后从第一个元素继续比到第 n-1个元素,剩余数组中最大的元素会被放到第n-1位,经过n-1轮后,整个排序就完成了。
实现代码如下:
var a = [1,2,3,43,21,5,0];
console.log("排序前的数组为:");
for(var k of a){
console.log(k);
}
//冒泡排序,从小到大排序
for(var i = 0;i < a.length;i++){
for(var j = 0;j < a.length;j++){
if(a[j] > a[j+1]){
let temp;
temp = a[j];
a[j] = a[j+1];
a[j+1] = temp;
}
}
}
console.log("排序后的数组为",a);
这样一个排序算法就写好了,但是上述代码还有很多优化空间
(1)上述代码实现循环时,每次都要从第一个元素一直比到最后一个元素,在每一轮循环时,最后一位数字已经是当前最大数了,不用再把它进行比较,所以我们可以改变代码如下,减小运算量。
...
for(var j = 0;j < a.length - i;j++)
...
(2)改变代码后,在试数时我们发现,每次循环到最后时,由于我们设定的判断表达式是j
...
for(var j = 0;j < a.length - i - 1;j++)
...
(3)代码全部改完后,我们可以思考一下 ,假设在某次循环完成后,已经排序好了,它下一次循环时并不会交换任何数,可是循环还在继续,直到不满足判断,为了提高性能,我们可以再添加一个标志位,如果循环时有数进行了交换,设它为false,如果没有循环,则它为true,跳出整个循环,这样大大优化了此代码:
var a = [1,2,3,43,21,5,0];
console.log("排序前的数组为:");
for(var k of a){
console.log(k);
}
//冒泡排序,从小到大排序
for(var i = 0;i < a.length;i++){
let flag = true;
for(var j = 0;j < a.length-i-1;j++){
if(a[j] > a[j+1]){
let temp;
temp = a[j];
a[j] = a[j+1];
a[j+1] = temp;
flag = false;
}
}
if(flag){
break;
}
}
console.log("排序后的数组为",a);