【数据结构(邓俊辉)学习笔记】向量04——有序向量

文章目录

  • 0.概述
  • 1.比较器
  • 2.有序性甄别
  • 3.唯一化
    • 3.1低效算法
    • 3.1.1实现
    • 3.1.2 复杂度
    • 3.1.3 改进思路
    • 3.2 高效算法
      • 3.2.1 实现
      • 3.2.2 复杂度
  • 4.查找
    • 4.1统一接口
    • 4.2 语义定义
    • 4.3 二分查找
      • 4.3.1 原理
      • 4.3.2 实现
      • 4.3.3 复杂度
      • 4.3.4 查找长度
      • 4.3.5 不足
    • 4.4 Fibonacci查找
      • 4.4.1 改进思路
      • 4.4.2 黄金分割
      • 4.4.3 实现
      • 4.4.4 复杂度分析
      • 4.4.5 平均查找长度
    • 4.5 通用策略
    • 4.6 结论

0.概述

若向量S[0, n)中的所有元素不仅按线性次序存放,而且其数值大小也按此次序单调分布,则称作有序向量(sorted vector)。

1.比较器

有序向量的定义的先决条件:各元素之间必须能够比较大小和判等操作。这一条件构成了有序向量中“次序”概念的基础,否则所谓的“有序”将无从谈起。

复杂数据对象应重载"<“和”<="等操作符。

2.有序性甄别

在这里插入图片描述
算法思想:顺序扫描整个向量,逐一比较每一对相邻元素——向量已经有序,当且仅当它们都是顺序的。

template <typename T> 
int Vector<T>::disordered() const { //返回向量中逆序相邻元素对的总数
	int n = 0; //计数器
	for ( int i = 1; i < _size; i++ ) //逐一检查_size - 1对相邻元素
		if ( _elem[i - 1] > _elem[i] ) n++; //逆序则计数
	return n; //向量有序当且仅当n = 0
}

3.唯一化

出于效率的考虑,为清除无序向量中的重复元素,一般做法往往是首先将其转化为有序向量

3.1低效算法

3.1.1实现

算法思想:
在这里插入图片描述

template <typename T> 
int Vector<T>::uniquify() { //有序向量重复元素剔除算法(低效版)
	int oldSize = _size; int i = 1; //当前比对元素的秩,起始于首元素
	while ( i < _size ) //从前向后,逐一比对各对相邻元素
		_elem[i - 1] == _elem[i] ? remove ( i ) : i++; //若雷同,则初除后者;否则,转至后一元素
	return oldSize - _size; //向量觃模发化量,即被初除元素总数
}

3.1.2 复杂度

在这里插入图片描述
分析需用结论:remove()操作的复杂度线性正比于被删除元素的后继元素总数。不清除的可看无序向量。

最坏情况下:
当所有元素均雷同时,用于所有这些remove()操作的时间总量将高达:
(n - 2) + (n - 3) + … + 2 + 1= O( n 2 n^2 n2) 由算术级数得 。不清楚可看算法分析

这一效率竟与向量未排序时相同,说明该方法未能充分利用此时向量的有序性。

3.1.3 改进思路

反思:低效的根源在于,同一元素可作为被删除元素的后继多次前移
启示:若能以重复区间为单位,成批删除雷同元素,性能必能改进。

3.2 高效算法

3.2.1 实现

算法思想:
可以区间为单位成批地删除前后紧邻的各组重复元素,并将其后继元素(若存在)统一地大跨度前移。

template <typename T> 
Rank Vector<T>::uniquify() { //有序向量重复元素剔除算法(高效版)
   Rank i = 0, j = 0; //各对互异“相邻”元素的秩
   while ( ++j < _size ) //逐一扫描,直至末元素
      if ( _elem[i] != _elem[j] ) //跳过雷同者
         _elem[++i] = _elem[j]; //发现不同元素时,向前移至紧邻于前者右侧
   _size = ++i; shrink(); //直接截除尾部多余元素
   return j - i; //向量规模变化量,即被删除元素总数
}

