程序设计方法与实践-变治法

news2024/12/23 9:16:32

变换之美

变治法就是基于变换的思路,进而使原问题的求解变得简单的一种技术。

变治法一般有三种类型:

  • 实例化简:将问题变换为同问题,但换成更为简单、更易求解的实例。
  • 改变表现:变化为同实例的不同形式,而这个形式还会比较好解决。
  • 问题化简:将原问题变化成另问题实例,而这个问题的解决方案已知。

预排序(Presorting)

预排序的思想基于,当列表有序的时候,所有的操作将会变得更加简单。

  • 检查元素的唯一性

如果是一个带查找序列是无序的,那么想要检查数列中的元素是不是唯一的,使用蛮力法的时间复杂度肯定是\Theta \left ( n^{2} \right )的。

那如果用一个高效的排序算法\Theta \left ( n\log n \right )对待查列表进行排序,那么列表有序之后在进行元素唯一性检查,时间复杂度将会变成线性的,那么总体的时间复杂度就会是\Theta \left ( n\log n \right )

伪码描述:

//先做预排序
sort(A[0,...n-1]);

//基于预排序的元素唯一性检查
PresortElementUniqueness(A[0,...n-1]){
    for(int i=0; i<n-1; i++){
        if(A[i]==A[i+1])
        return FALSE;
    }
    return TRUE;
}
  • 模式计算(computer a mode)

模式计算简单说就是,找到一个列表中出现次数最多的元素。同样我们先考虑一下使用蛮力法去解决这个问题,最坏输入就是列表中没有相同的元素,那么每个元素i都要与剩下的i-1个元素进行比较,然后作为一个新的元素存入辅助列表中。

时间复杂度是:T\left ( n \right )=0+1+2+....n-1=\left ( n-1 \right )n/2

而如果当待查列表是有序的,就只需要线性遍历一遍列表,然后不断去更新那个元素连续出现的次数最多,所以时间复杂度就由原来的平方降低成了对数级别。

伪码描述:

//预排序
sort(A[0,...n-1])

//基于预排序的模式计算
PresortingComputerMode(A[0,...n-1]){
    int modefrequence=0;
    int modeValue=0;
    int runlength=0;
    int runValue=0;
    for(int i=0; i<n-1; i++){
        runlength=1; runValue=A[i];
        while(i+runlength < n-1 && A[i+runlength]==A[i]){
            runlength++;
        }
        if(runlength > modefrequence) modefrequence=runlength modeValue=A[i];
        i=i+runlength;
    }
    return modeValue;
}

这里注意虽然看起来是二重循环,但是实际上时间复杂度是线性的。 

  • 查找问题

我们知道,查找问题中比较优的算法是折半查找\Theta \left ( \log n \right ),但是使用折半查找的前提是列表有序。这了大家就会问了,不管待查列表是有序的还是无序的,查找问题的时间复杂度都是\Theta \left ( n \right )的。那么此时先预排序\Theta \left ( n\log n \right ),再进行折半查找,这不是画蛇添足了吗?

但其实大家想一想在现实的场景中,查找往往不是执行一次的操作,而是频繁执行的,而预排序是永久起作用的。所以当查找操作次数的规模较大时,预排序的优势就会渐渐显现出来。

这里有几道习题给到大家:

这道题比较有意思,能够很好地了解预排序在输入规模比较大时的优势。解决这个问题可以利用不等式:n\log n+k\log_{2} n<=kn/2

所以有,k>=\left (n\log n \right )/\left ( n/2-\log _{2} n\right ) 当n=1000时,k_{min}=21

会对预排序的优势有更好的的理解。

伪码描述:

//分治法求数列最大值最小值
MaxMin(A[l,...r],Max,Min){
    if(r-l==1){
        if(A[l]>A[r]) Max=A[l]
        else Min=A[r]
    }
    else{
        MaxMin(A[l,...(l+r)/2],Max1,Min1)
        MaxMin(A[(l+r)/2,...r],Max2,Min2)
        if(Max1<Max2) Max=Max2
        if(Min1>Min2) Min=Min2   
    }
}

平衡查找树-AVL树

  1. 查找树是一种典型的实现字典的数据结构。

  2. 查找树中的所有元都来自于待查列表集合,并且,左子树的结点元素都小于子树根节点元素,而右子树都大于它

  3. 将集合改写成一个二叉查找树,就是一种典型的“改变形式”的方法。

