排序是常见的功能,给定一组数据,对其进行排序。
在此之前,我们需要准备个基础工作--自动生成数组,并可以对该组数据做任何处理。
/**
* 测试类 ,数组
* @param numElements
* @constructor
*/
function CArray(numElements){
var me = this;
me.dataStore = [];
me.pos = 0;
me.numElements = numElements;
me.insert = insert;
me.toString = toString;
me.clear = clear;
me.setData = setData;
me.swap = swap;
me.bubbleSort = bubbleSort;
me.selectionSort = selectionSort;
me.insertionSort = insertionSort;
for(var i=0;i<numElements;++i){
me.dataStore[i] = i ;
}
function insert(element){
me.dataStore[me.pos++] = element;
}
function toString(){
var restr = "";
for ( var i = 0; i < me.dataStore.length; ++i ) {
restr += me.dataStore[i] + " ";
if (i > 0 & i % 10 == 0) {
restr += "
";
}
}
return restr;
}
function clear(){
for(var i=0;i<me.dataStore.length;++i){
me.dataStore[i]=0;
}
}
function setData(){
for ( var i = 0; i < me.numElements; ++i ) {
me.dataStore[i] = Math.floor(Math.random() * (me.numElements + 1));
}
}
}
用例:
var n= 10000;
var mynums = new CArray(n);
mynums.setData();
接下来是排序算法的实现
冒泡排序
//冒泡排序算法
//比较相邻的数据 左侧大于右侧时将他们进行互换。
function bubbleSort(){
var elen = me.dataStore.length;
var temp ;
for(var outer = elen; outer>=2;--outer){
for(var inner = 0;inner<=outer -1; ++inner){
if(me.dataStore[inner]>me.dataStore[inner+1]){
swap(me.dataStore,inner,inner+1)
}
}
//console.log(me.toString()) //打印这个排序过程。
}
}
测试:
var n= 10000;
console.time("bubble")
//冒泡排序
var mynums = new CArray(n);
mynums.setData();
mynums.bubbleSort();
console.timeEnd("bubble")
选择排序
选择排序从数组的开头开始,将第一个元素和其他元素进行比较。检查完所有元素后,最小的元素会被放到数组的第一个位置,然后算法会从第二个位置继续。这个过程一直进行,当进行到数组的倒数第二个位置时,所有的数据便完成了排序.
//选择排序
//将第一个元素与其他元素比较,较小的放到数组第一个位置 依次比较
function selectionSort(){
var min,temp ;
for(var outer = 0;outer<=me.dataStore.length-2;++outer){
min = outer;
//比较第一个元素 跟第二个元素(依次递增下标再比较,直到找到一个最小的)
for(var inner = outer+1;inner<=me.dataStore.length-1;++inner){
if(me.dataStore[inner]<me.dataStore[min]){
min = inner ;
}
}
swap(me.dataStore,outer,min)
//console.log(me.toString()) //打印这个排序过程。
}
}
测试:
var n = 1000;
//选择排序
var mynums = new CArray(n);
mynums.setData();
mynums.selectionSort();
console.timeEnd("select")
插入排序:
插入排序有两个循环。外循环将数组元素挨个移动,而内循环则对外循环中选中的元素及它后面的那个元素进行比较。如果外循环中选中的元素比内循环中选中的元素小,那么数组元素会向右移动,为内循环中的这个元素腾出位置
//插入排序
//将一堆数据开始排序,如果小,就往左放(此时数据已经在数组中占得一个下标,我们就把他下标右移一个位置,并把这个小的放左边)
function insertionSort(){
var temp,inner;
for(var outer=1;outer<=me.dataStore.length-1;++outer){
temp = me.dataStore[outer];
inner = outer;
while(inner>0&&(me.dataStore[inner-1]>=temp)){
me.dataStore[inner]=me.dataStore[inner-1];
--inner;
}
me.dataStore[inner]=temp;
// console.log(me.toString()) //打印这个排序过程。
}
}
测试:
//插入排序
var mynums = new CArray(n);
mynums.setData();
mynums.insertionSort();
console.timeEnd("insert")
高级排序
高级数据排序算法。它们通常被认为是处理大型数据集的最高效排序算法,它们处理的数据集可以达到上百万个元素,而不仅仅是几百个或者几千个。
希尔排序
希尔排序的工作原理是,通过定义一个间隔序列来表示在排序过程中进行比较的元素之间有多远的间隔,它会首先比较距离较远的元素,而非相邻的元素。和简单地比较相邻元素相比,使用这种方案可以使离正确位置很远的元素更快地回到合适的位置。
第一版:
//希尔排序
//定义间隔序列 在排序的时候到根据序列比较,动态调整序列的位置 。这种方案可以使离正确位置很远的元素更快地回到合适的位置。
//至于间隔序列是多少呢? 有很多个不同的序列 我们用的是Marcin Ciura 2001公开的序列 701 301 132 57 23 10 4 1
function shellsort(){
for (var g = 0; g < this.gaps.length; ++g) {
for (var i = this.gaps[g]; i < this.dataStore.length; ++i) {
var temp = this.dataStore[i];
for (var j = i; j >= this.gaps[g] && this.dataStore[j- this.gaps[g]] > temp;
j -= this.gaps[g]) {
this.dataStore[j] = this.dataStore[j - this.gaps[g]];
}
this.dataStore[j] = temp;
}
}
}
//定义间隔序列
function setGaps(arr){
me.gaps = arr;
}
第一版 我们需要遵循一个 固定的间隔序列。当你有不同数量的数据时,固定间隔序列会不适用现在的程序,所以呢,动态设定间隔序列就很有必要了。
第二版:
//希尔排序( 动态计算间隔序列)
function shellsort1(){
var N = this.dataStore.length;
var h = 1;
while (h < N/3) {
h = 3 * h + 1;
}
while (h >= 1) {
for (var i = h; i < N; i++) {
for (var j = i; j >= h && this.dataStore[j] < this.dataStore[j-h];j -= h) {
swap(this.dataStore, j, j-h);
}
}
h = (h-1)/3;
}
}
测试:
console.time("shell")
//希尔排序
var mynums = new CArray(n);
mynums.setData();
mynums.shellsort();
console.timeEnd("shell")
console.time("shell1")
//希尔排序
var mynums = new CArray(n);
mynums.setData();
mynums.shellsort1();
console.timeEnd("shell1")
归并排序
把一系列排好序的子序列合并成一个大的完整有序序列。
我们需要两个排好序的子数组,然后通过比较数据大小,先从最小的数据开始插入,最后合并得到第三个数组。
自顶向下的归并排序 因实现需要递归(数据多的话递归太深) 所以采取 自底向上的归并排序
自底向上的归并排序
这个算法首先将数据集分解为一组只有一个元素的数组。然后通过创建一组左右子数组将它们慢慢合并起来,每次合并都保存一部分排好序的数据,直到最后剩下的这个数组所有的数据都已完美排序
代码:
function mergeSort() {
if (this.dataStore.length < 2) {
return;
}
var step = 1;
var left, right;
while (step < this.dataStore.length) {
left = 0;
right = step;
while (right + step <= this.dataStore.length) {
mergeArrays(this.dataStore, left, left+step, right, right+step);
left = right + step;
right = left + step;
}
if (right < this.dataStore.length) {
mergeArrays(this.dataStore, left, left+step, right, this.dataStore.length);
}
//步进值
step *= 2;
}
}
function mergeArrays(arr, startLeft, stopLeft, startRight, stopRight) {
var rightArr = new Array(stopRight - startRight + 1);
var leftArr = new Array(stopLeft - startLeft + 1);
k = startRight;
//处理右边数组
for (var i = 0; i < (rightArr.length-1); ++i) {
rightArr[i] = arr[k];
++k;
}
//处理左边数组
k = startLeft;
for (var i = 0; i < (leftArr.length-1); ++i) {
leftArr[i] = arr[k];
++k;
}
rightArr[rightArr.length-1] = Infinity; // 哨兵值
leftArr[leftArr.length-1] = Infinity; // 哨兵值
//合并
var m = 0;
var n = 0;
for (var k = startLeft; k < stopRight; ++k) {
if (leftArr[m] <= rightArr[n]) {
arr[k] = leftArr[m];
m++;
} else {
arr[k] = rightArr[n];
n++;
}
}
//console.log("left array - ", leftArr);
//console.log("right array - ", rightArr);
}
测试:
console.time("mergeSort")
//归并排序
var mynums = new CArray(n);
mynums.setData();
mynums.mergeSort();
console.timeEnd("mergeSort")
通过测试,发现归并排序跟 希尔排序效率差不多(10000个数据)
快排
简单地讲 将数据分解为 大于或者小于 [基准值] 的2部分,通过递归的方式直至完成。
代码
function qSort(arr) {
if (arr.length == 0) {
return [];
}
var left = [];
var right = [];
var pivot = arr[0];
for (var i = 1; i < arr.length; i++) {
if (arr[i] < pivot) {
left.push(arr[i]);
} else {
right.push(arr[i]);
}
}
return qSort(left).concat(pivot, qSort(right));
}
测试:
console.time("qSort")
//快速排序
var mynums = new CArray(n);
mynums.setData();
var list = mynums.getData();
mynums.qSort(list);
console.timeEnd("qSort")