时间复杂度为O(n)的选择问题

1. 问题描述

特别简单的问题,从一组数组中选出第k大的元素A[i],最容易想到的办法时间复杂度为O(n^2),考虑快排中的划分法(分治思想)

2.试用快排中的划分法

选择的基准元素默认是数组的第一个。

图1 复杂度较高的划分法伪代码(来源:国科大刘老师PPT)

对应代码:

// 试用划分法int PartSelect(vector<int>& A, int n, int k) {int m, r, j;m = 1; r = n + 1;A[n + 1] = 0;while (true) {j = r;partition(A,m,j);if (k = j) return j; // 返回j,当前数组的元素A[j]是第j小的元素else if (k < j) r = j; // j是新的下标上界else {m = j + 1;k = k - j; // j+1是新的下标下界}}}// 但是这样每次分治迭代只会减少1的复杂度,最坏复杂度(n2)

3. 改进后的选取中位数的划分法

其他划分法主要过程如下图所示:

图1 《算法导论 》p123

改进后的选择问题的划分法:改变划分元素,使每次划分规模减半,将A划分为r(如r=5)个一组,共有n/5(向上取整)个组(取n/5个中位),每组找一个中位数,把它们放到集合M中,可使用排序法。

4. 代码实现

talk is cheap,show me the code.

代码的一些解释,选用的排序算法是改进后的插入排序(使用二分查找去插),复杂度是O(nlogn),但是排序的元素是各个组的,总复杂度就是另外一回事来了。也可使用归并排序,在下面的代码中都有实现。

目录3的算法描述中选取各组中位数组成一个新的组,看起来很简单,但是下面实现的代码中没有增加新的数组,都在一个数组中进行。具体表现为将选取的中位数通通聚集到数组的前面。再排序,选出最终的,通过交换元素位置实现。

在调试时,发现最开始是3组,递归到最后是1组,所以加了一些判断,代码看起来有点复杂和乱,暂时不管了~能力有限见谅,有错误敬请指正~

