基础技术

快速排序-三种实现方式

2018-09-25  本文已影响0人  孤帆缘航

概念

      快速排序的基本思想是分治法,选取一个“基准数”作为比较参照,经过排序运算,将数据分为两个规模更小的数据源,其中一个所有的数据都比另一部分的小,然后递归进行操作从而使数据达到有序的状态。

分治法的基本思想是:将原问题分解为若干个规模更小但结构与原问题相似的子问题。递归地解这些子问题,然后将这些子问题的解组合为原问题的解。

快速排序的时间复杂度在最坏情况下是O(N2),平均的时间复杂度是O(N*logN)。快速排序是不稳定的算法。

算法稳定性:假设在数列中存在 a[i] = a[j],若在排序之前,a[i] 在a[j] 前面;并且排序之后,a[i] 仍然在a[j] 前面。则这个排序算法是稳定的!

实现方式

      记录一下三种简单的实现方式,总结以代码+日志的方式展示,通过日志可以清晰的展示出每轮每次的数据操作。前提假设最终的目标排序方式都是升序,可以为了清晰展示出每次具体操作了哪些数据,日志[]中括号内展示的为数组下标,()小括号内展示的为本轮的数据源,""双引号内展示的为本次需要交换的两个值,''单引号内展示的为pre(前后指针)的值

左右指针法

      通俗点说,就是一个指针从右侧依次递减,一个指针从左侧依次递增,当发现符合条件的情况时,交换两个指针指向的值,然后以基准值为分割点,将本次数据源分为两个小的数组,依次递归循环该操作。

      这里需要注意的就是内部循环初始先从右侧开始还是左侧开始,这里先分析下本例当中,要求升序排列,从左侧(最终肯定是小于基准值的值)取默认基准值,当内部循环优先从右侧开始时,每次循环完,右侧指针指向的永远是小于基准值的值(排除已经是升序的情况),这时假如本次没有交换,在循环外右侧指针(等于左侧指针)就可以与基准值的进行交换,最终基准值归位,小于基准值的数都在一侧。
      相应的,我们也可以在内循环时优先从左侧判断,这时在选基准值时就选择右侧的;或者尝试下降序时的初始方式,这里就不赘述了……

public static void quickSort(int left, int right) {
    if (left >= right) {
        return;
    }
        
    int l = left;
    int r = right;
    int target = arrs[l];
        
    while (l < r) {
        while (arrs[r] >= target && l < r) {
            r--;
        }
        while (arrs[l] <= target && l < r) {
            l++;
        }
            
        if (l < r) {
            swap(arrs[l], arrs[r]);
        }
    }
        
    if (l != left) {
        swap(arrs[l], arrs[left]):
    }
        
    quickSort(left, l - 1);
    quickSort(l + 1, right);
}
随机初始数组: [29, 13, 86, 2, 55, 7, 22, 33, ] 
################

第1轮,基准值是29:
交换[2]和[6]位置的值
交换前的数据: [(29, 13, "86", 2, 55, 7, "22", 33), ] 
交换后的数据: [(29, 13, "22", 2, 55, 7, "86", 33), ] 
交换[4]和[5]位置的值
交换前的数据: [(29, 13, 22, 2, "55", "7", 86, 33), ] 
交换后的数据: [(29, 13, 22, 2, "7", "55", 86, 33), ] 
内循环结束,当前l=r=[4],与基准值(下标[0])值交换
交换前的数据: [("29", 13, 22, 2, "7", 55, 86, 33), ] 
交换后的数据: [("7", 13, 22, 2, "29", 55, 86, 33), ] 
第1轮结束

第2轮,基准值是7:
交换[1]和[3]位置的值
交换前的数据: [(7, "13", 22, "2"), 29, 55, 86, 33, ] 
交换后的数据: [(7, "2", 22, "13"), 29, 55, 86, 33, ] 
内循环结束,当前l=r=[1],与基准值(下标[0])值交换
交换前的数据: [("7", "2", 22, 13), 29, 55, 86, 33, ] 
交换后的数据: [("2", "7", 22, 13), 29, 55, 86, 33, ] 
第2轮结束

第3轮,基准值是22:
循环结束,当前l=r=[3],与基准值(下标[2])值交换
交换前的数据: [2, 7, ("22", "13"), 29, 55, 86, 33, ] 
交换后的数据: [2, 7, ("13", "22"), 29, 55, 86, 33, ] 
第3轮结束

