《算法导论》学习笔记之Chapter9中位数和顺序统计量

2024-05-16 00:18

本文主要是介绍《算法导论》学习笔记之Chapter9中位数和顺序统计量,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

第9章 中位数和顺序统计量

先定义:在一个由n个元素组成的集合中,第i个顺序统计量是该集合中第i小的元素。一个中位数是它所属集合的“中点元素”。当n是奇数时,中位数是唯一的,位于i=n/2处。当n为偶数时,中位数有两个,分别位于i = n/2和i=n/2+1处。不考虑n的奇偶性,中位数总是出现在i=(n+1)/2向下取整处,也叫下中位数,和i=(n+2)/2向上取整处,也叫上中位数。本书默认下中位数。

本章讨论的是:从n个互异的元素构成的集合中选择第i个顺序统计量的问题。

如果不学习其他方法,仅使用前面介绍的算法,可以考虑使用归并排序或堆排序的算法,在O(nlogn)时间内完成查找。下面会介绍一个更快的方法。


求最大值最小值问题:

单词求最大值或最小值,会进行θ(n)次的比较,如果同时求出最大值最小值,简单来说可能需要2n-2次比较。但是,稍微改变一下:提前记录已知的最大值和最小值,每次选择一对数据进行比较,较大的与最大值比较,较小的与最小值比较,这样每次进行3次比较,总体会进行3(n/2)向下取整次比较,效率提升不少。

该问题中:如何设定已知的最大值和最小值取决n的奇偶性:n为奇数时,取第一个元素作为最大值最小值,后面元素成对比较;n为偶数时,对前两个元素进行比较,大的作为最大值,小的作为最小值,之后与n为奇数时相同的比较过程。若n为奇数,总比较次数为3(n/2)向下取整次;n为偶数,总比较次数为3(n-2)/2+1,共3n/2-2次比较。所以,不管哪一种情况,总的比较次数最多是3(n/2)向下取整次。


基于快速排序算法的线性时间选择算法

相同点是:均采用递归划分数据;

区别是:快速排序处理划分的两边,期望运行时间是θ(nlogn)

               选择算法只处理划分的一边,期望运行时间是线性时间θ(n)


下面介绍一个期望运行时间(注意:是期望,不是最坏运行时间)是线性时间的选择算法,代码实现如下:

// 基于快排的线性时间选择算法,选择数组中第i小的元素public static int RandomizedSelect(int[] a, int p, int r, int i) {if (p == r) {return a[p];}// q的左侧是比q小的,右侧是比q大的int q = RandomPartition(a, p, r);// k坐标表示的该元素是在数组中排行第k的数据;int k = q - p + 1;if (i == k) {return a[q];} else if (i < k) {return RandomizedSelect(a, p, q - 1, i);} else {return RandomizedSelect(a, q + 1, r, i - k);}}


上述算法最坏情况运行时间是θ(n.^2),但是因为是随机化的,所以不存在一个特定的情况导致最坏情况发生,该算法有线性的期望运行时间θ(n)。


下面是一个最坏情况是线性时间的选择算法

该算法的原理图如下:

左侧算法原理代码实现如下:

