【28】一个无序的序列查找第K大的数

简介: 题目:给定一个无序序列和一个数k,求这个无序序列中第K大的数分析:方案一:最朴素的方法利用快速排序然后找到第k个数,时间复杂度O(nlogn)方案二:根据快速排序中的partition方法,每次可以把一个序列分成两个部分,左边全部小于等于基准,右边的全部大于等于基准。


题目:给定一个无序序列和一个数k,求这个无序序列中第K大的数


分析:

方案一:最朴素的方法利用快速排序然后找到第k个数,时间复杂度O(nlogn)

方案二:根据快速排序中的partition方法,每次可以把一个序列分成两个部分,左边全部小于等于基准,右边的全部大于等于基准。如果partition方法中基准的位置刚好是第k个则可以直接得多这个数,如果大于k说明在左边,如果小于k说明在右边。

              利用这个方法每次可以将序列不断的划分直至找到这第k个数, 因为partition函数的时间复杂度最大为O(n),而我们每次都可以把序列分成一半,因此总的时间复杂度还是O(n)。

              时间复杂度的证明:

              1. 假设总的元素个数有n个,则

                  第一次枚举n个数

                  第二次枚举n/2

                  第三次枚举n/4

                  ...................1

              2. 则总的时间为n+n/2+n/4+....+1 <= 2n,因此时间复杂度为O(n)


举例:

例如无序序列为0 9 -1 6 7 3 5,k为3则第k大的数为6。


实例代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;

//Partition方法
int Partition(int *arrNum, int l, int r){
	//不合法数据 
	if(arrNum == NULL || l > r){
        return -1;
    }
    int mid = (l+r)>>1;
	swap(arrNum[mid], arrNum[r]);
	int leftSeqIndex = l;
	//枚举区间 
	for(int i = l; i < r; i++){
	    if(arrNum[i] < arrNum[r]){
           if(i > leftSeqIndex){
		   		swap(arrNum[i], arrNum[leftSeqIndex]);
   		   }
   		   ++leftSeqIndex;
		}
    } 
    //基准交换回来
	swap(arrNum[leftSeqIndex], arrNum[r]);
	return leftSeqIndex; 
}

//求第k大的数
int GetNumber(int *arrNum, int n, int k){
	//输出-1表示不合法 
	if(arrNum == NULL || n <= 0 || k <= 0 || k > n){
        return -1;
    }
    //第k大的数等价于找第n-k+1小的数
	k = n-k+1;
	int l = 0;
	int r = n-1;
	while(l <= r){
	    int index = Partition(arrNum, l, r);
		if(index+1 == k){ //找到 
            return arrNum[index];
        }
        else if(index+1 < k){ //在右边序列找
		    l = index+1; 
	    }
	    else{ //在左边序列找 
	        r = index-1;
	    }
	}
} 

int main(){
   int arrNum[] = {0,9,-1,6,7,3,5};
   int value = GetNumber(arrNum, 7, 3);
   if(value == -1){
       cout<<"no found number";
   }
   else{
       cout<<value<<endl; 
   }

   getchar();
   return 0;
}

/*
输出
6
*/ 



相关文章
【数据结构和算法】删掉一个元素以后全为 1 的最长子数组
这是力扣的 1493 题,难度为中等,解题方案有很多种,本文讲解我认为最奇妙的一种。又又又是一道滑动窗口的典型例题,可以帮助我们巩固滑动窗口算法。这道题很活灵活现,需要加深对题意的变相理解。给你一个二进制数组nums,你需要从中删掉一个元素。 请你在删掉元素的结果数组中,返回最长的且只包含 1 的非空子数组的长度。 如果不存在这样的子数组,请返回 0 。
125 1
有一个已经排好序的数组。现输入一个数,要求按原来的规律将它插入数组中。
有一个已经排好序的数组。现输入一个数,要求按原来的规律将它插入数组中。
356 4
|
10月前
58.有一个已经排好序的数组。现输入一个数,要求按原来的规律将它插入数组中
58.有一个已经排好序的数组。现输入一个数,要求按原来的规律将它插入数组中
49 0
数据结构和算法面试题:给定一个整数数组 nums,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
数据结构和算法面试题:给定一个整数数组 nums,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
110 0
【C语言刷题】调整奇数偶数顺序、有序序列合并以及有序序列判断
【C语言刷题】调整奇数偶数顺序、有序序列合并以及有序序列判断
80 0
C语言:输入两个升序排列的序列,将两个序列合并为一个有序序列并输出。
描述 输入两个升序排列的序列,将两个序列合并为一个有序序列并输出。 输入描述: 输入包含三行, 第一行包含两个正整数n, m,用空格分隔。n表示第二行第一个升序序列中数字的个数,m表示第三行第二个升序序列中数字的个数。 第二行包含n个整数,用空格分隔。 第三行包含m个整数,用空格分隔。
315 0
C语言:输入两个升序排列的序列,将两个序列合并为一个有序序列并输出。
经典算法详解(11)递归查找数组中的最大值
题目:编写一个程序,用递归的方法实现查找数组中的最大值。 C++实现 1 #include 2 3 using namespace std; 4 //第一种方法是常规方法,不是使用递归,首先将第一个元素的值赋值给max,然后遍历数组, 5 //当遇到超高max的值时将其赋值给max,最...
1924 0
二分查找的平均查找长度详解【转】
来源:http://blog.csdn.net/turne/article/details/50488378 看数据结构书的时候碰上的内容,我自己将它化成关于级数的题,然后自己算的过程,基本就是等比级数和等差级数的混合内容。
3765 0
给定一个字符串,能否最多删除一段连续的一段使得剩下的为“2020”
给定一个字符串,能否最多删除一段连续的一段使得剩下的为“2020”
91 0