第4轮,基准值是55:
交换[6]和[7]位置的值
交换前的数据: [2, 7, 13, 22, 29, (55, "86", "33"), ] 
交换后的数据: [2, 7, 13, 22, 29, (55, "33", "86"), ] 
内循环结束,当前l=r=[6],与基准值(下标[5])值交换
交换前的数据: [2, 7, 13, 22, 29, ("55", "33", 86), ] 
交换后的数据: [2, 7, 13, 22, 29, ("33", "55", 86), ] 
第4轮结束

################
排序最终数组: [2, 7, 13, 22, 29, 33, 55, 86, ] 

填坑法

public static void quickSort(int left, int right) {
    if (left >= right) {
        return;
    }
        
    int l = left;
    int r = right;
    int target = arrs[l];
        
    while (l < r) {
        while (arrs[r] >= target && l < r) {
            r--;
        }
        if (l < r) {
            arrs[l] = arrs[r];
        }
            
        while (arrs[l] <= target && l < r) {
            l++;
        }
        if (l < r) {        
            arrs[r] = arrs[l];
        }
    }
    arrs[l] = target;
    
    quickSort(left, l - 1);
    quickSort(l + 1, right);
}
随机初始数组: [14, 18, 1, 28, 30, 11, 78, 55, ] 
################

第1轮,基准值是14,当前坑位[0]:
交换前的数据: [("14", 18, 1, 28, 30, "11", 78, 55), ] 
交换后的数据: [("11", 18, 1, 28, 30, "11", 78, 55), ] 当前坑位[5],值是11
交换前的数据: [(11, "18", 1, 28, 30, "11", 78, 55), ] 
交换后的数据: [(11, "18", 1, 28, 30, "18", 78, 55), ] 当前坑位[1],值是18
交换前的数据: [(11, "18", "1", 28, 30, 18, 78, 55), ] 
交换后的数据: [(11, "1", "1", 28, 30, 18, 78, 55), ] 当前坑位[2],值是1
内循环结束,准备将基准值补上最后一个坑位 [(11, 1, "1", 28, 30, 18, 78, 55), ] 
交换后的数据: [(11, 1, "14", 28, 30, 18, 78, 55), ] 
第1轮结束

第2轮,基准值是11,当前坑位[0]:
交换前的数据: [("11", "1"), 14, 28, 30, 18, 78, 55, ] 
交换后的数据: [("1", "1"), 14, 28, 30, 18, 78, 55, ] 当前坑位[1],值是1
内循环结束,准备将基准值补上最后一个坑位 [(1, "1"), 14, 28, 30, 18, 78, 55, ] 
交换后的数据: [(1, "11"), 14, 28, 30, 18, 78, 55, ] 
第2轮结束

第3轮,基准值是28,当前坑位[3]:
交换前的数据: [1, 11, 14, ("28", 30, "18", 78, 55), ] 
交换后的数据: [1, 11, 14, ("18", 30, "18", 78, 55), ] 当前坑位[5],值是18
交换前的数据: [1, 11, 14, (18, "30", "18", 78, 55), ] 
交换后的数据: [1, 11, 14, (18, "30", "30", 78, 55), ] 当前坑位[4],值是30
内循环结束,准备将基准值补上最后一个坑位 [1, 11, 14, (18, "30", 30, 78, 55), ] 
交换后的数据: [1, 11, 14, (18, "28", 30, 78, 55), ] 
第3轮结束

第4轮,基准值是30,当前坑位[5]:
内循环结束,准备将基准值补上最后一个坑位 [1, 11, 14, 18, 28, ("30", 78, 55), ] 
交换后的数据: [1, 11, 14, 18, 28, ("30", 78, 55), ] 
第4轮结束

第5轮,基准值是78,当前坑位[6]:
交换前的数据: [1, 11, 14, 18, 28, 30, ("78", "55"), ] 
交换后的数据: [1, 11, 14, 18, 28, 30, ("55", "55"), ] 当前坑位[7],值是55
内循环结束,准备将基准值补上最后一个坑位 [1, 11, 14, 18, 28, 30, (55, "55"), ] 
交换后的数据: [1, 11, 14, 18, 28, 30, (55, "78"), ] 
第5轮结束
################
排序最终数组: [1, 11, 14, 18, 28, 30, 55, 78, ] 

前后指针法

