* 快 速 排 序 (Quick Sort)*
关键思想
由两端向中间交替搜索直至重合。
时间复杂度
Time Complexity | Value |
---|---|
最优时间复杂度 | O ( n l o g 2 n ) O(nlog_{2}n) O(nlog2n) |
最差时间复杂度 | O ( n 2 ) O(n^{2}) O(n2) |
平均时间复杂度 | O ( n l o g 2 n ) O(nlog_{2}n) O(nlog2n) |
解析:
1)最优时间复杂度:每一轮快速排序所选的关键值正好将序列几乎等分成两部分,经过 l o g 2 n log_{2}n log2n轮划分便可使得所有子表的长度是 1 1 1。因为每轮划分所需的时间与元素个数成正比即 c n cn cn,其中 c c c是常数。
2)最差时间复杂度:每一轮快速排序所选的关键值是序列中最大值或最小值,一共需要经过 n − 1 n-1 n−1轮才可结束。具体而言,即第 i i i轮快速排序比较 n − i + 1 n-i+1 n−i+1次,则共比较 ∑ i = 2 n = n ( n + 1 ) 2 − 1 \displaystyle\sum_{i=2}^{n}=\frac {n(n+1)} 2-1 i=2∑n=2n(n+1)−1次。
3)平均时间复杂度:统计意义而言,每轮快速排序所选择的关键值是最大值或最小值的概率较小。
空间复杂度
Space Complexity | Value |
---|---|
空间复杂度 | O ( n l o g 2 n ) O(nlog_{2}n) O(nlog2n) |
注意:部分资料查阅空间复杂度是 O ( 1 ) O(1) O(1)。
稳定性 ×
解析:
不稳定性:若原序列中数 i i i位于数 j j j前面,且 i = j i=j i=j,整个排序过程结束之后数 i i i可能位于数 j j j后面。
实例:[12 5 4 19 25 1 34 7 10 23 8 5](Java)
public static void main(String[] args) {
// TODO 自动生成的方法存根
System.out.println("请键盘输入整数序列[例如:12 5 4 19 25 1 34 7 10 23 8 5 end]"); // 从键盘接收输入信息
Scanner list = new Scanner(System.in);
ArrayList<Integer> inputArray = new ArrayList<Integer>();
while (list.hasNextInt()) {
inputArray.add(list.nextInt());
} // 输入--非数字--结束输入过程
list.close();
System.out.println("输入:" + inputArray);
Quick_Sort(inputArray, 0, inputArray.size() - 1);
}
public static int k = 1; // 第k轮快速排序
public static void Quick_Sort(ArrayList<Integer> inputArray, int low, int high) {
if (low < high) {
System.out.print("第" + k + "轮" + "快速排序:");
k += 1;
// Case1: Start = KEY
/*Output(inputArray, low); // 关键帧位置
int key = StartEqualKey(inputArray, low, high);*/
// Case2: End = KEY
/*Output(inputArray, high); // 关键帧位置
int key = EndEqualKey(inputArray, low, high); */
int index = (int)(Math.random() * (high - low)) + low; // Random = KEY
Output(inputArray, index);
int value = inputArray.get(index); // 关键值
// Case3: Random = KEY + Case1
/*inputArray.set(index, inputArray.get(low));
inputArray.set(low, value); // 将关键值与起始元素互换位置,即问题转化为Case1
int key = StartEqualKey(inputArray, low, high);*/
// Case4: Random = KEY + Case2
/*inputArray.set(index, inputArray.get(high));
inputArray.set(high, value); // 将关键值与末尾元素互换位置,即问题转化为Case2
int key = EndEqualKey(inputArray, low, high);*/
// Case5: Random = KEY + IMPROVE
System.out.print("<" + value + ">" + "与" + inputArray.get(high) + "交换:");
inputArray.set(index, inputArray.get(high));
inputArray.set(high, value); // 将关键值与末尾元素互换位置
Output(inputArray, high);
int key = RandomEqualKey(inputArray, low, high);
// 递归--左子序列和右子序列
Quick_Sort(inputArray, low, key - 1); // 递归:左子序列
Quick_Sort(inputArray, key + 1, high); // 递归:右子序列
}
}
Case1:Start = KEY(从小到大排序)
步骤:
- 选择序列中起始元素作为关键值;
- 从后往前搜索:若元素值大于等于关键值则保持不变,反之前置;
- 从前往后搜索:若元素值小于等于关键值则保持不变,反之后置;
- 重复2和3直至两端搜索重合。
// Case1: Start = KEY
/** 步骤:
1)选择序列中起始元素作为关键值;
2)从后往前搜索:若元素值大于等于关键值则保持不变,反之前置;
3)从前往后搜索:若元素值小于等于关键值则保持不变,反之后置;
4)重复2)和3)直至两端搜索重合。
*/
public static int StartEqualKey(ArrayList<Integer> inputArray, int low, int high) {
int value = inputArray.get(low); // 起始元素作为关键值
while (low < high) {
// step1:从后往前搜索
while ((inputArray.get(high) >= value) && (low != high)) { // 若元素值大于等于关键值则保持不变
if (inputArray.get(high) == value)
System.out.print(inputArray.get(high) + "=" + value + " → 不交换 → ");
else
System.out.print(inputArray.get(high) + ">" + value + " → 不交换 → ");
Output(inputArray, low);
high -= 1;
}
if ((inputArray.get(high) < value) && (low != high)) { // 若元素值小于关键值则前置
System.out.print(inputArray.get(high) + "<" + value + " → 交换 → ");
inputArray.set(low, inputArray.get(high));
inputArray.set(high, value);
Output(inputArray, high);
low += 1;
}
// step2:从前往后搜索
while ((inputArray.get(low) <= value) && (low != high)) { // 若元素值小于等于关键值则保持不变
if (inputArray.get(low) == value)
System.out.print(inputArray.get(low) + "=" + value + " → 不交换 → ");
else
System.out.print(inputArray.get(low) + "<" + value + " → 不交换 → ");
Output(inputArray, high);
low += 1;
}
if ((inputArray.get(low) > value) && (low != high)) { // 若元素值大于关键值则后置
System.out.print(inputArray.get(low) + ">" + value + " → 交换 → ");
inputArray.set(high, inputArray.get(low));
inputArray.set(low, value);
Output(inputArray, low);
high -= 1;
}
}
System.out.print("End: ");
Output(inputArray, low);
return low; // 返回关键值位置=low
}
Case2:End = KEY(从小到大排序)
步骤:
- 选择序列中末尾元素作为关键值;
- 从前往后搜索:若元素值小于等于关键值则保持不变,反之后置;
- 从后往前搜索:若元素值大于等于关键值则保持不变,反之前置;
- 重复2和3直至两端搜索重合。
// Case2: End = KEY
/** 步骤:
1)选择序列中末尾元素作为关键值;
2)从前往后搜索:若元素值小于等于关键值则保持不变,反之后置;
3)从后往前搜索:若元素值大于等于关键值则保持不变,反之前置;
4)重复2)和3)直至两端搜索重合。
*/
public static int EndEqualKey(ArrayList<Integer> inputArray, int low, int high) {
int value = inputArray.get(high); // 末尾元素作为关键值
while (low < high) {
// step1:从前往后搜索
while ((inputArray.get(low) <= value) && (low != high)) { // 若元素值小于等于关键值则保持不变
if (inputArray.get(low) == value)
System.out.print(inputArray.get(low) + "=" + value + " → 不交换 → ");
else
System.out.print(inputArray.get(low) + "<" + value + " → 不交换 → ");
Output(inputArray, high);
low += 1;
}
if ((inputArray.get(low) > value) && (low != high)) { // 若元素值大于关键值则后置
System.out.print(inputArray.get(low) + ">" + value + " → 交换 → ");
inputArray.set(high, inputArray.get(low));
inputArray.set(low, value);
Output(inputArray, low);
high -= 1;
}
// step2:从后往前搜索
while ((inputArray.get(high) >= value) && (low != high)) { // 若元素值大于等于关键值则保持不变
if (inputArray.get(high) == value)
System.out.print(inputArray.get(high) + "=" + value + " → 不交换 → ");
else
System.out.print(inputArray.get(high) + ">" + value + " → 不交换 → ");
Output(inputArray, low);
high -= 1;
}
if ((inputArray.get(high) < value) && (low != high)) { // 若元素值小于关键值则前置
System.out.print(inputArray.get(high) + "<" + value + " → 交换 → ");
inputArray.set(low, inputArray.get(high));
inputArray.set(high, value);
Output(inputArray, high);
low += 1;
}
}
System.out.print("End: ");
Output(inputArray, high);
return high; // 返回关键值位置=high
}
Case3:Random = KEY【优化Case1】(从小到大排序)
思考:如果先随机选择序列中某一元素作为关键值,再将关键值与起始元素互换位置。
Case4:Random = KEY【优化Case2】(从小到大排序)
思考:如果先随机选择序列中某一元素作为关键值,再将关键值与末尾元素互换位置。
Case5:Random = KEY + IMPROVE(从小到大排序)
步骤:
- 随机选择序列中某一元素作为关键值;
- 将关键值与末尾元素互换位置,形成新的序列;
- 从前往后搜索:若元素值小于等于关键值则保持不变,反之后置(与关键值前 面的元素依次互换位置,而非与关键值互换位置);
- 重复2和3直至从前往后搜索一轮结束;
- 将关键值插入中间位置。
// Case5: Random = KEY + IMPROVE
/** 步骤:
1)随机选择序列中某一元素作为关键值;
2)将关键值与末尾元素互换位置,形成新的序列;
3)从前往后搜索:若元素值小于等于关键值则保持不变,反之则后置(与关键值前面的元素依次互换位置,而非与关键值互换位置);
4)重复2)和3)直至从前往后搜索一轮结束;
5)将关键值插入中间位置。
*/
public static int RandomEqualKey(ArrayList<Integer> inputArray, int low, int high) {
int value = inputArray.get(high); // 随机选择某一元素作为关键值
int locationValue = high; // 关键值位置
while (low < high) {
// 从前往后搜索
while ((inputArray.get(low) <= value) && (low < (high - 1))) { // 若元素值小于等于关键值则保持不变
if (inputArray.get(low) == value)
System.out.print(inputArray.get(low) + "=" + value + " → 不交换 → ");
else
System.out.print(inputArray.get(low) + "<" + value + " → 不交换 → ");
Output(inputArray, locationValue);
low += 1;
}
if ((inputArray.get(low) > value) && (low < (high - 1))) {
// 若元素值大于关键值则后置(与关键值前面的元素依次互换位置,而非与关键值互换位置)
System.out.print(inputArray.get(low) + ">" + value + " → " + inputArray.get(low)
+ "与" + inputArray.get(high - 1) + "位置交换 → ");
int temp = inputArray.get(high - 1);
inputArray.set(high - 1, inputArray.get(low));
inputArray.set(low, temp);
Output(inputArray, locationValue);
high -= 1;
}
// 某一轮快速排序中剩余的最后一个元素与关键值比较
if ((inputArray.get(low) > value) && (low == (high - 1))) { // 当该元素大于关键值时
System.out.print(inputArray.get(low) + ">" + value + " → " + inputArray.get(low)
+ "与<" + value + ">位置交换 → ");
inputArray.set(locationValue, inputArray.get(low));
inputArray.set(low, value);
Output(inputArray, low);
high -= 1;
}
if ((inputArray.get(low) <= value) && (low == (high - 1))) { // 当该元素小于关键值时
if (inputArray.get(low + 1) == value) {
if (inputArray.get(low) == value)
System.out.print(inputArray.get(low) + "=" + value);
else
System.out.print(inputArray.get(low) + "<" + value);
System.out.print(" → " + inputArray.get(low + 1) + "与<" + value + ">位置不交换 → ");
}
else {
if (inputArray.get(low) == value)
System.out.print(inputArray.get(low) + "=" + value);
else
System.out.print(inputArray.get(low) + "<" + value);
System.out.print(" → " + inputArray.get(low + 1) + "与<" + value + ">位置交换 → ");
inputArray.set(locationValue, inputArray.get(low + 1));
inputArray.set(low + 1, value);
}
Output(inputArray, low + 1);
low += 1;
}
}
System.out.print("End: ");
Output(inputArray, high);
return high; // 返回关键值位置=high
}
附录
public static void Output(ArrayList<Integer> inputArray, int location) { // 具体每一步骤细节输出
System.out.print("[");
if (location == inputArray.size() - 1) { // 关键值位于序列末端
for (int num = 0; num < inputArray.size() - 1; num++) {
System.out.print(inputArray.get(num) + ", ");
}
System.out.print("<" + inputArray.get(location) + ">");
}
else { // 关键值位于序列中间(包括起始位置)
for (int num = 0; num < inputArray.size() - 1; num++) {
if (num == location)
System.out.print("<" + inputArray.get(location) + ">, ");
else
System.out.print(inputArray.get(num) + ", ");
}
System.out.print(inputArray.get(inputArray.size() - 1));
}
System.out.println("]");
}