3.2.2 复杂度

在这里插入图片描述
由此可知,uniquify()算法的时间复杂度应为O(n),较之无序向量唯一化算法的O( n 2 n^2 n2),效率整整提高了一个线性因子。关键在于向量已经排序

4.查找

4.1统一接口

为区别于无序向量的查找接口find(),有序向量的查找接口将统一命名为search()。外部接口形式上统一,内部实现算法却不见得完全统一。

template <typename T> //在有序向量的区间[lo, hi)内,确定不大于e的最后一个节点的秩
Rank Vector<T>::search( T const& e, Rank lo, Rank hi ) const { // 0 <= lo < hi <= _size
   return ( rand() % 2 ) ? binSearch( _elem, e, lo, hi ) : fibSearch( _elem, e, lo, hi );
} //等概率地随机使用二分查找、Fibonacci查找
  • 如何处理特殊情况?
    比如,目标不存在;或者反过来,目标元素同时存在多个

4.2 语义定义

在语义上进一步的细致约定,使search()接口作为一个基本部件为其他算法利用。

在有序向量区间V[lo,hi)中,确定不大于e的最后一个元素。
在这里插入图片描述

4.3 二分查找

4.3.1 原理

在这里插入图片描述
每经过至多两次比较操作,可以将查找问题简化为一个规模更小的新问题。如此,借助递归机制即可便捷地描述和实现此类算法。

4.3.2 实现

算法思想:减而治之

//二分查找算法(版本A):在有序向量的区间[lo, hi)内查找元素e,0 <= lo <= hi <= _size
template <typename T> static Rank binSearch( T* S, T const& e, Rank lo, Rank hi ) {
   /*DSA*/printf ( "BIN search (A)\n" );
   while ( lo < hi ) { //每步迭代可能要做两次比较判断,有三个分支
      /*DSA*/ for ( int i = 0; i < lo; i++ ) printf ( "     " ); if ( lo >= 0 ) for ( int i = lo; i < hi; i++ ) printf ( "....^" ); printf ( "\n" );
      Rank mi = ( lo + hi ) >> 1; //以中点为轴点(区间宽度折半,等效于其数值表示的右移一位)
      if      ( e < S[mi] ) hi = mi; //深入前半段[lo, mi)继续查找
      else if ( S[mi] < e ) lo = mi + 1; //深入后半段(mi, hi)继续查找
      else                  return mi; //在mi处命中
      /*DSA*/ if ( lo >= hi ) { for ( int i = 0; i < mi; i++ ) printf ( "     " ); if ( mi >= 0 ) printf ( "....|\n" ); else printf ( "<<<<|\n" ); }
   } //成功查找可以提前终止
   return -1; //查找失败
} //有多个命中元素时,不能保证返回秩最大者;查找失败时,简单地返回-1,而不能指示失败的位置
  • 通过快捷的整数移位操作回避了相对更加耗时的除法运算。
  • 通过引入lo、hi和mi等变量,将减治算法通常的递归模式改成了迭代模式。(递归消除)

4.3.3 复杂度

在这里插入图片描述
随着迭代的不断深入,有效的查找区间宽度将按1/2的比例以几何级数的速度递减。经过至多log2(hi - lo)步迭代后,算法必然终止。故总体时间复杂度不超过:
O( l o g 2 ( h i − l o ) log_2(hi - lo) log2(hilo)) = O(logn)
上图中的递归公式也可得出这个结论,递推公式不熟悉的可以看递推分析。

顺序查找算法的O(n)复杂度相比无序向量的查找find()无序向量,O(logn)几乎改进了一个线性因子(任意c > 0,logn = O( n c n^c nc))。

4.3.4 查找长度

查找算法的整体效率主要地取决于其中所执行的元素大小比较操作的次数,即所谓查找长度。