public static void quickSort(int left, int right) {
    if (left >= right) {
        return;
    }
        
    int cur = left;
    int pre = cur - 1;
    int target = arrs[right];
        
    while (cur < right) {
        while (arrs[cur] < target && ++pre != cur) {
            swap(arrs[cur], arrs[pre];
        }
        cur++;
    }
        
    swap(arrs[++pre], arrs[right]);
        
    quickSort(left, pre -1);
    quickSort(pre + 1, right);
}
随机初始数组: [86, 59, 63, 53, 68, 99, 58, 1, ] 
################

第1轮,基准值是1,'pre':[-1],"cur":[0]:
"cur"指针++了: [(86, "59", 63, 53, 68, 99, 58, 1), ] ,'pre':[-1], "cur":[1]
"cur"指针++了: [(86, 59, "63", 53, 68, 99, 58, 1), ] ,'pre':[-1], "cur":[2]
"cur"指针++了: [(86, 59, 63, "53", 68, 99, 58, 1), ] ,'pre':[-1], "cur":[3]
"cur"指针++了: [(86, 59, 63, 53, "68", 99, 58, 1), ] ,'pre':[-1], "cur":[4]
"cur"指针++了: [(86, 59, 63, 53, 68, "99", 58, 1), ] ,'pre':[-1], "cur":[5]
"cur"指针++了: [(86, 59, 63, 53, 68, 99, "58", 1), ] ,'pre':[-1], "cur":[6]
"cur"指针++了: [(86, 59, 63, 53, 68, 99, 58, "1"), ] ,'pre':[-1], "cur":[7]
内循环结束,准备将'++pre'(大于基准值的值)与基准值交换: [("86", 59, 63, 53, 68, 99, 58, "1"), ] 
交换后的数据: [("1", 59, 63, 53, 68, 99, 58, "86"), ] 
第1轮结束

第2轮,基准值是86,'pre':[0],"cur":[1]:
"cur"指针++了: [1, ('59', "63", 53, 68, 99, 58, 86), ] ,'pre':[1], "cur":[2]
"cur"指针++了: [1, (59, '63', "53", 68, 99, 58, 86), ] ,'pre':[2], "cur":[3]
"cur"指针++了: [1, (59, 63, '53', "68", 99, 58, 86), ] ,'pre':[3], "cur":[4]
"cur"指针++了: [1, (59, 63, 53, '68', "99", 58, 86), ] ,'pre':[4], "cur":[5]
"cur"指针++了: [1, (59, 63, 53, '68', 99, "58", 86), ] ,'pre':[4], "cur":[6]
交换前的数据: [1, (59, 63, 53, 68, "99", "58", 86), ] 
交换后的数据: [1, (59, 63, 53, 68, "58", "99", 86), ] 
"cur"指针++了: [1, (59, 63, 53, 68, '58', 99, "86"), ] ,'pre':[5], "cur":[7]
内循环结束,准备将'++pre'(大于基准值的值)与基准值交换: [1, (59, 63, 53, 68, 58, "99", "86"), ] 
交换后的数据: [1, (59, 63, 53, 68, 58, "86", "99"), ] 
第2轮结束

第3轮,基准值是58,'pre':[0],"cur":[1]:
"cur"指针++了: ['1', (59, "63", 53, 68, 58), 86, 99, ] ,'pre':[0], "cur":[2]
"cur"指针++了: ['1', (59, 63, "53", 68, 58), 86, 99, ] ,'pre':[0], "cur":[3]
交换前的数据: [1, ("59", 63, "53", 68, 58), 86, 99, ] 
交换后的数据: [1, ("53", 63, "59", 68, 58), 86, 99, ] 
"cur"指针++了: [1, ('53', 63, 59, "68", 58), 86, 99, ] ,'pre':[1], "cur":[4]
"cur"指针++了: [1, ('53', 63, 59, 68, "58"), 86, 99, ] ,'pre':[1], "cur":[5]
内循环结束,准备将'++pre'(大于基准值的值)与基准值交换: [1, (53, "63", 59, 68, "58"), 86, 99, ] 
交换后的数据: [1, (53, "58", 59, 68, "63"), 86, 99, ] 
第3轮结束

第4轮,基准值是63,'pre':[2],"cur":[3]:
"cur"指针++了: [1, 53, 58, ('59', "68", 63), 86, 99, ] ,'pre':[3], "cur":[4]
"cur"指针++了: [1, 53, 58, ('59', 68, "63"), 86, 99, ] ,'pre':[3], "cur":[5]
内循环结束,准备将'++pre'(大于基准值的值)与基准值交换: [1, 53, 58, (59, "68", "63"), 86, 99, ] 
交换后的数据: [1, 53, 58, (59, "63", "68"), 86, 99, ] 
第4轮结束
################
排序最终数组: [1, 53, 58, 59, 63, 68, 86, 99, ] 

上一篇下一篇

猜你喜欢

热点阅读