而基于第二点性质,最优情况下使用二叉查找树的效率为\Theta \left ( \log n \right ),但是在最差情况下其时间复杂度会退化为\Theta \left ( n \right ),也就是这个二叉树可能极不平衡。

  • AVL树是一个二叉查找树,并且给每一个顶点定义一个平衡因子(只能取0、1.-1),也就是该结点左子树与右子树的高度差(空树的高度差定义为-1) 。
  • 而当要插入一个结点时,该二叉树会失去平衡,那么就要进行旋转,使得该二叉树再次平衡。

不平衡时的旋转情况: 

  • 根结点的左子树增加一个左子树上加一个结点使得不平衡(即插入1)时

连接根节点和它右子树的边进行右旋:

  • 根结点的右子树的右子树增加一个结点使得不平衡(即插入3)时

连接根结点和右子树的边进行左旋

  • 根结点左子树的右子树上加一个结点使得不平衡: 

进行左右旋: 

  • 根结点的右子树的左子树加上一个结点使得不平衡: 

进行右左旋

上面是比较简单的例子,下面看一下实际中较为复杂的例子:

毫无疑问的是,这种情况属于,在根结点的左子树的右子树上加上一个结点使得不平衡。所以我要进行左右旋:

比较麻烦的一点是如何处理,T1和T3,但其实只要仔细分析还是比较容易的。我们已应该利用好二叉查找树的性质,也就是左子树上的结点都小于根结点,右子树上的结点都大于它。则,T1上的结点都大于c,所以只能挂在c的右子树上;同理,T3都小于r,所以只能挂在r的左子树上。

最后再给出一道题给大家思考:

依次插入:5、6、8、3、2、4、7  

答案参考:        

 

堆和堆排序

堆的性质

  • 堆是一种满二叉树
  • 并且堆中的每个结点,都大于它的左右结点

堆的构造

  • 自底向上:
  1. 先构造一个满二叉树,按顺序放置键值。
  2. 然后从最后的双亲结点开始,检查是否满足堆的性质2,若不满足则将该双亲结点与最大的子结点交换。一直到根结点。

以bottom-up为例,

伪码描述:

//使用自底向上构造堆
HeapBottom-up(H[1,...n]){
    for(int i=n/2; i>=1; i++){
        k=i; v=H[i]; heap=false;  //判断以当前结点为根结点的树是不是堆的标志
        while(!heap && 2*k<=n){
            int j=2*k;
            if(j<n){
                if(H[j]<=H[j+1]) j++;
            }
            if(H[k]>H[j]){
                H[k]=H[j];
                k=j;
            }else{
                heap=true;
            }
        } //while
        H[k]=v;
    } //for
}

过程详解:

最后的双亲结点,也就是7,检查是否满足。不满足,则交换7和8:

然后从右往左继续找,也就是9,检查是否满足。满足,则继续找2,不满足则交换2和9:

但是因为9的位置发生了变化,所以要检查原先9的位置是否依旧满足条件。2显然不满足条件,则交换2和6:

  • 自顶向下
  1. 将包含新键值K的新结点插入最后一个叶子结点后面。
  2. 然后按照相同的方法调整位置。

过程详解:

例如现在需要插入新键值10,则将其与父母结点比较,8比10要小,所以应该交换位置:

然后继续检查,更新后会受影响的父母结点,也就是9,同样9小于10,所以要交换位置:

如此便完成了新键值的插入。 

此时有同学可能会问,为什么要费这么大功夫去做这件事呢?这里还是要考虑堆的重要性质:任意结点一定大于它的左右字结点

堆的删除

堆的删除大致分为两步:

  1. 将堆的最大键值删除,就是让其与最后一个叶结点交换。
  2. 将堆的规模-1,然后再次进行堆化。

堆排序

所以总结就是,堆排序的步骤:

  1. 将待排序数组,构造成堆
  2. 然后删除堆的最大键值
  3. 规模-1,继续堆化,并重复

删除的顺序即为降序顺序。 

时间效率分析:

霍纳法则

p\left ( x \right )=a_{n}x^{n}+a_{n-1}x^{n-1}...a_{1}x+a_{0}

一个多项式的表达式如上,如果我想要在计算机中去储存这些系数,并且完成这些计算,其实是比较困难的。可能人脑,看这些比较简单,但是这形式用编程解决并不容易。于是采用变治法去思考,我们将其转换成另一种形式:

p\left ( x \right )=\left ( ...\left ( a_{n}x+a_{n-1} \right )x... \right )x+a_{0} 

你可能会说,这种形式看起来很别扭,而且有点多此一举,但是这计算机看来,这种形式是最方便做计算的了。

通过一个观察下面实例,不难发现规律:

在计算机中,一旦过程可以被重复,就可以用循环解决

伪码描述: 

Horner(p[0,...n]){
    int p = p[n], x;  //设变量p,并输入x
    for(int i=n-1; i>=0; i--){
        p = p*x + p[i];
    }
    return p;
}

