js快速排序算法
算法思路:
1.先从数组中取出一个数作为基准数。
2.分区:比基数大的数全放到它的右边,小于或等于它的数全放到它的左边。
3.再对左右区间重复第二步,直到各区间只有一个数。
快速排序的每一轮处理其实就是将这一轮的基准数归位,直到所有的数都归位为止,排序就结束了。整个算法的处理过程如下:
具体实现:
假设我们对T = [6,1,2,7,9,3,4,5,10,8]数组进行快速排序。
1.将数组第一个元素做为基准数,为6
2.确定哨兵
用两个变量i和j,分别指向序列最左边和最右边。我们为这两个变量起个好听的名字“哨兵i”和“哨兵j”。刚开始的时候让哨兵i指向序列的最左边,让哨兵j指向序列的最右边。
3. 开始探测,并分区
首先哨兵j开始出动。哨兵j一步一步地向左挪动(即j--),直到找到一个小于6的数停下来。接下来哨兵i再一步一步向右挪动(即i++),直到找到一个数大于6的数停下来。最后哨兵j停在了数字5面前,哨兵i停在了数字7面前。
现在交换哨兵i和哨兵j所指向的元素的值。交换之后的序列如下
接下来开始哨兵j继续向左挪动。他发现了4(比基准数6要小,满足要求)之后停了下来。哨兵i也继续向右挪动的,他发现了9(比基准数6要大,满足要求)之后停了下来。此时再次进行交换。
第二次交换结束,“探测”继续。哨兵j继续向左挪动,他发现了3(比基准数6要小,满足要求)之后又停了下来。哨兵i继续向右移动,此时哨兵i和哨兵j相遇了,都走到3面前。说明此时“探测”结束。
将基准数6和3进行交换。交换之后的序列如下:
到此便完成了第一轮分区。此时以基准数6为分界点,6左边的数都小于等于6,6右边的数都大于等于6。
4. 对6两边的数组做递归处理即可。
问题:
为什么要哨兵j先出动?
举个例子,假设让哨兵i先动,到下图这一步。哨兵继续往前走,到9的位置,发现i和j相遇,这时候你就要交换9和6的位置,排序就乱了,大于6的数出现在了左边。原因就是哨兵j停留的位置一定比基准数大,如果让i先走,就有可能在哨兵j的位置相遇,这时交换基准数和相遇位置就会出错。
实现代码:
/** 题目:快速排序算法 思路:两个哨兵,i,j,j从右边找比基数小的,i从左边找比基数大的,然后交换两个目标元素的位置,直到i=j,然后交换i和基数的位置,递归处理。 **/ function quick_sort(arr,from,to){ var i = from; //哨兵i var j = to; //哨兵j var key = arr[from]; //参考值 if(from >= to){ //如果数组只有一个元素 return; }
//一轮探查未结束,因为结束时i=j while(i < j){ while(arr[j] > key && i < j){ //从右边向左找第一个比key小的数,找到或者两个哨兵相碰,跳出循环 j--; } while(arr[i] <= key && i < j){ //从左边向右找第一个比key大的数,找到或者两个哨兵相碰,跳出循环,这里的=号保证在本轮循环结束前,key的位置不变,否则的话跳出循环,交换i和from的位置的时候,from位置的上元素有可能不是key i++; } /** 代码执行道这里,1、两个哨兵到找到了目标值。2、j哨兵找到了目标值。3、两个哨兵都没找到(key是当前数组最小值) **/ if(i < j){ //交换两个元素的位置 var temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } } arr[from] = arr[i]; arr[i] = key; quick_sort(arr,from,i-1); quick_sort(arr,i+1,to); } var arr = [3,3,-5,6,0,2,-1,-1,3]; console.log(arr); quick_sort(arr,0,arr.length-1); console.log(arr);