• 剑指Offer-快速排序


    剑指Offer上的快速排序的Partition函数与我在数据结构书上学到的不一样,因此就想要探索下这两种不同的处理方式。

    基本思想

    快速排序的基本思想是基于分治法。在待排序表L[1...n]中任取一个元素pivot作为基准,通过一趟排序将待排序表划分为独立的两部分L[1...k-1]和L[k+1...n],使得L[1...k-1]中所有元素小于pivot,L[k+1...n]中所有元素大于或等于pivot,则pivot放在了其最终位置L(k)上。而后递归地对两个子表重复上述过程,直至每部分内只有一个元素或空为止,即所有元素放在了其最终位置上。

    剑指Offer上的Partition实现

    // Partition为分割函数, length表示data数组的长度
    int Partition(int data[], int length, int low, int high) {
        if (data == nullptr || length <= 0 || low < 0 || high >= length) {
            return 1;
        }
        
        // 在[low, high]区间中随机取一个基准值,并将其换到区间最末尾
        int index = RandomInRange(low, high);
        Swap(&data[index], &data[high]);
        
        // small在low的前面一位
        int small = low - 1;
           
        for (index = low; index < high; ++index) {
            if (data[index] < data[high]) {
                // ++small后表示大于基准值的第一个数的下标
                // 如果不存在大于基准值的数,则表示当前比较的数字下标
                ++small;
    
                // 交换后的small表示小于基准值的最后一个数字的下标
                if (small != index) {
                    Swap(&data[index], &data[small]);
                }
            }
        }
        
        // ++small后表示大于基准值的第一个数的下标
        ++small;
        Swap(&data[small], &data[high]);
        
        return small;
        
    }
    

    快速排序其它函数实现如下:

    
    // 生成一个随机数,范围在[low,high),是一个前闭后开的区间
    int RandomInRange(int low, int high) {
        return rand() % (high - low) + low;
    }
    
    // 交换两个整数
    void Swap(int* left, int* right) {
        int temp  = *left;
        *left = *right;
        *right = temp;
    }
    
    // 快速排序函数
    void QuickSort(int data[], int length, int low, int high) {
        if (low == high) {
            return;
        }
        
        int index = Partition(data, length, low, high);
        
        if (index > low) {
            QuickSort(data, length, low, index - 1);
        }
        
        if (index < high) {
            QuickSort(data, length, index + 1, high);
        }
    }
    
    // 数据打印
    void PrintData(int data[], int length) {
        for (int i = 0; i < length; ++i) {
            std::cout << data[i] << " ";
        }
        
        std::cout << std::endl;
    }
    
    int main() {
        // 以系统时间作为随机函数种子
        srand((unsigned)time(nullptr));
    
        int const length = 10;
        
        int data[length];
    
        // 生成随机数
        for (int i = 0; i < length; ++i) {
            data[i] = RandomInRange(1, length*length);
        }
        
        // 打印生成的随机数
        PrintData(data, length);
        
        // 调用快排函数进行排序
        QuickSort(data, length, 0, length - 1);
      
        // 打印快速排序后的数
        PrintData(data, length);
        
        return 0;
        
    }
    

    初始顺序为{49, 38, 65, 97, 76, 13, 27},基准值为49的数据的第一趟快速排序的实现如下所示:

    步骤 small index
    0 49 38 65 97 76 13 27
    1 27 38 65 97 76 13 49
    2 0 0 27 38 65 97 76 13 49
    3 1 1 27 38 65 97 76 13 49
    4 1 2 27 38 65 97 76 13 49
    5 1 3 27 38 65 97 76 13 49
    6 1 4 27 38 65 97 76 13 49
    7 2 5 27 38 13 97 76 65 49
    8 3 6 27 38 13 49 76 65 97

    第一趟排序完之后基准值49之前的{27, 38, 13}都比49小,基准值49之后的{76, 65, 97}都比49大。

    一般教材上的Partition实现

    // Partition为分割函数
    int Partition(int data[], int low, int high) {
    
        // 将当前表中第一个元素设为基准值,对表进行划分
        int pivot = data[low]; 
    
        // 循环跳出条件
        while (low < high) {
            while (low < high && data[high] >= pivot) {
                --high;
            }
    
            // 将比基准值小的元素移动到左端
            data[low] = data[high];
    
            while (low < high && data[low] <= pivot) {
                ++low;
            }
    
            // 将比基准值大的元素移动到右端
            data[high] = data[low];
        }
    
        // 基准元素存放到最终位置
        data[low] = pivot;
    
        // 返回存放基准值的最终位置
        return low;
    }
    

    快速排序其它函数实现如下:

    void QuickSort(int data[], int low, int high) {
        // 递归跳出条件
        if (low < high) {
            // 将data[low...high]一分为二,pivotpos是基准值
            int pivotpos = Partition(data, low, high);
    
            // 对左子表进行递归排序
            QuickSort(data, low, pivotpos - 1);
    
            // 对右子表进行递归排序
            QuickSort(data, pivotpos + 1, high);
        }
    }
    

    初始顺序为{49, 38, 65, 97, 76, 13, 27},基准值为49的数据的第一趟快速排序的实现如下所示:

    步骤 low high
    0 0 6 49 38 65 97 76 13 27
    1 2 6 27 38 65 97 76 13 27
    2 2 6 27 38 65 97 76 13 65
    3 2 5 27 38 13 97 76 13 65
    4 3 5 27 38 13 97 76 97 65
    5 3 3 27 38 13 49 76 97 65

    第一趟排序完之后基准值49之前的{27, 38, 13}都比49小,基准值49之后的{76, 97, 65}都比49大。

    总结

    可以看出,上述这两种思想导致的运行过程是不同的,前者是用small指代小于标准元素的最右位置,在遍历过程中进行交换操作来保证small左边都是小于标准元素的元素,而后者是用low,high分别表示小于和大于基准值的集合边界位置,所以后者更浅显易懂。


    个人主页:

    www.codeapes.cn

  • 相关阅读:
    当虚拟空间(主机)不支持301时,该怎样重定向域名
    总结高权重论坛
    一个错
    layui树形框架
    命令模式
    《编写有效用例》读书笔记2
    jieba安装与简单使用
    list正序倒序排列
    每日博客
    每日博客
  • 原文地址:https://www.cnblogs.com/codeapes666/p/12089185.html
Copyright © 2020-2023  润新知