时间复杂度:\Theta \left ( n \right ) 

二进制幂

基于“改变形式”的思想,使用指数n的二进制表示,来计算a^{n} :

  • 从左往右计算二进制串(利用霍纳法则

首先将指数n表示为比特串:n=b_{l}2^{l}+...b_{1}2^{1}+b_{0}

所以就将形式转化为了: a^{n}=a^{p\left ( 2 \right )}=a^{b_{l}2^{l}+...b_{1}2^{1}+b_{0}}

而这个形式也就是我们熟悉的,利用霍纳法则求解多项式值的形式。

伪码描述: 

L_RBExp( a, b[n] ){
    int product = a;
    for(int i=n-1; i>=0; i--){
        product *=product;
        if(!b[i]){      
            product *=a;  //这一步大家要仔细看一下
        }
    }
}
  • 从右往左计算二进制串

a^{b_{l}2^{l}+...b_{1}2^{1}+b_{0}}=a^{b_{l}2^{l}}*...a^{b_{1}2^{1}}*a^{b_{0}}

也就是将其化为乘积的形式。 

伪码描述: 

R_LBExp( a,b[n] ){
    term = a;
    if(b[0]==1) product = a;
    else product = 1;
    for(int i=1; i<=n; i++){
        term *=term;
        if(!b[1]) pruduct *=term;
    }
    return product;
}

上面就是变治法的一些主要内容,然后还有一个比较重要的一个知识点就是高斯消元法,我还没有梳理好,之后会更新。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

解决Anaconda出现CondaHTTPError: HTTP 000 CONNECTION FAILED for url

解决Anaconda出现CondaHTTPError: HTTP 000 CONNECTION FAILED for url 第一类情况 在anaconda创建新环境时&#xff0c;使用如下代码 conda create -n charts python3.7 错误原因&#xff1a; 默认镜像源访问速度过慢&#xff0c;会导致超时从而导致更新和下载失败。 解决方…

Spring Boot框架:电商系统的技术革新

4 系统设计 网上商城系统的设计方案比如功能框架的设计&#xff0c;比如数据库的设计的好坏也就决定了该系统在开发层面是否高效&#xff0c;以及在系统维护层面是否容易维护和升级&#xff0c;因为在系统实现阶段是需要考虑用户的所有需求&#xff0c;要是在设计阶段没有经过全…

wordpress下载站主题推荐riproV5 wordpress日主题

iPro主题全新V5版本&#xff0c;是一个优秀且功能强大、易于管理、现代化的WordPress虚拟资源商城主题。支持首页模块化布局和WP原生小工具模块化首页可拖拽设置&#xff0c;让您的网站设计体验更加舒适。同时支持了高级筛选、自带会员生态系统、超全支付接口等众多功能&#x…

微服务即时通讯系统的实现(客户端)----(1)

目录 1. 项目整体介绍1.1 项目概况1.2 界面预览和功能介绍1.3 技术重点和服务器架构 2. 项目环境搭建2.1 安装Qt62.3 安装vcpkg2.3 安装protobuf2.4 构建项目2.5 配置CMake属性 3. 项目核心数据结构的实现3.1 创建data.h存放核心的类3.2 工具函数的实现3.3 创建编译开关 4. 界面…

2024年11月15日

1.计算机网络 逻辑右移 做加减法 定点乘法 原码乘法运算 一位乘 计组 2.英语六级

算法定制LiteAIServer摄像机实时接入分析平台玩手机打电话检测算法:智能监控的新篇章

在现代社会&#xff0c;随着智能手机的普及&#xff0c;无论是在工作场所还是公共场所&#xff0c;玩手机或打电话的行为日益普遍。然而&#xff0c;在某些特定环境下&#xff0c;如工厂生产线、仓库、学校课堂等&#xff0c;这些行为可能会影响到工作效率、安全或教学秩序。为…

算法--解决二叉树遍历问题

第一 实现树的结构 class Node(): # 构造函数&#xff0c;初始化节点对象&#xff0c;包含数据和左右子节点 def __init__(self, dataNone): self.data data # 节点存储的数据 self.left None # 左子节点&#xff0c;默认为None self.rig…

深度学习基础—Beam search集束搜索

引言 深度学习基础—Seq2Seq模型https://blog.csdn.net/sniper_fandc/article/details/143781223?fromshareblogdetail&sharetypeblogdetail&sharerId143781223&sharereferPC&sharesourcesniper_fandc&sharefromfrom_link 上篇博客讲到&#xff0c;贪心算…

C++__day1

1、思维导图 2、如果登录失败&#xff0c;提示用户登录失败信息&#xff0c;并且提示错误几次&#xff0c;且重新输入&#xff1b;如果输入错误三次&#xff0c;则退出系统 #include <iostream> using namespace std;int main() {string id , pswd;string user"admi…

【机器学习】数学知识:欧式距离(Euclidean Distance)和曼哈顿距离(Manhattan Distance)

欧式距离和曼哈顿距离是两种常用的距离度量方法&#xff0c;用于衡量两点之间的相似性或差异性。它们在几何分析、数据挖掘、机器学习等领域有广泛应用。 1. 欧式距离 概念 欧式距离&#xff08;Euclidean Distance&#xff09;是最常见的直线距离度量方法&#xff0c;源于欧…

Java之JDBC,Maven,MYBatis

前言 就是用来操作数据库的 1.JDBC快速入门 注意在使用前一定要导入jar包 在模块那里新建目录&#xff0c;新建lib&#xff0c;粘贴复制jar包&#xff0c;我这个jar设置的是模块有效 package test1017;import java.sql.Connection; import java.sql.DriverManager; import…

JavaWeb笔记整理——Spring Task、WebSocket

目录 SpringTask ​cron表达式 WebSocket SpringTask cron表达式 WebSocket

【大数据学习 | HBASE高级】rowkey的设计,hbase的预分区和压缩

1. rowkey的设计 ​ RowKey可以是任意字符串&#xff0c;最大长度64KB&#xff0c;实际应用中一般为10~100bytes&#xff0c;字典顺序排序&#xff0c;rowkey的设计至关重要&#xff0c;会影响region分布&#xff0c;如果rowkey设计不合理还会出现region写热点等一系列问题。 …

如何实现主备租户的无缝切换 | OceanBase应用实践

对于DBA而言&#xff0c;确保数据库的高可用性、容灾等能力是其日常工作中需要持续思考和关注的重要事项。一方面&#xff0c;可以利用数据库自身所具备的功能来实现这些目标&#xff1b;若数据库本身不提供相应功能&#xff0c;DBA则需寻找其他工具来增强数据库的高可用性和容…

Spring 中的 BeanDefinitionParserDelegate 和 NamespaceHandler

一、BeanDefinitionParserDelegate Spring在解析xml文件的时候&#xff0c;在遇到<bean>标签的时候&#xff0c;我们会使用BeanDefinitionParserDelegate对象类解析<bean>标签的内容&#xff0c;包括<bean>标签的多个属性&#xff0c;例如 id name class in…

MQTT从入门到精通之MQTT Dashboard

MQTT Dashboard 1 Dashboard简介 EMQX 提供了一个内置的管理控制台&#xff0c;即 EMQX Dashboard。方便用户通过 Web 页面就能轻松管理和监控 EMQX 集群&#xff0c;并配置和使用所需的各项功能。 访问地址&#xff1a;http://ip:18083 首次登录访问账号&#xff1a;admin…

Flume和kafka的整合

1、Kafka作为Source 【数据进入到kafka中&#xff0c;抽取出来】 在flume的conf文件夹下&#xff0c;有一个flumeconf 文件夹&#xff1a;这个文件夹是自己创建的 创建一个flume脚本文件&#xff1a; kafka-memory-logger.conf Flume 1.9用户手册中文版 — 可能是目前翻译最完…

vue2项目中在线预览csv文件

简介 希望在项目中&#xff0c;在线预览.csv文件&#xff0c;本以为插件很多&#xff0c;结果都只是支持excel&#xff08;.xls、.xlsx&#xff09;一到.csv就歇菜。。。 关于文件预览 vue-office&#xff1a;文档、 查看在线演示demo&#xff0c;支持docx、.xlsx、pdf、ppt…

右键添加获取可供WSL使用的路径,对windows文件夹也适用,即获取符合Linux规范的路径内容给WSL

文章目录 1. 功能展示1.1. 对 WSL 文件/文件夹/目录空白位置 使用1.2. 对 Windows 文件/文件夹/目录空白位置 使用1.3. Fin 2. 方法3. 文件内容3.1. AddWSLPath.reg3.2. CopyPath.vbs 4. 念念碎 1. 功能展示 1.1. 对 WSL 文件/文件夹/目录空白位置 使用 输出 /etc 1.2. 对 Wi…

新版Apache tomcat服务安装 Mac+Window双环境(笔记)

简介&#xff1a;Tomcat服务器器的下载和安装&#xff1a; 安装前提 1&#xff09;电脑需要有java环境&#xff0c;jdk8以上&#xff0c;否则启动不不成功 2&#xff09;已经安装Sublime⽂文件编辑软件 3&#xff09;window电脑需要显示⽂文件拓拓展名 官网&#xff08;https:…