参考资料算法
3 1 4 1 5 9 2 6 5 3
1 1 2 3 3 4 5 5 6
下面我就只讲解1和2步骤, 而在1,2中,关键在于如何实现“划分”数组
划分的过程有三个关键点:“基准元素”, “左游标” 和“右游标”。数据结构
【注意】这里你可能会问: 在咱们制定的规则里, 左游标先扫描和右游标先扫描有区别吗? (若是你这样想的话就和我想到一块去了...嘿嘿),由于就上图而言,两种状况下一趟排序中两个游标相遇的位置是不一样的(通常而言,除非相遇位置的下方的元素恰好和基准元素相同):dom
6 1 2 5 4 3 9 7 10 8
1 2 5 4 3 6 9 7 10 8
// 交换两个数组元素 private static void exchange(int [] a , int i, int j) { int temp = a[i]; a[i] = a[j]; a[j] = temp; }
private static int partition (int[] a, int low, int high) { int i = low, j = high+1; // i, j为左右扫描指针 PS: 思考下为何j比i 多加一个1呢? int pivotkey = a[low]; // pivotkey 为选取的基准元素(头元素) while(true) { while (a[--j]>pivotkey) { if(j == low) break; } // 右游标左移 while(a[++i]<pivotkey) { if(i == high) break; } // 左游标右移 if(i>=j) break; // 左右游标相遇时候中止, 因此跳出外部while循环 else exchange(a,i, j) ; // 左右游标未相遇时中止, 交换各自所指元素,循环继续 } exchange(a, low, j); // 基准元素和游标相遇时所指元素交换,为最后一次交换 return j; // 一趟排序完成, 返回基准元素位置 }
private static void sort (int [] a, int low, int high) { if(high<= low) { return; } // 终止递归 int j = partition(a, low, high); // 调用partition进行切分 sort(a, low, j-1); // 对上一轮排序(切分)时,基准元素左边的子数组进行递归 sort(a, j+1, high); // 对上一轮排序(切分)时,基准元素右边的子数组进行递归 }
while (a[--j]>pivotkey) { ... }
先将右游标左移一位,而后判断指向的数组元素和基准元素pivotkey比较大小, 若是该元素大于基准元素, 那么循环继续,j再次减1,右游标再次左移一位...... (循环体能够看做是空的)函数
if(i>=j) break;
从i < j到 i == j 表明了“游标未相遇”到“游标相遇”的过分过程,此时跳出外部循环, 切分已接近完成,紧接着经过 exchange(a, low, j) 交换基准元素和相遇游标所指元素的位置, low是基准元素的位置(头部元素), j是当前两个游标相遇的位置性能
while (a[--j]>pivotkey) { if(j == low) break; } // 右游标左移
中,当随着右游标左移,到j = low + 1的时候,有 a[--j] == pivotkey为true(二者都是基准元素),自动跳出了while循环,因此就不须要在循环体里再判断 j == low 了测试
int i = low, j = high+1
结合下面两个While循环中的判断条件:优化
while (a[--j]>pivotkey) { ... } while (a[++i]<pivotkey) { ... }
可知道, 左游标 i 第一次自增的时候, 跳过了对基准元素 a[low] 所执行的 a[low] < pivotkey判断, 这是由于在咱们当前的算法方案里,基准元素和左游标初始所指的元素是同一个,因此没有执行a[low]>pivotke这个判断的必要。因此跳过( 一开始a[low] == pivotkey,若是执行判断那么一开始就会跳出内While循环,这显然不是咱们但愿看到的)ui
sort(a, low, j-1); sort(a, j+1, high);
进行下一轮递归,设置j -1 和j + 1 是由于上一轮基准元素的位置已是有序的了,不要再归入下一轮递归里编码
public class QuickSort { // 交换两个数组元素 private static void exchange(int [] a , int i, int j) { int temp = a[i]; a[i] = a[j]; a[j] = temp; } private static int partition (int[] a, int low, int high) { int i = low, j = high+1; // i, j为左右扫描指针 PS: 思考下为何j比i 多加一个1呢? int pivotkey = a[low]; // pivotkey 为选取的基准元素(头元素) while(true) { while (a[--j]>pivotkey) { if(j == low) break; } // 右游标左移 while(a[++i]<pivotkey) { if(i == high) break; } // 左游标右移 if(i>=j) break; // 左右游标相遇时候中止, 因此跳出外部while循环 else exchange(a,i, j) ; // 左右游标未相遇时中止, 交换各自所指元素,循环继续 } exchange(a, low, j); // 基准元素和游标相遇时所指元素交换,为最后一次交换 return j; // 一趟排序完成, 返回基准元素位置 } private static void sort (int [] a, int low, int high) { if(high<= low) { return; } // 当high == low, 此时已经是单元素子数组,天然有序, 故终止递归 int j = partition(a, low, high); // 调用partition进行切分 sort(a, low, j-1); // 对上一轮排序(切分)时,基准元素左边的子数组进行递归 sort(a, j+1, high); // 对上一轮排序(切分)时,基准元素右边的子数组进行递归 } public static void sort (int [] a){ //sort函数重载, 只向外暴露一个数组参数 sort(a, 0, a.length - 1); } }
public class Test { public static void main (String [] args) { int [] array = {4,1,5,9,2,6,5,6,1,8,0,7 }; QuickSort.sort(array); for (int i = 0; i < array.length; i++) { System.out.print(array[i]); } } }
01124556789
if(high<= low) { return; }
if(high<= low + M) { Insertion.sort(a,low, high) return; } // Insertion表示一个插入排序类
private static void sort (int [] a, int low, int high) { if(high<= low + 10) { Insertion.sort(a,low, high) return; } // Insertion表示一个插入排序类 int j = partition(a, low, high); // 调用partition进行切分 sort(a, low, j-1); // 对上一轮排序(切分)时,基准元素左边的子数组进行递归 sort(a, j+1, high); // 对上一轮排序(切分)时,基准元素右边的子数组进行递归 }
public static void sort (int [] a){ StdRandom.shuffle(a) // 外部导入的乱序算法,打乱数组的分布 sort(a, 0, a.length - 1); }
private static int getRandom (int []a, int low, int high) { int RdIndex = (int) (low + Math.random()* (high - low)); // 随机取出其中一个数组元素的下标 exchange(a, RdIndex, low); // 将其和最左边的元素互换 return a[low]; } private static int partition (int[] a, int low, int high) { int i = low, j = high+1; // i, j为左右扫描指针 PS: 思考下为何j比i 多加一个1呢? int pivotkey = getRandom (a, low, high); // 基准元素随机化 while(true) { while (a[--j]>pivotkey) { if(j == low) break; } // 右游标左移 while(a[++i]<pivotkey) { if(i == high) break; } // 左游标右移 if(i>=j) break; // 左右游标相遇时候中止, 因此跳出外部while循环 else exchange(a,i, j) ; // 左右游标未相遇时中止, 交换各自所指元素,循环继续 } exchange(a, low, j); // 基准元素和游标相遇时所指元素交换,为最后一次交换 return j; // 一趟排序完成, 返回基准元素位置 }
package mypackage; public class QuickSort { // 交换两个数组元素 private static void exchange(int [] a , int i, int j) { int temp = a[i]; a[i] = a[j]; a[j] = temp; } // 选取左中右三个元素,求出中位数, 放入数组最左边的a[low]中 private static int selectMiddleOfThree(int[] a, int low, int high) { int middle = low + (high - low)/2; // 取得位于数组中间的元素middle
if(a[low]>a[high]) { exchange(a, low, high); //此时有 a[low] < a[high] } if(a[middle]>a[high]){ exchange(a, middle, high); //此时有 a[low], a[middle] < a[high] } if(a[middle]>a[low]) { exchange(a, middle, low); //此时有a[middle]< a[low] < a[high] } return a[low]; // a[low]的值已经被换成三数中的中位数, 将其返回 } private static int partition (int[] a, int low, int high) { int i = low, j = high+1; // i, j为左右扫描指针 PS: 思考下为何j比i 多加一个1呢? int pivotkey = selectMiddleOfThree( a, low, high); while(true) { while (a[--j]>pivotkey) { if(j == low) break; } // 右游标左移 while(a[++i]<pivotkey) { if(i == high) break; } // 左游标右移 if(i>=j) break; // 左右游标相遇时候中止, 因此跳出外部while循环 else exchange(a,i, j) ; // 左右游标未相遇时中止, 交换各自所指元素,循环继续 } exchange(a, low, j); // 基准元素和游标相遇时所指元素交换,为最后一次交换 return j; // 一趟排序完成, 返回基准元素位置 } private static void sort (int [] a, int low, int high) { if(high<= low) { return; } // 当high == low, 此时已经是单元素子数组,天然有序, 故终止递归 int j = partition(a, low, high); // 调用partition进行切分 sort(a, low, j-1); // 对上一轮排序(切分)时,基准元素左边的子数组进行递归 sort(a, j+1, high); // 对上一轮排序(切分)时,基准元素右边的子数组进行递归 } public static void sort (int [] a){ //sort函数重载, 只向外暴露一个数组参数 sort(a, 0, a.length - 1); } }
while(a[++i]<pivotkey) { if(i == high) break; }
咱们只要尝试把这一做用交给a[++i]<pivotkey去完成, 不就能够把 if(i == high) break; 给去掉了吗?
public class QuickSort { // 交换两个数组元素 private static void exchange(int [] a , int i, int j) { int temp = a[i]; a[i] = a[j]; a[j] = temp; } //将原数组里最大的元素移到最右边, 构造“哨兵” private static void Max(int [] a) { int max = 0; for(int i = 1; i<a.length;i++) { if(a[i]>a[max]) { max = i; } } exchange(a, max, a.length -1); } private static int partition (int[] a, int low, int high) { int i = low, j = high+1; // i, j为左右扫描指针 PS: 思考下为何j比i 多加一个1呢? int pivotkey = a[low]; // pivotkey 为选取的基准元素(头元素) while(true) { while (a[--j]>pivotkey) { } // 空的循环体 while(a[++i]<pivotkey) { } // 空的循环体 if(i>=j) break; // 左右游标相遇时候中止, 因此跳出外部while循环 else exchange(a,i, j) ; // 左右游标未相遇时中止, 交换各自所指元素,循环继续 } exchange(a, low, j); // 基准元素和游标相遇时所指元素交换,为最后一次交换 return j; // 一趟排序完成, 返回基准元素位置 } private static void sort (int [] a, int low, int high) { if(high<= low) { return; } // 当high == low, 此时已经是单元素子数组,天然有序, 故终止递归 int j = partition(a, low, high); // 调用partition进行切分 sort(a, low, j-1); // 对上一轮排序(切分)时,基准元素左边的子数组进行递归 sort(a, j+1, high); // 对上一轮排序(切分)时,基准元素右边的子数组进行递归 } public static void sort (int [] a){ //sort函数重载, 只向外暴露一个数组参数 Max(a); // 将原数组里最大元素移到最右边, 构造“哨兵” sort(a, 0, a.length - 1); } }
package mypackage; public class Quick3way { public static void exchange(int [] a , int i, int j) { int temp = a[i]; a[i] = a[j]; a[j] = temp; } public static void sort (int [] a, int low, int high) { if(low>=high) { return; } int lt = low, gt = high, i =low+1; int v = a[low]; while(i<=gt) { int aValue = a[i]; if(aValue>v) { exchange(a, i, gt--); } else if(aValue<v) { exchange(a, i++, lt++); } else{ i++; } } sort(a, low, lt-1); sort(a, gt+1, high); } public static void sort (int [] a) { sort(a, 0, a.length - 1); } }