通常,需分别针对成功与失败查找,从最好、最坏、平均等角度评估

结论:此版二分查找成功、失败时的平均查找长度均大致为O(1.5logn)

4.3.5 不足

尽管二分查找算法(版本A)即便在最坏情况下也可保证O(logn)的渐进时间复杂度,但就其常系数1.5而言仍有改进余地。

4.4 Fibonacci查找

4.4.1 改进思路

在这里插入图片描述
解决问题的思路:

  1. 其一,调整前、后区域的宽度,适当地加长(缩短)前(后)子向量
  2. 其二,统一沿两个方向深入所需要执行的比较次数,比如都统一为一次

4.4.2 黄金分割

实际上,减治策略本身并不要求子向量切分点mi必须居中,故按上述改进思路,不妨按黄金分割比来确定mi。
在这里插入图片描述

4.4.3 实现

算法思路:减治策略——黄金分割比来确定mi

#include "fibonacci/Fib.h" //引入Fib数列类
//Fibonacci查找算法(版本A):在有序向量的区间[lo, hi)内查找元素e,0 <= lo <= hi <= _size
template <typename T> static Rank fibSearch( T* S, T const& e, Rank lo, Rank hi ) {
   /*DSA*/printf ( "FIB search (A)\n" );
   //用O(log_phi(n = hi - lo)时间创建Fib数列
   for ( Fib fib( hi - lo ); lo < hi; ) { //Fib制表备查;此后每步迭代仅一次比较、两个分支
      /*DSA*/ for ( Rank i = 0; i < lo; i++ ) printf ( "     " ); if ( lo >= 0 ) for ( Rank i = lo; i < hi; i++ ) printf ( "....^" ); else printf ( "<<<<|" ); printf ( "\n" );
      while ( hi - lo < fib.get() ) fib.prev(); //自后向前顺序查找(分摊O(1))
      Rank mi = lo + fib.get() - 1; //确定形如Fib(k)-1的轴点
      if      ( e < S[mi] ) hi = mi; //深入前半段[lo, mi)继续查找
      else if ( S[mi] < e ) lo = mi + 1; //深入后半段(mi, hi)继续查找
      else                  return mi; //在mi处命中
      /*DSA*/ if ( lo >= hi ) { for ( int i = 0; i < mi; i++ ) printf ( "     " ); if ( mi >= 0 ) printf ( "....|\n" ); else printf ( "<<<<|\n" ); }
   } //一旦找到,随即终止
   return -1; //查找失败
} //有多个命中元素时,不能保证返回秩最大者;失败时,简单地返回-1,而不能指示失败的位置

对Fib数不清楚得可以看fib

4.4.4 复杂度分析

进入循环之前调用构造器Fib(n = hi - lo),将初始长度设置为“不小于n的最小Fibonacci项”。这一步所需花费的O( l o g ϕ log_\phi logϕn)时间,分摊到后续O( l o g ϕ log_\phi logϕn)步迭代中,并不影响算法整体的渐进复杂度。

4.4.5 平均查找长度

结论:O(1.44∙log2n)

4.5 通用策略

在这里插入图片描述

4.6 结论

  • 二分查找算法的时间复杂度O(logn),平均查找长度O(1.5∙log2n)
  • Fibonacci查找算法的时间复杂度O(logn),平均查找长度O(1.44∙log2n)

Fibonacci查找算法效率略有提高。二分查找和Fibonacci查找还可改进,见

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/572244.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

项目_预览和模拟器运行_真机运行鸿蒙应用---HarmonyOS4.0+鸿蒙NEXT工作笔记002

然后再来看如何使用预览,可以看到 右侧有个preview,点开就可以了 然后再有一个tools,这里 Device Manager,这个是模拟器 点开以后可以看到让我们连接,本地模拟器,还是远程模拟器,还是远程设备 这里我们选择phone 如果选择remote device,这个需要登录华为账号,会自动弹出来登…