private static int select(int[] a, int l, int r, int k) {if (r - l < 75) {insertSort(a, l, r); // 当数组个数较小时,用快速排序进行排序return a[l + k - 1];}int group = (r - l + 5) / 5;//对数组进行分组,每组5个元素。其中加5操类似于向上取整for (int i = 0; i < group; i++) {//该循环结束之后会导致数组中的前group个数被所有子数组的中位数替代int left = l + 5 * i;int right = (l + i * 5 + 4) > r ? r : l + i * 5 + 4; // 如果超出右边界就用右边界赋值insertSort(a, left, right);//对每组进行插入排序int mid = (left + right) / 2;//找到中间坐标swap(a, l + i, mid); // 将各组中位数与前i个交换}int pivot = select(a, l, l + group - 1, (group + 1) / 2); // 找出中位数的中位数int p = partition(a, l, r, pivot); // 用中位数的中位数作为主元的位置int leftNum = p - l; // leftNum用来记录主元位置的前边的元素个数if (k == leftNum + 1)return a[p];else if (k <= leftNum)return select(a, l, p - 1, k);else // 若k在主元位子的后边,则要从主元位置的后边数起,即第(k - leftNum - 1)个return select(a, p + 1, r, k - leftNum - 1);}// 适用于线性时间选择的partition方法private static int partition(int[] a, int l, int r, int pivot) { int i = l;int j = r;while (true) {while (a[i] <= pivot && i < r)++i; // i一直向后移动,直到出现a[i]>pivotwhile (a[j] > pivot)--j; // j一直向前移动,直到出现a[j]<pivotif (i >= j)break;swap(a, i, j);}a[l] = a[j];a[j] = pivot;return j;}private static void insertSort(int[] a, int low, int high) { // 插入排序for (int i = low + 1; i <= high; i++) {int key = a[i];int j = i - 1;while (j >= low && a[j] > key) {a[j + 1] = a[j];j--;}a[j + 1] = key;}}

与比较排序一样,上述选择算法也是通过元素之间的比较来确定彼此之间的次序的,但是不同的是,上述选择算法在没有排序的情况下就已经解决了选择问题,所以运行时间并不受Ω(nlogn)的限制,而是线性运行时间。且与第8章介绍的线性运行时间不同的是,对输入并没有任何限制。

备注:后面的代码参考别人的博客内容,链接如下:http://blog.csdn.net/notfamous/article/details/23261341


这篇关于《算法导论》学习笔记之Chapter9中位数和顺序统计量的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/993331

相关文章

C++统计函数执行时间的最佳实践

《C++统计函数执行时间的最佳实践》在软件开发过程中,性能分析是优化程序的重要环节,了解函数的执行时间分布对于识别性能瓶颈至关重要,本文将分享一个C++函数执行时间统计工具,希望对大家有所帮助... 目录前言工具特性核心设计1. 数据结构设计2. 单例模式管理器3. RAII自动计时使用方法基本用法高级用法

python 线程池顺序执行的方法实现

《python线程池顺序执行的方法实现》在Python中,线程池默认是并发执行任务的,但若需要实现任务的顺序执行,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋... 目录方案一:强制单线程(伪顺序执行)方案二:按提交顺序获取结果方案三:任务间依赖控制方案四:队列顺序消

Unity新手入门学习殿堂级知识详细讲解(图文)

《Unity新手入门学习殿堂级知识详细讲解(图文)》Unity是一款跨平台游戏引擎,支持2D/3D及VR/AR开发,核心功能模块包括图形、音频、物理等,通过可视化编辑器与脚本扩展实现开发,项目结构含A... 目录入门概述什么是 UnityUnity引擎基础认知编辑器核心操作Unity 编辑器项目模式分类工程

IDEA与MyEclipse代码量统计方式

《IDEA与MyEclipse代码量统计方式》文章介绍在项目中不安装第三方工具统计代码行数的方法,分别说明MyEclipse通过正则搜索(排除空行和注释)及IDEA使用Statistic插件或调整搜索... 目录项目场景MyEclipse代码量统计IDEA代码量统计总结项目场景在项目中,有时候我们需要统计

Python学习笔记之getattr和hasattr用法示例详解

《Python学习笔记之getattr和hasattr用法示例详解》在Python中,hasattr()、getattr()和setattr()是一组内置函数,用于对对象的属性进行操作和查询,这篇文章... 目录1.getattr用法详解1.1 基本作用1.2 示例1.3 原理2.hasattr用法详解2.

SQL Server跟踪自动统计信息更新实战指南

《SQLServer跟踪自动统计信息更新实战指南》本文详解SQLServer自动统计信息更新的跟踪方法,推荐使用扩展事件实时捕获更新操作及详细信息,同时结合系统视图快速检查统计信息状态,重点强调修... 目录SQL Server 如何跟踪自动统计信息更新:深入解析与实战指南 核心跟踪方法1️⃣ 利用系统目录

Spring Bean初始化及@PostConstruc执行顺序示例详解

《SpringBean初始化及@PostConstruc执行顺序示例详解》本文给大家介绍SpringBean初始化及@PostConstruc执行顺序,本文通过实例代码给大家介绍的非常详细,对大家的... 目录1. Bean初始化执行顺序2. 成员变量初始化顺序2.1 普通Java类(非Spring环境)(

浅析Spring如何控制Bean的加载顺序

《浅析Spring如何控制Bean的加载顺序》在大多数情况下,我们不需要手动控制Bean的加载顺序,因为Spring的IoC容器足够智能,但在某些特殊场景下,这种隐式的依赖关系可能不存在,下面我们就来... 目录核心原则:依赖驱动加载手动控制 Bean 加载顺序的方法方法 1:使用@DependsOn(最直

Java中的雪花算法Snowflake解析与实践技巧

《Java中的雪花算法Snowflake解析与实践技巧》本文解析了雪花算法的原理、Java实现及生产实践,涵盖ID结构、位运算技巧、时钟回拨处理、WorkerId分配等关键点,并探讨了百度UidGen... 目录一、雪花算法核心原理1.1 算法起源1.2 ID结构详解1.3 核心特性二、Java实现解析2.

在Linux终端中统计非二进制文件行数的实现方法

《在Linux终端中统计非二进制文件行数的实现方法》在Linux系统中,有时需要统计非二进制文件(如CSV、TXT文件)的行数,而不希望手动打开文件进行查看,例如,在处理大型日志文件、数据文件时,了解... 目录在linux终端中统计非二进制文件的行数技术背景实现步骤1. 使用wc命令2. 使用grep命令