快速排序
快速排序(Quicksort)是对冒泡排序的一种改进。
它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
简而言之,快排算法,就是我们常说的二分法。 通过基准值,将数字序列 分为 小于临界值的部分,和大于临界值的部分。然后在每一部分继续递归执行分割,直至 原始序列变成有序序列。
关键:
- 基准值是一个序列中随机的一个值,在整个排序过程中,是确定不变的。
- 这种类似于细胞分裂的排序方式,明显是一种递归的思想。因此算法大的骨架,可以采用递归方法,或者栈的回溯来实现。
实现
递归方法实现:
/** 递归方法实现:
*@param data 数据源
*@param startIndex 开始处理的下标 *@param
*/
public static void recursiveSort(int[] data,int startIndex,int endIndex){
if(startIndex<endIndex){
//获取基准元素的位置
int privotIndex= partition(data, startIndex,endIndex);
//根据基准元素的位置,分成两部分序列,继续进行排序
//左半部分
recursiveSort(data,startIndex,privotIndex-1);
//右半部分
recursiveSort(data,privotIndex+1,endIndex);
}
}
用栈实现:
/** 递归方法实现:
*@param data 数据源
*@param startIndex 开始处理的下标 *@param
*/
public static void recursiveSortStatck(int[] data, int startIndex, int endIndex) {
Stack<Map<String, Integer>> stack = new Stack<>();
Map<String, Integer> pair = new HashMap<>();
pair.put("startIndex", startIndex);
pair.put("endIndex", endIndex);
stack.push(pair);
while (!stack.isEmpty()) {
Map<String, Integer> pop = stack.pop();
Integer popStart = pop.get("startIndex");
Integer popEnd = pop.get("endIndex");
//获取基准元素的位置
int privotIndex = partition(data, popStart, popEnd);
if (privotIndex - 1 > popStart) {
Map<String, Integer> startMap = new HashMap<>();
startMap.put("startIndex", popStart);
startMap.put("endIndex", privotIndex - 1);
stack.push(startMap);
}
if (privotIndex + 1 < popEnd) {
Map<String, Integer> endMap = new HashMap<>();
endMap.put("startIndex", privotIndex + 1);
endMap.put("endIndex", popEnd);
stack.push(endMap);
}
}
}
很简单,以上代码已经写出了算法的骨架,现在问题的关键又 转换到 获取基准元素的位置 partition 方法中,因此该方法的目的。
- 使data 数组 ,在 【startIndex,endIndex】在区间内是有序的。
- 在该区间内,返回基准元素的位置。
双边循环法
/**
* 双边循环
* @param datas
* @param startIndex
* @param endIndex
* @return
*/
public static int partition (int[] datas,int startIndex,int endIndex){
//取一个基准值(取第一个)
int standard=datas[startIndex];
//起始位置的游标A
int startCursor=startIndex;
//结束位置的游标B
int endCursor=endIndex;
//当两个游标不重合的时候
while (startCursor!=endCursor){
//从右往左比较,当B 游标对应的值 不小于temp时(该值在temp的右边,),B游标向左移动一位,比较上一位的值
while (datas[endCursor]>=standard&&endCursor>startCursor){
endCursor--;
}
//当从右向左比较时,发现值 C 小于temp 时(该值应该在temp的左边),暂停。开始从左往右遍历
//当A 游标对应的值不大于temp 的时候(该值在temp的左边),A游标向有移动一位,比较下一位值
while (datas[startCursor]<=standard&&startCursor<endCursor){
startCursor++;
}
//当从左往右比较,遇到比temp 大的值 D(该值应该在temp的右边)时候停止移动。
//综上,可知,c 应该应该在基准值的左侧,d 应该在基准值的右侧 ,所以交换 C 与 D 的值,
if(startCursor!=endCursor){
int temp=datas[endCursor];
datas[endCursor]=datas[startCursor];
datas[startCursor]=temp;
}
}
//当 游标 A,B 重合的时候,此时应该是基准值的准确位置, 交换 standard 和 重合位置的值
int tempB=datas[endCursor];
datas[endCursor]=standard;
datas[startIndex]=tempB;
return endCursor;
}
单 边循环法
/**
* 单边循环
* @param datas
* @param startIndex
* @param endIndex
* @return
*/
public static int findCenterNumB(int[] datas,int startIndex,int endIndex){
int standard=datas[startIndex];
//用来标记小于基准元素的边界
int currentCursor=startIndex;
for (int i= startIndex;i<=endIndex;i++){
int data = datas[i];
//当元素小于基准值的话,
if(data<standard){
// 小于基准边界+1
currentCursor++;
//交换游标位置 和遍历到位置的数据
int temp=datas[currentCursor]
datas[currentCursor]=datas[i];
datas[i]=temp;
}
}
//将基准元素交换到游标的位置上。
datas[startIndex]= datas[currentCursor];
datas[currentCursor]=standard;
return currentCursor;
}