PyTorch深度解析:Tensor——神经网络的核心构建块

在深度学习和神经网络的研究与应用中&#xff0c;Tensor&#xff08;张量&#xff09;无疑是一个核心概念。特别是在PyTorch这一强大的深度学习框架中&#xff0c;Tensor更是扮演了举足轻重的角色。本文将深入探讨PyTorch中的Tensor&#xff0c;从其基本定义、特性、操作到实际…

2024年度西安市创新联合体备案申报条件时间要求须知

一、申报条件 组建市级创新联合体需具备牵头单位、成员单位、组建协议、首席科学家等四个条件。 (一)牵头单位 1.牵头单位应为在西安市注册登记的省市产业链龙头骨干企业&#xff0c;重点支持市级重点产业链“链主”企业; 2.牵头单位一般为1家。 (二)成员单位 1.成员单位…

5分钟——快速搭建后端springboot项目

5分钟——快速搭建后端springboot项目 1. idea新建工程2. 构建pom.xml文件3. 构建application.yml配置文件4. 构建springboot启动类5. 补充增删改查代码6. 运行代码7. 下一章 1. idea新建工程 点击右上角新建一个代码工程 别的地方不太一样也不用太担心&#xff0c;先创建一个…

linux驱动-CCF-0基础

1. 时钟设备 晶振&#xff1a;提供基础时钟源的&#xff08;可分为有源晶振、无源晶振两种&#xff09;&#xff1b; PLL: 用于倍频的锁相环&#xff1b; mux: 用于多路时钟源选择&#xff1b; Divider: 用于分频的&#xff1b; gate: 用于时钟使能的与门电路等 注册函数…

Python基础09-装饰器深度解析与应用

在Python中&#xff0c;装饰器&#xff08;Decorator&#xff09;是一种设计模式&#xff0c;用于修改或增强函数或方法的行为&#xff0c;而无需更改其实际代码。装饰器允许我们以一种灵活且可重用的方式向函数添加新的功能。本文将深入探讨Python装饰器的多种用法&#xff0c…

2024最新版JavaScript逆向爬虫教程-------基础篇之JavaScript密码学以及CryptoJS各种常用算法的实现

目录 一、密码学介绍1.1 为什么要学密码学?1.2 密码学里面学哪一些 二、字符编码三、位运算四、Hex 编码与 Base64 编码4.1 Hex 编码4.2 Base64 编码 五、消息摘要算法5.1 简介5.2 JS中的MD5、SHA、HMAC、SM3 六、对称加密算法6.1 介绍6.2 加密模式和填充方式6.3 CryptoJS 中D…

Redis入门到通关之数据结构解析-IntSet

文章目录 概述IntSet升级简易源码总结 欢迎来到 请回答1024 的博客 &#x1f34e;&#x1f34e;&#x1f34e;欢迎来到 请回答1024的博客 关于博主&#xff1a; 我是 请回答1024&#xff0c;一个追求数学与计算的边界、时间与空间的平衡&#xff0c;0与1的延伸的后端开发者。 …

OpenHarmony开源软件供应链安全风险

慕冬亮&#xff0c;华中科技大学网络空间安全学院副教授&#xff0c;武汉英才&#xff0c;华中科技大学OpenHarmony技术俱乐部、开放原子开源社团指导教师。研究方向为软件与系统安全&#xff0c;在国际安全会议上发表十余篇论文&#xff0c;并获得ACM CCS 2018杰出论文奖。创立…

ocr文字识别软件是干什么的?

OCR&#xff08;Optical Character Recognition&#xff0c;光学字符识别&#xff09;文字识别软件是一种能够将图像或者扫描的文档中的文字内容转换为可编辑的文本格式的软件。它的主要功能包括&#xff1a; 1. **文字提取&#xff1a;**识别图像中的文字并提取出来&#xff0…

