【5】快速排序C++完整实现;LeetCode 215. 数组中的第k个最大元素;快速排序partition部分的几种C++实现代码;

0 文章目录

  1. 寻找第k大元素的实现思路;
  2. 快速排序算法分析;
  3. partition部分的两种C++实现代码;
  4. 可运行的完整C++代码(不是在leetcode里的,那里也已经有各类各样的参考答案了),可自定义输入输出进行测试。

1 寻找数组中第k个最大元素

在未排序的数组中找到第 k 个最大的元素。请注意,你须要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不一样的元素。ios

输入: (分别是数组长度、k值。这里与力扣题目要求略有不一样,请本身根据须要酌情修改代码)
6 2
[3,2,1,5,6,4]web

输出:
5算法

实现思路分析:
很容易想到的办法是,给未排序的数组排序,若是是升序排列,假设数组长度为n,那么所求第k个最大元素的值即下标为(n-k)对应的值
选择一个高效的排序算法,写好对应的输入输出函数便可。通常可采用堆排序、快速排序等时间复杂度、空间复杂度都比较低的算法,本文采用快速排序。数组

优化:
容易想到:冒泡排序中,第k大的元素在第k趟就已经排好了,能够直接提取,后面的(n-k)趟实际上是不须要的操做,那么快速排序是否也存在这种“省力”的机会呢?架构

快速排序的思想是:选取一个pivot枢纽元素,比它大的放在右边,比它小的放在左边, 针对左边的一部分,重复上述操做递归。若是这个pivot通过第一轮递归后的位置是正好是(n-k),那么能够直接返回pivot的值即答案。若是(n-k)在pivot下标值的右边,那么只须要递归执行右边部分寻找便可,这样算法的时间复杂度可从 n log n 进一步下降到 nsvg

2 快速排序算法分析

上面思路提及来简单,然而实现起来确实另外一回事了。(哭泣)若是好久不看而后忽然考到真的写不出来。难就难在,每个变量的做用、变量在循环外仍是循环内,如何实现递归、正确终止递归,以及最难的,在不开辟新数组空间的状况下,如何实现(与pivot比较后)大小数的交换。即 partition 部分算法的实现。函数

首先说递归部分,有两种常见的实现方式。一种是单独写partition部分,将其返回值做为递归数组的边界;另外一种是只用一个函数搞定,直接在内部传递pivot下标值递归。
大体实现思路分别以下:测试

第一种:优化

int partition(int arr[], int left, int right)  //找基准数 划分
{ 
 
  
   ......
   //为了方便看清递归架构,此处具体实现省略,后面3再具体讲。
   ......
}

void quick_sort(int arr[], int left, int right)
{ 
 
  
    if (left > right)
        return;
    int j = partition(arr, left, right);
    quick_sort(arr, left, j - 1);
    quick_sort(arr, j + 1, right);
}

第二种:ui

void QuickSort(int array[], int start, int last)
{ 
 
  
    int i = start;
    int j = last;
    int pivot = array[i];
    if (i < j)
    { 
 
  
        ......
        //此处依然省略partition部分的代码
        ......
        //把基准数放到i位置
        array[i] = pivot;
        //递归方法
        QuickSort(array, start, i - 1);
        QuickSort(array, i + 1, last);
    }
}

3 partition部分的两种C++实现代码

利用双指针的思想,从左右两头开始向中间走,边走边和pivot的值比较,左边应该比pivot小,若是遇到比pivot大的就停下来准备交换,右边同理,遇到比pivot小的就中止移动指针。对于具体如何实现交换,主要有两种实现思路:

第一种是:

  1. 先走right,右边找到小于pivot的值时,right指针停下;
    (必定要先走right,由于它遇到比pivot小的值停下,最后 left 和 right 遇到的时候,能够拿两者指向的值与pivot交换,先走right能够保证它必定小于pivot。)
  2. 再走left,左边找到大于pivot的值时,left指针停下;
  3. 此时若是 left < right,交换两者;
  4. 循环上述三步,直到 left 和right 相遇;
  5. 交换当前 left(right)指针指向的位置与初始first位置的数值。

代码以下:

int partition(int *nums, int left, int right)
{ 
 
  
    int pivot = nums[left];
    int first = left;
        while (left < right) { 
 
  
            while (left < right & nums[right] >= pivot) { 
 
  
                right--;
            }
            while (left < right & nums[left] <= pivot) { 
 
  
                left++;
            }
            if(left < right){ 
 
  
                int tem = nums[right];
                nums[right] = nums[left];
                nums[left] = tem;
            };
        }
        nums[first] = nums[left];
        nums[left] = pivot;
        return left;
}

第二种是:

  1. 选取 left 指针指向的值为pivot(首个元素);
  2. 先走 right,遇到比pivot小的值,停下指针,将这个值赋给 left 指针指向的元素;(此时,left 的值已经赋值给了pivot,因此直接覆盖没问题)
  3. 再走 left,遇到比 pivot 大的值,就停下指针。将 left 中的值赋给第2步中 right 指针指向的元素;(第2步中,刚给 left 赋的值是必定小于pivot的)
  4. 循环执行二、3步,直至 left 和 right 指针相遇;
  5. 至此,二、3两步就完成了交换一大一小两个值,而后最开始 0 位置的值存在 pivot中,将 pivot 放到排序以后的枢纽位置,即 left 和 right 指针指向的位置。

代码以下:

int partition(vector<int> &nums, int left, int right) { 
 
  
        int pivot = nums[left];
        while (left < right) { 
 
  
            while (left < right & nums[right] >= pivot) { 
 
  
                right--;
            }
            nums[left] = nums[right];
            while (left < right & nums[left] < pivot) { 
 
  
                left++;
            }
            nums[right] = nums[left];
        }
        nums[left] = pivot;
        return left;
}
4 可运行的完整C++代码

最后附上一份能够直接在编译器 / IDE中执行的C++代码:

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
int partition(int *nums, int left, int right)
{ 
 
  
    int pivot = nums[left];
    int first = left;
        while (left < right) { 
 
  
            while (left < right & nums[right] >= pivot) { 
 
  
                right--;
            }
            while (left < right & nums[left] <= pivot) { 
 
  
                left++;
            }
            if(left < right){ 
 
  
                int tem = nums[right];
                nums[right] = nums[left];
                nums[left] = tem;
            };
        }
        nums[first] = nums[left];
        nums[left] = pivot;
        return left;
}
int theKthNumber(int *nums, int n, int k){ 
 
  
    int left = 0;
    int right = n - 1;
    int target = n - k;
    while (true) { 
 
  
        int p = partition(nums, left, right);
        if (p == target) { 
 
  
            return nums[p];
        } else if (target < p) { 
 
  
            right = p - 1;
        } else { 
 
  
            left = p + 1;
        }
    }
}
int main(){ 
 
  
    int len,k;
    scanf("%d%d", &len, &k);
    int *N = (int *)malloc(len*(sizeof(int)));
    for (int i = 0; i <len ; ++i) { 
 
  
        scanf("%d", &N[i]);
    }
    printf("%d\n",theKthNumber(N, len, k));
    return 0;
}