//选择问题
//问题描述:在一个数组A[1..n]中寻找第k小元素t,并将其存放于位置k,即A[k]=t。// 关键词:分治#include <iostream>
using namespace std;
#include<algorithm>
#include <vector>class SelectPartitionClass
{
public://选择问题// 快排 划分元素int partition(vector<int> &arr,int startIndex, int endIndex) { // 被划分的数组是arr[m,p-1],//选定作划分的数组元素(基准元素)是v=arr[m]int pivot = arr[startIndex];//取第一个元素当做基准元素int left = startIndex;int right = endIndex;    while (left!=right) {while (left<right&&arr[right]>pivot) {right--;// 控制right指针左移}while (left<right && arr[left]<=pivot) {left++;// 从右向左查}if (left < right) { // 交换A[i]和A[p]的位置swap(arr[left], arr[right]);}}//pivot和指针重合点交换arr[startIndex] = arr[left];arr[left] = pivot; // 划分元素在位置p,把本来在2位置的划分元素挪到4去。也相当于做一个交换return left; // 返回基准元素的位置}// 试用划分法int PartSelect(vector<int>& A, int n, int k) {int m, r, j;m = 1; r = n + 1;A[n + 1] = 0;while (true) {j = r;partition(A,m,j);if (k = j) return j; // 返回j,当前数组的元素A[j]是第j小的元素else if (k < j) r = j; // j是新的下标上界else {m = j + 1;k = k - j; // j+1是新的下标下界}}}// 但是这样每次分治迭代只会减少1的复杂度,最坏复杂度(n2)int midposition = 0;// 改进后的选择问题的划分法:改变划分元素,使每次划分规模减半int Select(vector<int> &arr,int m,int p,int k) {// 输入arr[0,...n],k是置顶要输出的第k小的元素// 将A划分为r(如r=5)个一组,共有n/5(向上取整)个组(取n/5个中位)// 每组找一个中位数,把它们放到集合M中// 可使用排序法,时间复杂度为O(c)// 参数说明:返回一个i值,使得A[i]是A[m..p]中第k小的元素   int n, i, j;int r = 5;       if (p - m + 1 <= r) {insertSort(arr, m, p);return m+k; // m+k是这个的中位数}while(true){n = p - m + 1;int t = floor( n / r);                                // 新的边界,0-m + floor(n / r) if (t==1) {// 当只有1组时,交换后arr[1]就是中位数了,直接去划分就好for (int i = 1; i <= 1; i++) {//计算中间值int right = m + i * r - 1;                       insertSort(arr, m + (i - 1) * r, right); // i代表这是第几组// 将中间值收集到A[m..p]的前部swap(arr[m + i - 1], arr[m + (i - 1) * r + (int)ceil(r / 2)]);           }}else{for (int i = 1; i <= floor(n / r) + 1; i++) {//计算中间值int right = m + i * r - 1;if (i == floor(n / r) + 1) {right = n - 1;insertSort(arr, m + (i - 1) * r, right); // i代表这是第几组// 最后一组元素个数n mod 5                swap(arr[m + i - 1], arr[m + (i - 1) * r + (int)ceil((n % 5) / 2)]);}else {insertSort(arr, m + (i - 1) * r, right); // i代表这是第几组// 将中间值收集到A[m..p]的前部swap(arr[m + i - 1], arr[m + (i - 1) * r + (int)ceil(r / 2)]);}}int newright = m + floor(n / r) ;// 分成了3组就有3个中位数[0,2]再次进行排序,选最终的中位数// 为什么要将ceil(floor(n/r)/2 中值的中值 作为k传入=>选好的中位数现在排在最前面,还需要再中位数中选出中位数j=Select(arr,m, newright,ceil(floor(n/r)/2));// 左边界m,右边界m+floor(n/r)-1,中值的中值// 这里期望j返回的是什么呢?返回中位数的位置?swap(arr[m],arr[j]);}midposition= partition(arr,m, p); // 以最终选出的中位数arr[m],这里中位数已经被收集到前面了作为参照数,将数组s划分为两个部分,使得m所处的位置比左侧的数目大1if (midposition-m+1==k) {// 如果划分后左侧的元素的个数+1后刚好等于k,那么这个中位数就是要找的return arr[midposition];           }else if(midposition -m+1>k) {p = midposition - 1;}else {k = k - (midposition -m+1);m = midposition + 1;}}}
private:// 二分法 查找插入位置int searchInsert(vector<int>& Array, int left, int right, int target){/* if ((right-left)==1) {int position;position = Array[left] > Array[right] ? left-1 : right-1;return position;}*/while (left <= right){int mid = left + (right - left) / 2;if (Array[mid] == target){return mid;}else if (Array[mid] < target)//如果中间值偏小,将初端右移{left = mid + 1;}else //中间值偏大则终端左移{right = mid - 1;}}return left;//如果目标值target比最大值还大,最后left>right,left=nums.size()//如果比最小值还小,最后left值为0,因为right会先减为-1后与left交换,left=0且比right大,跳出循环且符合题意,目标值变为初端}// 插入排序,时间复杂度是n^2,但是使用二分查找优化后的插入排序复杂度是O(nlogn)void insertSort(vector<int>& arr, int start, int end){int length = end - start + 1;//将arr[]按升序排列for (int j = 1; j < length; j++){//利用二分法查找要插入的位置,计算机算法与分析第三章课后练习第二.1题//int key = arr[j+ times *5];int key = arr[j + start];int k = searchInsert(arr, start, j + start, key);//找到需要插入的位置k// 交换位置if (k != j + start) {for (int i = j + start; i > k; i--) {arr[i] = arr[i - 1];}arr[k] = key;}}}/*归并排序的复杂度直接是O(nlogn)*/void merge(vector<int>& arr, int low, int mid, int high) {//merge函数将两部分,(分别排好序的子数组),合并成一个,再存于原数组arrint h, i, j, k;h = low; i = low; j = mid + 1; // h,j作为游标,i指向temp存放数组元素的游标/* c++中 利用vector创建一个带有变量的二维数组!!! */int len = arr.size();vector<int> temp(11); // 这个temp数组是local的,长度为数组arr长度while (h <= mid && j <= high) { // 当两个集合都没有取尽时if (arr[h] <= arr[j]) {temp[i] = arr[h];h = h + 1;}else {temp[i] = arr[j];j++;}i++;}if (h > mid) { // 当第一子组元素被 取尽,而第二组元素未被取尽时for (int k = j; k <= high; k++) {temp[i] = arr[k];i++;}}else { // 当第2组取尽,第一组未被取尽时for (int k = h; k <= mid; k++) {temp[i] = arr[k];i++;}}for (int k = low; k <= high; k++) {//将临时数组temp中的元素再赋值给arrarr[k] = temp[k];}}void mergeSort(vector<int>& arr, int low, int high) {// arr[low..high]是一个全程数组,含有high-low+1个待排序元素if (low < high) {int mid = low+(high-low) / 2;mergeSort(arr, low, mid);mergeSort(arr, mid + 1, high);merge(arr, low, mid, high);}}
};//Ques20200925
int main() {SelectPartitionClass cla = SelectPartitionClass();vector<int> arrs = { -3,2,5,8,4,3,9,10,6,7,1,0,14 };int a=cla.Select(arrs, 0,12,6);cout << a;return 0;
}

5.运行效果

 -3,2,5,8,4,3,9,10,6,7,1,0,14

-3,0,1,2,3,4,5,6,7,8,9,10,14

第6大的元素是4.

测试了下,好像没太多问题...希望= =

6. 复杂度分析

图2 复杂度分析(来源:国科大刘老师PPT)