CSS盒子模型的认识

前言&#xff1a; 当我们打开一个网页使用F12进行调试时&#xff0c;经常可以看到如下图片&#xff0c;这便是一个盒子。 什么是盒子&#xff1a; 所谓盒子模型&#xff08;Box Model&#xff09;就是把 HTML 页面中的元素看作是一个矩形的盒子&#xff0c;也就是一个盛装内容的…

LeetCode 热题 100 Day06

矩阵相关题型 Leetcode 48. 旋转图像【中等】 题意理解&#xff1a; 将一个矩阵顺时针旋转90度&#xff0c;返回旋转后的矩阵。 要求&#xff1a; 在原地修改&#xff0c;不借助额外的空间 如果可以使用辅助数组来实现转置,则有 matrix_new[i][j]matrix[j][row-i-1]; 解…

机器人系统开发ros2-基础实践02-自定义一个机器人动作aciton服务端和客户端(c++ 实现)

aciton 是 ROS 中异步通信的一种形式。 操作客户端向操作服务器发送目标请求。 动作服务器将目标反馈和结果发送给动作客户端。 先决条件&#xff1a; 将需要上一个 教程创建操作action_tutorials_interfaces中定义的包和接口。Fibonacci.action 步骤1&#xff1a; 1.1 创建…

ComfyUI学习旅程

一、模型文件&#xff08;Checkpoint&#xff09; 首先它很大&#xff0c;这些文件是你从huggingface或者civitai下载而来的&#xff0c; 所以这些大文件如 .ckpt 或 .safetensors &#xff0c;实际上包含了什么内容呢&#xff1f; 它包含了包含了三种不同模型的权重&#x…

用wps自带工具给图片做标注

在wps中&#xff0c;选中wps中的图片&#xff0c;右键选择【编辑】进入图片编辑器&#xff0c;在选项卡面板右侧选择【标注】工具&#xff0c;再选择【添加文本】工具&#xff0c;即可直接在图片上输入文字&#xff0c;标注完成后选择【覆盖原图】就完成标注任务。

【计算机网络】网络模型

OSI七层网络模型 七层模型如图所示 每层的概念和功能 物理层 职责&#xff1a;将数据以比特为单位&#xff0c;通过不同的传输介质将数据传输出去。 主要协议&#xff1a;物理媒介相关的协议&#xff0c;如RS232&#xff0c;V.35&#xff0c;以太网等。 数据链路层 职责&…

嵌入式Linux driver开发实操(二十三):ASOC

ASoC的结构及嵌入到Linux音频框架 ALSA片上系统(ASoC)层的总体项目目标是为嵌入式片上系统处理器(如pxa2xx、au1x00、iMX等)和便携式音频编解码器提供更好的ALSA支持。在ASoC子系统之前,内核中对SoC音频有一些支持,但它有一些局限性: ->编解码器驱动程序通常与底层So…

搜维尔科技提供电影和动画的动作捕捉解决方案

电影和动画&#xff0c;实时发现讲故事的本质 讲故事的技巧已经发展到给导演比以往更多的控制力和灵活性。从实时预可视化镜头&#xff0c;到将实时镜头与直接流入您选择的工具的同步数据进行合成。动作捕捉消除了计划中的猜测&#xff0c;使不可能成为可能。 特色解决方案-行…

学习Rust的第17天:Traits

Rust traits&#xff0c;包括自定义trait声明&#xff0c;trait边界&#xff0c;实现trait的返回类型&#xff0c;条件方法实现和blanket实现。Rust的多态性严重依赖于traits&#xff0c;这允许基于trait的分派和泛型编程。掌握traits使开发人员能够创建灵活的、可维护的代码&a…

springcloud Ribbion 实战

一、Ribbon单独使用&#xff0c;配置自动重试&#xff0c;实现负载均衡和高可用 1.spring-cloud-starter-netflix-ribbon 包引入 <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-ribbon</art…