精华内容
下载资源
问答
  • 一组数据如果有特别大的数或特别小的数时,一般用中位数 一组数据比较多(20个以上),范围比较集中,一般用众数 其余情况一般还是平均数比较精确 一、联系与区别:  1、平均数是通过计算得到的,因此它会因...

    原文链接:http://www.360doc.com/content/18/0717/09/57858800_771067787.shtml

    个人理解,说简单点:
    一组数据中如果有特别大的数或特别小的数时,一般用中位数
    一组数据比较多(20个以上),范围比较集中,一般用众数
    其余情况一般还是平均数比较精确

    一、联系与区别:

      1、平均数是通过计算得到的,因此它会因每一个数据的变化而变化。

      2、中位数是通过排序得到的,它不受最大、最小两个极端数值的影响.中位数在一定程度上综合了平均数和中位数的优点,具有比较好的代表性。部分数据的变动对中位数没有影响,当一组数据中的个别数据变动较大时,常用它来描述这组数据的集中趋势。另外,因中位数在一组数据的数值排序中处中间的位置,

      3、众数也是数据的一种代表数,反映了一组数据的集中程度.日常生活中诸如“最佳”、“最受欢迎”、“最满意”等,都与众数有关系,它反映了一种最普遍的倾向.

    二、平均数、中位数和众数它们都有各自的的优缺点.

    平均数:
    (1)需要全组所有数据来计算;
    (2)易受数据中极端数值的影响.

    中位数:
    (1)仅需把数据按顺序排列后即可确定;
    (2)不易受数据中极端数值的影响.

    众数:
    (1)通过计数得到;
    (2)不易受数据中极端数值的影响

    关于“中位数、众数、平均数”这三个知识点的理解,我简单谈谈自己的认识和理解。
    ⒈众数。
    一组数据中出现次数最多的那个数据,叫做这组数据的众数。
    ⒉众数的特点。
    ①众数在一组数据中出现的次数最多;②众数反映了一组数据的集中趋势,当众数出现的次数越多,它就越能代表这组数据的整体状况,并且它能比较直观地了解到一组数据的大致情况。但是,当一组数据大小不同,差异又很大时,就很难判断众数的准确值了。此外,当一组数据的那个众数出现的次数不具明显优势时,用它来反映一组数据的典型水平是不大可靠的。
    3.众数与平均数的区别。
    众数表示一组数据中出现次数最多的那个数据;平均数是一组数据中表示平均每份的数量。
    4.中位数的概念。
    一组数据按大小顺序排列,位于最中间的一个数据(当有偶数个数据时,为最中间两个数据的平均数)叫做这组数据的中位数。
    5.众数、中位数及平均数的求法。
    ①众数由所给数据可直接求出;②求中位数时,首先要先排序(从小到大或从大到小),然后根据数据的个数,当数据为奇数个时,最中间的一个数就是中位数;当数据为偶数个时,最中间两个数的平均数就是中位数。③求平均数时,就用各数据的总和除以数据的个数,得数就是这组数据的平均数。
    6.中位数与众数的特点。
    ⑴中位数是一组数据中唯一的,可能是这组数据中的数据,也可能不是这组数据中的数据;
    ⑵求中位数时,先将数据有小到大顺序排列,若这组数据是奇数个,则中间的数据是中位数;若这组数据是偶数个时,则中间的两个数据的平均数是中位数;
    ⑶中位数的单位与数据的单位相同;
    ⑷众数考察的是一组数据中出现的频数;
    ⑸众数的大小只与这组数的个别数据有关,它一定是一组数据中的某个数据,其单位与数据的单位相同;
    (6)众数可能是一个或多个甚至没有;
    (7)平均数、众数和中位数都是描述一组数据集中趋势的量。
    7.平均数、中位数与众数的异同:
    ⑴平均数、众数和中位数都是描述一组数据集中趋势的量;
    ⑵平均数、众数和中位数都有单位;
    ⑶平均数反映一组数据的平均水平,与这组数据中的每个数都有关系,所以最为重要,应用最广;
    ⑷中位数不受个别偏大或偏小数据的影响;
    ⑸众数与各组数据出现的频数有关,不受个别数据的影响,有时是我们最为关心的数据。
    8.统计量。
    平均数、众数和中位数都叫统计量,它们在统计中,有着广泛的应用。
    9.举手表决法。
    在生活中,往往会有由多数人来从众多答案中选择一个的情形,一般都利用“举手表决”方式来解决问题。即在统计出所有提议及相应票数的情况下,看各票数的众数是否超过总票数的一半,如果众数超过了总票数的一半,选择的最终答案就是这个众数。如果出现了双众数(两个众数),可对这两个众数采用抓阄、抽签或投掷硬币等办法选出最终的答案。
    10.平均数、众数和中位数三种统计数据在生活中的意义。
    平均数说明的是整体的平均水平;众数说明的是生活中的多数情况;中位数说明的是生活中的中等水平。
    11.如何通过平均数、众数和中位数对表面现象到背景材料进行客观分析。
    在个别的数据过大或过小的情况下,“平均数”代表数据整体水平是有局限性的,也就是说个别极端数据是会对平均数产生较大的影响的,而对众数和中位数的影响则不那么明显。所以,这时要用众数活中位数来代表整体数据更合适。即:如果在一组相差较大的数据中,用中位数或众数作为表示这组数据特征的统计量往往更有意义。

    算数平均数、中位数与众数——统计量背后的故事

    现代经济社会的数字化程度越来越高,我们会发现在我们生活的这个世界里充斥着各种各样的数字。人们在描述事物或过程时,人们也已经习惯性的偏好于接受数字信息以及对于各种数字的整理和分析。因此,社会经济统计越发的重要。统计学一定是基于现实经济社会发展的需要牵引而不断发展的。在运用统计方法、观察统计数字时不能仅仅看到数字,更要看到数字背后的故事。其实统计学作为一门工具能够帮助我们更为深刻的理解抽象的社会经济现象。当我们仔细发掘其中涵义就会发现,其实自然科学与社会科学并不是相隔千里,它们有着很多地方可以相互的对应,存在普遍而深刻的联系。
    笔者曾在为一些本科学生讲授统计学而准备教案时,产生了一些似乎有些勉强,但的确可以训练思维的想法。下面以对于如何理解“算数平均数、中位数与众数”之间的关系为例说一说统计量背后的故事。这三个统计量都是用来描述样本集中趋势的,但三者描述的机制和所表达出来的内涵有不小的区别。算数平均数这样一个统计量反映了样本内所有个体的信息,尽管反映的程度因个体在整体中所占比重不同而不同。在政治过程中,算数平均数与完全的平均主义、严格的每人一票、“全民公投”等相对应。中位数指的在是从小到大排序之后的样本序列中,位于中间的数值,它并不能反映所有样本个体的信息,仅仅考虑的是在相对位置上中间的样本的信息。在一个社会中,按照财富和社会地位进行排序位于中间位置的是中产阶级。中产阶级的意见受到重视的社会是一个较为稳定的社会,是一个有了较高发展程度的社会。众数指的则是在样本中出现次数做多的个体。很明显,在政治过程中这是与“少数服从多数”相对应的。出现次数最多的个体信息被表达出来,其他个体的所有信息完全被忽视。那个个体票数最多,它的利益得以实现,而少数人的利益则不能够得到保证。这恰恰证明了所谓民主的局限之一,即“多数人对少数人的暴政”。
    在一个社会里,完全的平均主义会使人们失去进取的动力,“全民公投”的成本极高并且也不能保证个体表达出其真实意愿,因此这并不是理想的政治过程。在改革开放之前实行的计划经济体制最终走下了历史舞台也正是因为我们清楚地认识到了这样的问题;我们反对台湾当局针对台湾是否独立实行“全民公投”也正是基于这一点。那么美国式的民主,即“少数服从多数”是否理想呢?民主是有局限性的,如此的政治过程不能够保护少数人的利益,正是其重要的缺陷之一。况且如果需要政府来保障那些不能通过政治过程实现自身利益的个体,成本极高。相对而言,使中产阶级的利益得以表达,将会形成一个稳定的社会结构,市较为理想的政治过程。人们会有不断进取的心态使自己成为中产阶级,同时最富裕的阶层也受到了一定限制,从而不会凭借其财富垄断社会的公共资源,为整个社会提供了一套阶层之间相互流动的渠道和机制。当然,如此的政治过程仍然是具有一定局限性的。比如仍然会有部分弱势群体的利益得不到保护。但是,相对于“少数服从多数”的政治过程,政府出面保护弱势群体的成本将低得多了。那么我们能不能为社会提供一个最为理想的政治过程呢,哪怕那仅仅是一种理想呢?或许可以。在统计学中,最理想的情况是反映集中趋势的三个统计量相互重合,即算数平均数、中位数和众数相等。这种情况下的社会结构分布可以被看作为正态分布。中产阶级的在数量上占整体的多数,即为富裕与极贫困者皆为少数;中产阶级通过民主的政治过程表达出自身的利益取向;平均看来整个社会在一个较高的发展水平上运行。

    教参上说了他们三者的联系

    “重视理解平均数、中位数与众数的联系与区别。
    描述一组数据的集中趋势,可以用平均数、中位数和众数,它们有各自不同的特点。
    平均数应用最为广泛,用它作为一组数据的代表,比较可靠和稳定,它与这组数据中的每一个数据都有关系,能够最为充分地反映这组数据所包含的信息,在进行统计推断时有重要的作用;但容易受到极端数据的影响。
    中位数在一组数据的数值排序中处于中间的位置,故其在统计学分析中也常常扮演着“分水岭”的角色,人们由中位数可以对事物的大体趋势进行判断和掌控。
    众数着眼于对各数据出现的频数的考察,其大小仅与一组数据中的部分数据有关,当一组数据中有不少数据多次重复出现时,它的众数往往是我们关心的一种统计量。
    在这部分知识的教学中,要注意讲清上述三个量的联系与区别。使学生知道它们都是描述一组数据集中趋势的统计量,但描述的角度和适用范围有所不同,在具体的问题中究竟采用哪种统计量来描述一组数据的集中趋势,要根据数据的特点及我们所关心的问题来确定。”

    有个顺口溜 分析数据平中众,比较接近选平均,相差较大看中位,频数较大用众数;
       所有数据定平均,个数去除数据和,即可得到平均数;大小排列知中位;
       整理数据顺次排,单个数据取中问,双个数据两平均;频数最大是众数

    展开全文
  • 寻找两个有序数组的中位数(附上种解法)

    千次阅读 多人点赞 2019-12-06 18:23:32
    •解法 •结束 •写前面 这道题比较经典,我当时做的时候,想出了两种解决方案,不过总感觉算法不够优美,所以找到了另一种我觉得非常精妙的解法,利用了K最小数的思想去解决这道题,很值得讲一下,不知道K...

    目录

    •写在前面

    •题目

    •解法一

    •解法二

    •解法三

    •结束


    •写在前面

    这道题比较经典,我当时在做的时候,想出了两种解决方案,不过总感觉算法不够优美,所以找到了另一种我觉得非常精妙的解法,利用了K最小数的思想去解决这道题,很值得讲一下,不知道K最小数的,可以参考我另一篇文章,点击这里跳转就可以了。下面我废话不多说,直接开始讲解。

    •题目

    首先先把题目呈上来,具体题目如下:

    给定两个大小为 m 和 n 的有序数组 nums1 和 nums2。请你找出这两个有序
    数组的中位数,并且要求算法的时间复杂度为 O(log(m + n))。你可以假
    设 nums1 和 nums2 不会同时为空。
    测试用例:
    nums1 = [1, 3]
    nums2 = [2]
    测试结果:则中位数是 2.0

    •解法一

    怎么说解法一呢,其实可以把解法一当成暴力解法,对这道题进行硬解。但是如果仔细看要求的话就会发现,题目里要求了时间复杂度是O(log(m + n)),所以这种解法不能满足要求,不过因为我当时先看到的是没有O(log(m + n))要求的题目,所以就直接暴力解决了(对于有些题目,暴力不失为一种省力的好办法,小声bb)。这个解法没什么好讲的,我就大概说一下思路。

    具体思路就是,将两个数组使用归并的思想,进行整合,然后求解。(ps:别说直接合起来,然后排序,你要是这样做的话,算法老师的棺材板都压不住),直接上代码。当然啦,下面代码直接整合成了一个数组,其实我们也可以不用真的整合数组,只要通过不断比对,然后记录值就可以了(注意边界问题)。这种解法的时间复杂度是O(m + n)哦。

    public static double Test4S2(int[] nums1, int[] nums2) {
            List<Integer> array = new ArrayList<>(10);
    
            int size1 = nums1.length;
            int size2 = nums2.length;
            int index1 = 0;
            int index2 = 0;
    
            while(index1 != size1 || index2 != size2){
                if(index1 == size1){
                    for (int i = index2; i < size2; i++){
                        array.add(nums2[i]);
                    }
                    break;
                }else if (index2 == size2){
                    for (int i = index1; i < size1; i++){
                        array.add(nums1[i]);
                    }
                    break;
                }
                array.add(nums1[index1] < nums2[index2] ? nums1[index1] : nums2[index2]);
                if(nums1[index1] < nums2[index2]){
                    if(index1 < size1)
                        index1++;
                }
                else{
                    if(index2 < size2) index2++;
                }
            }
    
            int sizeArray = array.size() / 2;
            if(array.size() % 2 == 0) return (array.get(sizeArray - 1) +         array.get(sizeArray)) / 2.00;
            else return array.get(sizeArray);
        }

    •解法二

    在考虑题目时间复杂度O(log(m + n))的情况下,我换了一种解法,这种解法也没啥精妙的地方,主要是对中位数的概念理解好了,就容易想到。首先很多时候,我们看到了log一般就要想到二分法。

    具体思路,中位数的概念其实可以理解为,将数组整体分为两个部分,一边大于等于中位数,一边小于等于中位数。那么在这道题目中两个有序数组,我们可以将两个数组并排画一条线,这条线能正好划分左右两个部分,而我们的任务就是要找到这条线。可能形容起来比较吃力,话不多说,上图看。

    就是为了找到i,j连起来的线,能够正好将两个数组划分成左右两个部分,划分好了之后,只需要记录左边最大的值和右边最小的值,通过这两个值求解中位数就可以了,研究之后你就会发现i和j的关系是

    i + j = m - i  + n - j

    因为左边部分和右边部分的数量要相等,有了这个之后,我们只要最开始随机确定i(直接在小数组中间取i),然后通过左右移动i(j移动的方向和i相反)找到我们要的那条线。不过一定要小心边界问题,对于边界要进行处理好哦。代码如下

    public static double Test4S3(int[] nums1, int[] nums2) {
            int m = nums1.length;
            int n = nums2.length;
            if (m > n) {
                return Test4S3(nums2,nums1); // 保证 m <= n
            }
            int iMin = 0, iMax = m;
            while (iMin <= iMax) {
                int i = (iMin + iMax) / 2;
                int j = (m + n + 1) / 2 - i;
                if (j != 0 && i != m && nums2[j-1] > nums1[i]){ // i 需要增大
                    iMin = i + 1;
                }
                else if (i != 0 && j != n && nums1[i-1] > nums2[j]) { // i 需要减小
                    iMax = i - 1;
                }
                else { // 达到要求,并且将边界条件列出来单独考虑
                    int maxLeft = 0;
                    if (i == 0) { maxLeft = nums2[j-1]; }
                    else if (j == 0) { maxLeft = nums1[i-1]; }
                    else { maxLeft = Math.max(nums1[i-1], nums2[j-1]); }
                    if ( (m + n) % 2 == 1 ) { return maxLeft; } // 奇数的话不需要考虑右半部分
    
                    int minRight = 0;
                    if (i == m) { minRight = nums2[j]; }
                    else if (j == n) { minRight = nums1[i]; }
                    else { minRight = Math.min(nums2[j], nums1[i]); }
    
                    return (maxLeft + minRight) / 2.0; //如果是偶数的话返回结果
                }
            }
            return 0.0;
        }

    •解法三

    最激动人心的解法来了,我当时看到这个解法的时候,感叹连连,解法非常的精妙有容易理解,利用第K小数的思想,使用递归二分法进行解决。

    具体思路,中位数其实就是第(总长度)/2小的数(奇偶我就不多说了,为了方便我就直接用奇数了),以为两个数组都是有序的,所以我们每次通过循环排除K的一半,直到最后找到K。看图看图,已这个例子为例,我们要找的K是7.

    这个时候K/2等于3,然后我们比较两个数组的第三个位置上的数,就可以排除小的那一边的三个数一定不是第K小,然后我们这个时候将排除的数标记。

    这个时候因为我们已经排除了3个数,接下来我们只要在新的两个数组中,找到K-3也就是第4小的数就可以了,同样的,将K比较K一半为止的数,重复如此

    所以我们采用递归的思路,为了防止数组长度小于 k/2,所以每次比较 min(k/2,len(数组) 对应的数字,把小的那个对应的数组的数字排除,将两个新数组进入递归,并且 k 要减去排除的数字的个数。递归出口就是当 k=1 或者其中一个数字长度是 0 了。直接上代码。

        private static int kMinNum(int start1, int end1, int[] nums1, int start2, int end2, int[] nums2, int k){
            int len1 = end1 - start1 + 1;
            int len2 = end2 - start2 + 1;
            if(len1 > len2) return kMinNum(start2, end2, nums2, start1, end1, nums1, k);
            if(len1 == 0) return nums2[start2 + k - 1];
            if(k == 1) return Math.min(nums1[start1], nums2[start2]);
            int i = start1 + Math.min(len1, k / 2) - 1;
            int j = start2 + Math.min(len2, k / 2) - 1;
    
            if(nums1[i] > nums2[j]){
                return kMinNum(start1, end1, nums1, j + 1, end2, nums2,k - (j - start2 + 1));
            }else{
                return kMinNum(i + 1, end1, nums1, start2, end2, nums2, k - (i - start1 + 1));
            }
        }
    
        public static double Test4S1(int[] nums1, int[] nums2) {
            int n = nums1.length;
            int m = nums2.length;
            int left = (n + m + 1) / 2;
            int right = (n + m + 2) / 2;
            return (kMinNum(0, n - 1, nums1, 0, m - 1, nums2, left) + kMinNum(0, n - 1, nums1, 0, m - 1, nums2, right)) * 0.5;
        }

    •结束

    第三种解法真的感觉相当的精妙,这种思路很值得去吃透理解。

    展开全文
  • bfptr算法(即中位数中位数算法)

    万次阅读 多人点赞 2018-08-25 22:35:16
    BFPRT算法是解决从n个数中选择第k大或第k小的这个经典问题的著名算法,但很多人并不了解其细节。本文将首先介绍求解这个第k小数字问题的几个思路,然后重点介绍最坏情况下复杂度仍然为O(n)的BFPRT算法。 一 ...

    BFPRT算法是解决从n个数中选择第k大或第k小的数这个经典问题的著名算法,但很多人并不了解其细节。本文将首先介绍求解这个第k小数字问题的几个思路,然后重点介绍在最坏情况下复杂度仍然为O(n)的BFPRT算法。

    一 基本思路

    关于选择第k小的数字有许多方法,其效率和复杂度各不一样,可以根据实际情况进行选择。

    1. 将n个数排序(比如快速排序或归并排序),选取排序后的第k个数,时间复杂度为O(nlogn)。使用STL函数sort可以大大减少编码量。
    2. 将方法1中的排序方法改为线性时间排序算法(如基数排序或计数排序),时间复杂度为O(n)。但线性时间排序算法使用限制较多,不常使用。
    3. 维护一个k个元素的最大堆,存储当前遇到的最小的k个数,时间复杂度为O(nlogk)。这种方法同样适用于海量数据的处理。
    4. 部分的选择排序,即把最小的放在第1位,第二小的放在第2位,直到第k位为止,时间复杂度为O(kn)。实现非常简单。
    5. 部分的快速排序(快速选择算法),每次划分之后判断第k个数在左右哪个部分,然后递归对应的部分,平均时间复杂度为O(n)。但最坏情况下复杂度为O(n^2)。
    6. BFPRT算法,修改快速选择算法的主元选取规则,使用中位数的中位数作为主元,最坏情况下时间复杂度为O(n)

    二 快速选择算法

    快速选择算法就是修改之后的快速排序算法,前面快速排序的实现与应用这篇文章中讲了它的原理和实现。

    其主要思想就是在快速排序中得到划分结果之后,判断要求的第k个数是在划分结果的左边还是右边,然后只处理对应的那一部分,从而达到降低复杂度的效果。

    在快速排序中,平均情况下数组被划分成相等的两部分,则时间复杂度为T(n)=2*T(n/2)+O(n),可以解得T(n)=nlogn。
    在快速选择中,平均情况下数组也是分成相等的两部分,但是只处理其中一部分,于是T(n)=T(n/2)+O(n),可以解得T(n)=O(n)。

    但是两者在最坏情况下的时间复杂度均为O(n^2),出现在每次划分之后左右总有一边为空的情况下。为了避免这个问题,需要谨慎地选取划分的主元,一般的方法有:

    1. 固定选择首元素或尾元素作为主元。
    2. 随机选择一个元素作为主元。
    3. 三数取中,选择三个数的中位数作为主元。一般是首尾数,再加中间的一个数或者随机的一个数。

    ============================================================

    通常,我们需要在一大堆数中求前K大的数,或者求前K小的。比如在搜索引擎中求当天用户点击次数排名前10000的热词;在文本特征选择中求IF-IDF值按从大到小排名前K个的等等问题,都涉及到一个核心问题,即TOP-K问题

    通常来说,TOP-K问题可以先对所有数进行快速排序,然后取前K大的即可。但是这样做有两个问题。

    (1)快速排序的平均复杂度为,但最坏时间复杂度为,不能始终保证较好的复杂度。

    (2)我们只需要前K大的,而对其余不需要的数也进行了排序,浪费了大量排序时间。

    除这种方法之外,堆排序也是一个比较好的选择,可以维护一个大小为K的堆,时间复杂度为

    我们的目的是求前K大的或者前K小的元素,实际上有一个比较好的算法,叫做BFPTR算法,又称为中位数的中位数算法,它的最坏时间复杂度为,它是由Blum、Floyd、Pratt、Tarjan、Rivest 提出。

    该算法的思想是修改快速选择算法的主元选取方法,提高算法在最坏情况下的时间复杂度。我们先来看看快速排序是如何进行的。

    一趟快速排序的过程如下

    (1)先从序列中选取一个数作为基准数。

    (2)将比这个数大的数全部放到它的右边,把小于或者等于它的数全部放到它的左边。

    一趟快速排序也叫做Partion,即将序列划分为两部分,一部分比基准数小,另一部分比基准数大,然后再进行分治过程,因为每一次Partion不一定都能保证划分得很均匀,所以最坏情况下的时间复杂度不能保证总是为

    对于Partion过程,通常有两种方法:

    (1)两个指针从首尾向中间扫描(双向扫描)

     这种方法可以用挖坑填数来形容,比如

     

        

     

        初始化:i = 0; j = 9; pivot = a[0];

        现在a[0]保存到了变量pivot中了,相当于在数组a[0]处挖了个坑,那么可以将其它的数填到这里来。从j开始向前找一个小于或者等于pivot的数,即将a[8]填入a[0],但a[8]又形成了一个新坑,再从i开始向后找一个大于pivot的数,即a[3]填入a[8],那么a[3]又形成了一个新坑......

        就这样,直到i==j才停止,最终得到结果如下

     

        

     

        上述过程就是一趟快速排序

     

    代码:

    #include <iostream>
    #include <string.h>
    #include <stdio.h>
    #include <algorithm>
    #include <time.h>
    
    using namespace std;
    const int N = 10005;
    
    int Partion(int a[], int l, int r)
    {
    	int i = l;
    	int j = r;
    	int pivot = a[l];
    	while (i < j)
    	{
    		while (a[j] >= pivot && i < j)
    			j--;
    		a[i] = a[j];
    		while (a[i] <= pivot && i < j)
    			i++;
    		a[j] = a[i];
    	}
    	a[i] = pivot;
    	return i;
    }
    
    void QuickSort(int a[], int l, int r)
    {
    	if (l < r)
    	{
    		int k = Partion(a, l, r);
    		QuickSort(a, l, k - 1);
    		QuickSort(a, k + 1, r);
    	}
    }
    
    int a[N];
    
    int main()
    {
    	int n;
    	while (cin >> n)
    	{
    		for (int i = 0; i < n; i++)
    			cin >> a[i];
    		QuickSort(a, 0, n - 1);
    		for (int i = 0; i < n; i++)
    			cout << a[i] << " ";
    		cout << endl;
    	}
    	return 0;
    }

    (2)两个指针一前一后逐步向前扫描(单向扫描)

    代码:

    #include <iostream>  
    #include <string.h>  
    #include <stdio.h>  
       
    using namespace std;  
    const int N = 10005;  
       
    int Partion(int a[], int l, int r)  
    {  
        int i = l - 1;  
        int pivot = a[r];  
        for(int j = l; j < r; j++)  
        {  
            if(a[j] <= pivot)  
            {  
                i++;  
                swap(a[i], a[j]);  
            }  
        }  
        swap(a[i + 1], a[r]);  
        return i + 1;  
    }  
       
    void QuickSort(int a[], int l, int r)  
    {  
        if(l < r)  
        {  
            int k = Partion(a, l, r);  
            QuickSort(a, l, k - 1);  
            QuickSort(a, k + 1, r);  
        }  
    }  
       
    int a[N];  
       
    int main()  
    {  
        int n;  
        while(cin >> n)  
        {  
            for(int i = 0; i < n; i++)  
                cin >> a[i];  
            QuickSort(a, 0, n - 1);  
            for(int i = 0; i < n; i++)  
                cout << a[i] << " ";  
            cout << endl;  
        }  
        return 0;  
    }  

    实际上基于双向扫描的快速排序要比基于单向扫描的快速排序算法快很多。接下来,我们学习BFPTR算法的原理。

    BFPTR算法中,仅仅是改变了快速排序Partion中的pivot值的选取,在快速排序中,我们始终选择第一个元素或者最后一个元素作为pivot,而在BFPTR算法中,每次选择五分中位数的中位数作为pivot,这样做的目的就是使得划分比较合理,从而避免了最坏情况的发生。算法步骤如下:

    (1)将输入数组的个元素划分为组,每组5个元素,且至多只有一个组由剩下的个元素组成。

    (2)寻找个组中每一个组的中位数,首先对每组的元素进行插入排序,然后从排序过的序列中选出中位数。

    (3)对于(2)中找出的个中位数,递归进行步骤(1)和(2),直到只剩下一个数即为这个元素的中位数,找到中位数后并找到对应的下标

    (4)进行Partion划分过程,Partion划分中的pivot元素下标为

    (5)进行高低区判断即可。

    本算法的最坏时间复杂度为,值得注意的是通过BFPTR算法将数组按第K小(大)的元素划分为两部分,而这高低两部分不一定是有序的,通常我们也不需要求出顺序,而只需要求出前K大的或者前K小的。

    另外注意一点,求第K大就是求第n-K+1小,这两者等价。TOP K问题在工程中有重要应用,所以很有必要掌握。

    代码:

    #include <iostream>  
    #include <string.h>  
    #include <stdio.h>  
    #include <time.h>  
    #include <algorithm>  
       
    using namespace std;  
    const int N = 10005;  
       
    int a[N];  
       
    //插入排序  
    void InsertSort(int a[], int l, int r)  
    {  
        for(int i = l + 1; i <= r; i++)  
        {  
            if(a[i - 1] > a[i])  
            {  
                int t = a[i];  
                int j = i;  
                while(j > l && a[j - 1] > t)  
                {  
                    a[j] = a[j - 1];  
                    j--;  
                }  
                a[j] = t;  
            }  
        }  
    }  
       
    //寻找中位数的中位数  
    int FindMid(int a[], int l, int r)  
    {  
        if(l == r) return a[l];  
        int i = 0;  
        int n = 0;  
        for(i = l; i < r - 5; i += 5)  
        {  
            InsertSort(a, i, i + 4);  
            n = i - l;  
            swap(a[l + n / 5], a[i + 2]);  
        }  
       
        //处理剩余元素  
        int num = r - i + 1;  
        if(num > 0)  
        {  
            InsertSort(a, i, i + num - 1);  
            n = i - l;  
            swap(a[l + n / 5], a[i + num / 2]);  
        }  
        n /= 5;  
        if(n == l) return a[l];  
        return FindMid(a, l, l + n);  
    }  
       
    //寻找中位数的所在位置  
    int FindId(int a[], int l, int r, int num)  
    {  
        for(int i = l; i <= r; i++)  
            if(a[i] == num)  
                return i;  
        return -1;  
    }  
       
    //进行划分过程  
    int Partion(int a[], int l, int r, int p)  
    {  
        swap(a[p], a[l]);  
        int i = l;  
        int j = r;  
        int pivot = a[l];  
        while(i < j)  
        {  
            while(a[j] >= pivot && i < j)  
                j--;  
            a[i] = a[j];  
            while(a[i] <= pivot && i < j)  
                i++;  
            a[j] = a[i];  
        }  
        a[i] = pivot;  
        return i;  
    }  
       
    int BFPTR(int a[], int l, int r, int k)  
    {  
        int num = FindMid(a, l, r);    //寻找中位数的中位数  
        int p =  FindId(a, l, r, num); //找到中位数的中位数对应的id  
        int i = Partion(a, l, r, p);  
       
        int m = i - l + 1;  
        if(m == k) return a[i];  
        if(m > k)  return BFPTR(a, l, i - 1, k);  
        return BFPTR(a, i + 1, r, k - m);  
    }  
       
    int main()  
    {  
        int n, k;  
        scanf("%d", &n);  
        for(int i = 0; i < n; i++)  
            scanf("%d", &a[i]);  
        scanf("%d", &k);  
        printf("The %d th number is : %d\n", k, BFPTR(a, 0, n - 1, k));  
        for(int i = 0; i < n; i++)  
            printf("%d ", a[i]);  
        puts("");  
        return 0;  
    }  
       
    /** 
    10 
    72 6 57 88 60 42 83 73 48 85 
    5 
    */  

    关于本算法最坏时间复杂度为的证明可以参考《算法导论》9.3节,即112页,有详细分析。

     

    原文链接:https://blog.csdn.net/wyq_tc25/article/details/51801885

    展开全文
  • C / C++ 组合三位数

    千次阅读 2018-03-27 13:20:16
     将0到9这十个数字分成个3位数,要求第一个3位数,正好是第二个3位数的1/2,是第个3位数的1/3。问应当怎样分,编写程序实现。 输入格式 无 输出格式 “%d,%d,%d\n” (注意:输出的顺序为第一个3位数,第二...

    题目内容
      将0到9这十个数字分成三个3位数,要求第一个3位数,正好是第二个3位数的1/2,是第三个3位数的1/3。问应当怎样分,编写程序实现。
    输入格式
    输出格式 “%d,%d,%d\n” (注意:输出的顺序为第一个3位数,第二个3位数,第三个3位数)

    这里写图片描述

    //C语言使用数组实现
    #include <stdio.h>
    #include<stdbool.h>
    int main()
    {   
    	int p[10],num[3];
    	for(int i=100;i<=333;i++)
    	{
    		for(int j=0;j<10;j++) //p[0]~p[9]都初始化为0 
    		{
    			p[j]=0;
    		}
    		for(int m=0;m<3;m++)
    		{
    			num[m]=i*(m+1);
    			int mid=num[m];
    			while(mid!=0)
    			{
    				p[mid%10]++;//每个数出现几次 
    				mid/=10;
    			}
    		}
    		bool flag=true;
    		for(int k=0;k<10;k++)
    		{
    			if(p[k]>1)//出现多于一次 
    			{
    				flag=false;break;
    			}
    		}
    		if(flag==true)
    		{
    			printf("%d,%d,%d\n" ,num[0],num[1],num[2]);
    		}
    	}
    	return 0;
    }
    
    //当时感觉用C++写比较容易,但是限制语言必须是C
    #include <iostream>
    #include <set>
    using namespace std;
    int main()
    {
    	int num[3];
    	for (int i=100;i<=333;i++)
    	{
    		set<int> s;
    		for (int j=0;j<3;j++)
    		{
    			num[j]=i*(j+1);
    			int mid=num[j];
    			while(mid!=0)
    			{
    				s.insert(mid%10);//每个数字插入set容器
    				mid/=10;
    			}
    		}
    		if (s.size()==9)
    		{
    			cout<<num[0]<<","<<num[1]<<","<<num[2]<<endl;
    		}
    	}
    }
    
    展开全文
  • Python实现控制小数点位数的方法

    万次阅读 多人点赞 2019-08-12 20:40:39
    文章目录一、利用python内置的round()函数二、利用格式化方法、利用 math 模块里 ceil 和 floor ...一般情况是使用四舍五入的规则,但是碰到舍入的后一为5的情况,如果要取舍的位数前的偶数,则直接舍弃,如...
  • 题目:一个文件有 10G 个整数,乱序排列,要求找出中位数。内存限制为 2G。只写出思路即可(内存限制为 2G的意思就是,...中位数的位置:当样本数为奇数时,中位数=(N+1)/2 ; 当样本数为偶数时,中位数为N/2与1+...
  • 来源: POJ (Coursera声明:POJ上完成的习题将不会计入Coursera的最后成绩。) 注意: 总时间限制: 1000ms 内存限制: 65536kB 描述 你买了一箱n个苹果,很不幸的是买完时箱子里混进了一条虫子。虫子每x小时能吃掉一...
  • O(n)的时间复杂度求中位数

    千次阅读 2020-09-26 14:45:14
    O(n)中位数问题是指:O(n)的时间复杂度内找到一个无序序列的中位数开始O(n)时间复杂度求中位数之前,先手写一下快速排序。 快速排序的实现 Reference: 快速排序|菜鸟教程 白话经典算法系列之六 快速排序 ...
  • 如何得到一个数据流中位数?如果从数据流读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。
  • 如何找到两个数组的中位数

    千次阅读 2019-08-19 12:13:11
    大数组的长度是奇数(11),中位数显然是位于正中的第6个元素,也就是元素5 上面的例子是奇数个元素的情况。那么偶数的元素是什么样呢?让我们来看另一个例子: 上图这两个给定数组A和B,长度都是5,归并之后的大数...
  • 1.求一个序列的中位数(c++)

    千次阅读 2019-07-12 02:45:36
    2、能够得到这个序列的中位数 中位数定义: #1.如果元素个数是偶数,返回已排序序列最中间的两个数字的平均数 #2.如果元素个数是奇数,返回已排序序列中间那个数 二、分析 能够看到该题的标记是hard,显...
  • 一、使用中间值 import java.util.Scanner; public class IfElseExer { public static void main(String[] args){ Scanner s = new Scanner(System.in); int i = s.nextInt();... int j = s.nextInt();...
  • 用1,2,3,...,9组成3个三位数abc,def,ghi,每个数字恰好使用一次,要求abc:def:ghi=1:2:3。按照“abc def ghi”的格式输出所有解。 思路: 这道题不难,关键在于怎么判断是否每个数字使用一次。由于自己没想出好的...
  • #include #include #define EPS 1e-7 main() { int a; printf("please enter a num:"); scanf("%d",&a); if( a != 0) { if(a % 2 == 0 ) printf("num is 偶数");... } else printf("num 不是奇数也不是偶数"); }
  • //通过2可以发现 完全平方的因子个一定是奇数个 而其他的因子个一定成对出现 因此问题可以归结为求完全平方 void ReleasePrisoner3() { int n = 4; for(int i = 1; i ; i++) { if(i * i )//编号为i*i...
  • Python编程之求奇数

    千次阅读 2019-04-20 09:06:02
    问题描述:求0—7所能组成的奇数。 程序分析: 组成1位数是4个。 组成2位数是7*4个。 组成3位数是7*8*4个。 组成4位数是7*8*8*4个。 源代码: #!/usr/bin/python # -*- coding: UTF-8 -*- if __name...
  • 【题目】   现有两个长度相等的排序数组,求这两个排序数组组成的新数组的上中位数。 【其他限制】   时间复杂度最好为O(nlogn),空间复杂度为O(1)。
  • 最小距离之和-中位数

    千次阅读 2013-10-28 22:23:50
     中位数:将数组大小为n的数据,从大到小,或者是从小到大排列,那么当n为奇数的时候,中位数就是(n+1)/2的这个数,当n为偶数的时候,中位数就是n/2和(n+1)/2这二个数据的平均数。 中位数:也就是选取中间的数。...
  • 请找出两个排序数组的中位数并且总的运行时间复杂度为 O(log (m+n)) 。 示例 1: nums1 = [1, 3] nums2 = [2] 中位数是 2.0 示例 2: nums1 = [1, 2] nums2 = [3, 4] 中位数是 (2 + 3)/2 = 2.5 这是问题,我刚...
  • C++ 查找中位数

    千次阅读 2020-05-11 18:08:17
    2.已知中位数 要求左边小于(大于)中位数,右边大于(小于)中位数,且左边数列大小等于右边数列大小 。 而对左边数列或者右边数列数值的排列顺序无要求。 所以,我们可以采用优先队列进行解决。 代码实现 #include...
  • 公众号后台回复“图书“,了解更多号主新书内容 作者:胖里 来源: 胖里的日常 先来看看中位数的概念。中位数(Median)又称中值,统计学的专有名词,是按顺序排...
  • ##题目一 数组中偶数的和 1.实验代码 #include&lt;stdio.h&gt; int main() { int i,sum=0; int a[10]; for(i=0;i&lt;10,i++) { scanf("%d",&amp;a[i]); if(a[i]%2==0){ ...
  • 找出一个无序数组的中位数

    万次阅读 热门讨论 2017-08-04 14:01:25
    找出一个无序数组的中位数
  • 往期推荐【暑期预习】人教版一年级数学上册知识要点【暑期预习】人教版二年级数学上册知识要点【暑期预习】人教版年级数学上册知识要点【暑期预习】人教版四年级数学上册知识要点【暑期预习】人教版五年级数学上册...
  • 今天做蓝桥杯练习时遇到一道题,觉得题很新颖,所以写下来便于整理。题的内容是输入一个N,1~N内任意选择个...(1)奇数-偶数-奇数,此时很显然,个连续的若为此情况,个都是互质的,则此时最大的最小
  • 数据流中位数

    2016-12-24 15:12:04
    如果从数据流读出奇数个数值,那么中位数就是所有值排序之后位于中间的数值。如果数据流读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。  分析:下面总结使用了没有排序的数组、排序的数组...
  • 如果从数据流读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用Insert()方法读取数据流,使用...
  • 寻找中位数这个过程,可以使用2个二叉堆去实现,一个最大堆,一个最小堆,然后二者的对二者的top进行运算。具体见代码: void addnum(int num) { if(big_queue.empty()){ big_queue.push(num); } if(big_...
  • 中位数: 数字从小到大排列的数列位于中间位置的那个数,如果数列个数为奇数位则中位数位于(n+1)/2, 如果为偶数位则为n/2与n/2+1的两个数的平均值,可以用numpy.median函数计算 众数:数列出现频率最多的数, ...
  • 本章将介绍NumPy的常用函数...算数平均值和加权平均值都是数值分布寻找中心点的方法。然而,它们对于异常值(outlier)既不鲁棒也不敏感。举例来说,如果我们有一个高达100万美元的收盘价,这将影响到我们的计算结

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 32,368
精华内容 12,947
关键字:

在三位数中最小的偶数