算法题目打卡:Ques20201007相关推荐

  1. 本专栏所有力扣题目的目录链接, 刷算法题目的顺序(由易到难/面试频率)/注意点/技巧, 以及思维导图源文件问题(持续更新中)

    这篇文章为本专栏所有力扣题目提供目录链接, 更加方便读者根据题型或面试频率进行阅读, 此外也会介绍我在刷题过程中总结的刷算法题目的顺序/注意点/技巧, 最后说下文中出现的思维导图源文件的问题 和 打卡 ...

  2. PTA数据结构与算法题目集6-4 6-3 6-8

    PTA数据结构与算法题目集(中文) 6-4 链式表的按序号查找 ElementType FindKth( List L, int K ){int index = 0;while(L){++index; ...

  3. PTA数据结构与算法题目集 6-9 二叉树的遍历

    PTA数据结构与算法题目集(中文) 6-9 二叉树的遍历 void InorderTraversal( BinTree BT ){if(BT==NULL)return;if(BT->Left){ ...

  4. leetcode的回溯算法题目用这个模板解题,一网打尽,so easy!!!

    " 这是本人第 46 篇原创博文,每周至少两篇更新,谢谢赏脸阅读文章 这一篇文章来讲解一下如何做leetcode回溯算法题目,这一段时间我把leetcode上面的回溯算法的题目都刷了个遍,发 ...

  5. 微软面试中简单的算法题目(转)

    微软面试中简单的算法题目(转) (说明:这些题就不是什么花样了,考的是你的基础知识怎么样.再聪明而没有实学的人都将会被这些题所淘汰.)  1.链表和数组的区别在哪里? ANSWER 主要在基本概念上的 ...

  6. 算法题目中经典问题(易错点)

    算法题目中经典问题.易错点 (一).二维数组的传参问题 1.方法一:形参为二维数组并给定第二维长度 2.方法二:形参为指向数组的指针并给出数组长度 3.二维数组定义为全局变量 (二).多组测试控制台数 ...

  7. PTA 数据结构与算法题目集(中文)

    一:数据结构与算法题目(中文版) 7-2 一元多项式的乘法与加法运算 (20 分) 7-3 树的同构 (25 分) 7-4 是否同一棵二叉搜索树 (25 分) 7-6 列出连通集 (25 分)(详解) ...

  8. 数据结构与算法题目集7-32——哥尼斯堡的“七桥问题”

    我的数据结构与算法题目集代码仓:https://github.com/617076674/Data-structure-and-algorithm-topic-set 原题链接:https://pin ...

  9. HihoCoder上网络流算法题目建模总结

    经过了几天的学习和做题,我利用刘汝佳书上的网络流算法模板完成了HihoCoder上的几个网络流算法,HihoCoder可能还会继续更新网络流算法,所以我也会接着总结. 这个主要是对网络流算法的建模做分 ...

  10. java 完美洗牌,(2)数组相关算法题目

    数组是最简单的数据结构,占据连续内存并且按顺序存储. 以下是与数组有关的算法题目. (1)查询数组中重复数字 算法思路:(1)利用hash表,没有便放进去,有就返回(Java中HashMap存数字都是 ...

最新文章

  1. 关于angular的$resource中的isArray属性问题
  2. Stanford UFLDL教程 实现主成分分析和白化
  3. boost::hana::map_用法的测试程序
  4. Linux shell 条件判断if
  5. 节后的第一个周末,来领取一个Ipad吧!真香!
  6. Showing multiple lines on a single chart in Node-RED
  7. 特斯拉市值超过波音 成美国市值最高工业公司
  8. Django 分页组件替换自定义分页
  9. php如何生成伪静态url,thinkphp控制器(三) 伪静态及URL生成
  10. [SinGuLaRiTy] NOIP2017 提高组
  11. 和root权限挣扎过的一些记录
  12. Python2.X和Python3.X文件对话框、下拉列表的不同
  13. asp.net中使用水晶报表 ---pull
  14. 台式计算机时间不能同步,台式电脑时间同步不了?一分钟就能快速解决
  15. 初步了解PS“时间轴”
  16. 微型计算机主频一般为,【单选题】目前使用的微型计算机的主频一般为________。 A. 2.6GHz B. 256MHz C. 2.3THz D. 900Hz...
  17. 我所理解的JS ~~运算符
  18. 2.18 小红书的表情文案一键生成,原来这么简单【玩赚小红书】
  19. 2023年软件设计师中级(考点分析+复习笔记+历年真题+电子版课本)
  20. 一代互联网人,知了天命后

热门文章

  1. ddr4 dqs 频率_你好,请问你知道DDR3中是DQS是什么意思吗
  2. python教程五(imput和while)
  3. excel冻结指定行和列
  4. 京东自动抢购茅台脚本
  5. 双11后,第一批买家秀曝光……
  6. 华为手机pc模式机型_华为 P20 搭载的 PC 模式厉害了,刷新你对手机的认知
  7. Excel辅助“校验”
  8. 日子计算(结构+函数)
  9. 推荐 5 个免费高品质的图片资源网站
  10. 腾讯服务器微信分身,腾讯禁止微信双开应用,为什么还有那么多人“冒死”双开微信呢?...