复习排序with javascript
最近又翻看了一下数据结构(数据结构学渣)。
以前总是看不懂,连冒泡和选择排序都要纠结半天,后来才慢慢有意识能区分开来。
当真的理解了快速排序之后,才觉得,这是个很赞的排序,很容易理解。
于是简单的,模仿c的做法,实现了javascript上的排序,目前只有冒泡、选择和快速排序。//不过貌似快速排序用到了传递的性质,也许我应该改改。
function bubbleSort(arr){ console.log("冒泡排序:"); var len = arr.length; for(var i=0; i<len-1;i++){ for(var j=0; j<len-i-1; j++){ if(arr[j]>arr[j+1]){ var temp = arr[j+1]; arr[j+1] = arr[j]; arr[j] = temp; } } } return arr; } function chooseSort(arr){ console.log("选择排序:"); var len = arr.length; for(var i=0; i<len-1;i++){ for(var j=i+1; j<len; j++){ if(arr[i]>arr[j]){ var temp = arr[j]; arr[j] = arr[i]; arr[i] = temp; } } } return arr; } function quickSort(arr, left, right){ if(undefined===left){ console.log("快速排序改实参版:"); left=0; } if(undefined===right){ right = arr.length-1; } if(left>right){ return; } //移动左右索引变量 var i = left; var j = right; var midKey = arr[left]; while(i<j){ while(i<j&&midKey<=arr[j]){ j--; } while(i<j&&midKey>=arr[i]){ i++; } if(i<j){ var temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } } //交换中间数与中轴数 arr[left] = arr[i]; arr[i] = midKey; //递归左右,分治思想 quickSort(arr, left, i-1); quickSort(arr, i+1, right); return arr; } function quickSortArr(arr){ if(arr.length==1){ return arr; } if(arr.length==0){ return []; } var left = [], right = []; var povit = arr[0]; for(var i=1;i<arr.length;i++){ if(arr[i]<=povit){ left.push(arr[i]); }else{ right.push(arr[i]); } } var re = quickSortArr(left).concat([povit],quickSortArr(right)); return re; } function timeIt(fn, arr){ var start = new Date().getTime(); arr = fn(arr); var end = new Date().getTime(); console.log("cost(ms):" +(end-start)+" result:"+arr[0]+ " " + arr[1] +" "+arr[2]+"..."+arr[arr.length-1]); } function callArraySort(arr){ console.log("Array内置排序:"); //console.log(arr[0]); return Array.prototype.sort.call(arr, compareByArray); //console.log(b[0]); //console.log(b.length); } function compareByArray(a,b){ var i = parseInt(a); var j = parseInt(b); return i-j; } function heapSort(arr){ function maxHeapify(arr, index, len){ var left = getLeft(index); var right = getRight(index); var largest; if(left<len&&arr[left]>arr[index]){ largest = left; }else{ largest = index; } if(right<len&&arr[right]>arr[largest]){ largest = right; } if(largest!=index){ swapElement(arr, largest, index); maxHeapify(arr, largest, len); // 如果改变了位置,递归改变的子树调整 } } function swapElement(arr,index0, index1){ var temp = arr[index0]; arr[index0] = arr[index1]; arr[index1] = temp; } function getLeft(eleIndex){ return 2*eleIndex+1; } function getRight(eleIndex){ return 2*(eleIndex+1); } if(arr.length<=1){ return; } var len = arr.length; for(var i=len/2+1;i>=0;--i){ maxHeapify(arr, i, len); } for(var i=len-1;i>=1;--i){ swapElement(arr, 0, i); maxHeapify(arr, 0, --len); } return arr; } var a = [2,5,3,2,2,8,5,9,4,6,3,1,3,7]; //console.log(bubbleSort(a)); timeIt(bubbleSort, a); a = [2,5,3,2,2,8,5,9,4,6,3,1,3,7]; timeIt(chooseSort, a);//console.log(chooseSort(a)); a = [2,5,3,2,2,8,5,9,4,6,3,1,3,7]; timeIt(quickSort, a);//console.log(quickSort(a)); a = [2,5,3,2,2,8,5,9,4,6,3,1,3,7]; console.log("快速排序不改原参数版:"); timeIt(quickSortArr, a); a = [2,5,3,2,2,8,5,9,4,6,3,1,3,7]; timeIt(callArraySort, a); a = [2,5,3,2,2,8,5,9,4,6,3,1,3,7]; console.log("堆排序:"); timeIt(heapSort, a); var randArr = []; for(var i=0;i<10000; i++){ randArr[i] = Math.floor(Math.random()*100000); } a = randArr.slice(); timeIt(bubbleSort, a) a = randArr.slice(); timeIt(chooseSort, a);//console.log(chooseSort(a)); a = randArr.slice(); timeIt(quickSort, a); a = randArr.slice(); console.log("快速排序不改原参数版:"); timeIt(quickSortArr, a); a = randArr.slice(); timeIt(callArraySort, a); a = randArr.slice(); console.log("堆排序:"); timeIt(heapSort, a);
冒泡跟选择以前容易混淆,是因为不明白怎样叫冒泡,实际上,冒泡就是对整个序列,进行一趟前后元素两两比较的形式,大的数后移(或者小的),这样达到一趟实现了把大的数(或者小的数)移到了尾部,那整一个序列看成尾部朝上,往后一趟序列,从头开始一直到倒数第n个数进行两两比较,犹如关键元素(最大值或者最小值)往上冒泡的样子 ,因此叫冒泡。
而选择排序,则是对序列的每个数,从头开始,针对该位置与剩下的数进行比较, 如果有出现大于(小于)该位置的数,则交换位置,这样每一趟下来,就能确定固定位置该放置的数。 //这个解释感觉跟选择没多大关联,姑且当做是选择位置吧。
那么快速排序,就是直接用效率命名了。为什么快呢?看代码实现 ,while里面还有while,还有递归,感觉不快的样子,关于时间复杂度的问题,我还没嚼透。
快排思想是分而治之,既然一个分散的序列,假设通过比较某个值,分别将大于和小于该数的分到一边,就成了两部分,然后同样的道理,各部分再按切分大小方法再细分成两部分……以此推下去,直到变成了最小的部分就是三个数,a<b<c,这个时候各个细小部分组合起来,明显就是一个排序好的序列了。
最后贴一下代码在chrome console运行的结果,快排最后秒杀全场
2014-06-20 更新: 快排加入另一种实现,利用array的api,不过似乎速度不尽如人意,但是做到了不破坏实参数组的效果,而且不过在元素大的时候还是比冒泡选择强:
1 function quickSortArr(arr){ 2 if(arr.length==1){ 3 return arr; 4 } 5 if(arr.length==0){//这两句if实际上可以写成一句的 6 return []; 7 } 8 var left = [], 9 right = []; 10 var povit = arr[0]; 11 for(var i=1;i<arr.length;i++){ 12 if(arr[i]<=povit){ 13 left.push(arr[i]); 14 }else{ 15 right.push(arr[i]); 16 } 17 } 18 var re = quickSortArr(left).concat([povit],quickSortArr(right)); 19 return re;//这里为了调试才加的变量,实际可以不用 20 }
测试的时候,没留意到不改变原数组的问题,打印出原数组 ,我一度以为是自己写错,囧。好吧,too simple。
又更新,傻了,居然没把Array内置排序也加进去玩玩,不过出现点小插曲,就是sort方法的参数,默认是按元素的编码比较,单位数比较可能没发觉,但是多位数比较就会发现,不设置比较函数的话,10是小于2的,事关10的1确实小于2 = =||
没关系,我们再加个比较函数进去就可以了,如下:(整体代码更新到第一个代码区了。)
function callArraySort(arr){ console.log("Array内置排序:"); return Array.prototype.sort.call(arr, compareByArray); } function compareByArray(a,b){//其实直接return a-b;应该也可以。 var i = parseInt(a); var j = parseInt(b); return i-j; }
一开始没留意,后来查了mdn才意识到。MDN入口>>
贴一下排序结果:似乎内置排序比实际快排快,但比改实参版的慢哦。
新更新,加入了堆排序,实现是参考的这里>>
1 function heapSort(arr){ 2 function maxHeapify(arr, index, len){//对以arr[index]为根的子树的最大堆生成,如果交换了父节点,就再整理被交换的子节点 3 var left = getLeft(index); 4 var right = getRight(index); 5 var largest; 6 if(left<len&&arr[left]>arr[index]){ 7 largest = left; 8 }else{ 9 largest = index; 10 } 11 if(right<len&&arr[right]>arr[largest]){ 12 largest = right; 13 } 14 if(largest!=index){ 15 swapElement(arr, largest, index); 16 maxHeapify(arr, largest, len); // 如果改变了位置,递归改变的子树调整 17 } 18 } 19 function swapElement(arr,index0, index1){//交换位置 20 var temp = arr[index0]; 21 arr[index0] = arr[index1]; 22 arr[index1] = temp; 23 } 24 function getLeft(eleIndex){//获取eleIndex节点的左子节点 25 return 2*eleIndex+1; 26 } 27 function getRight(eleIndex){//获取eleIndex节点的右子节点 以0为起始下标 28 return 2*(eleIndex+1); 29 } 30 if(arr.length<=1){//排序起始,只有一个或者没有的数组,本来就不需要排序 31 return arr; 32 } 33 var len = arr.length; 34 for(var i=len/2+1;i>=0;--i){//首先是对无序的数组进行堆顶树的构造;len/2 + 1,我认为取的是树的最后一层的第一个叶子节点,后续叶子会在其父节点时比较 35 maxHeapify(arr, i, len); 36 } 37 38 for(var i=len-1;i>=1;--i){ 39 swapElement(arr, 0, i); 40 maxHeapify(arr, 0, --len); 41 } 42 return arr; 43 }
看了下,堆排序貌似还比内置排序快一点,比快排轻版块的慢一点:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律