数据结构和算法(1):开始

算法概述

所谓算法,即特定计算模型下,旨在解决特定问题的指令序列
输入 待处理的信息(问题)
输出 经处理的信息(答案)
正确性 的确可以解决指定的问题
确定性 任一算法都可以描述为一个由基本操作组成的序列
可行性 每一基本操作都可实现,且在常数时间内完成
有穷性 对于任何输入,经有穷次基本操作,都可以得到输出

程序未必是算法,例如发生死循环或者栈溢出时。

算法在满足基本要求时,最重要的是:速度尽可能快,存储空间尽可能少(效率)

计算模型

两个主要方面:
1.正确性:算法功能与问题要求一致?
2.成本:运行时间 + 存储空间

计算成本: T ( n ) = max ⁡ { T ( P ) ∣ ∣ P ∣ = n } T(n)=\max \{T(P) \space \boldsymbol | \space |P| = n \} T(n)=max{T(P)  P=n} 遵守最坏情况分析原则。

特定问题,不同算法下,需要抽象出一种理想的平台或模型,不再依赖于种种具体因素,从而直接准确地描述、测量并评价算法。

渐进复杂度

随着问题规模地增长,运算成本增大
T ( n ) = O ( f ( n ) ) if  ∃ c > 0 , n ≫ 2 , T ( n ) < c ⋅ f ( n ) T(n) = \mathcal O(f(n)) \space \text{if } \exists \space c>0,n\gg 2, T(n)<c\cdot f(n) T(n)=O(f(n)) if  c>0,n2,T(n)<cf(n)

T ( n ) T(n) T(n) 相比, f ( n ) f(n) f(n)更为简洁,但依然反应前者地增长趋势:

常系数可忽略: O ( f ( n ) ) = ( c × f ( n ) ) \mathcal O(f(n)) = (c \times f(n)) O(f(n))=(c×f(n))
低次项可忽略: O ( n a + n b ) = O ( n a ) , a > b > 0 \mathcal O(n^a+n^b)=\mathcal O(n^a),a>b>0 O(na+nb)=O(na)a>b>0
在这里插入图片描述
1.常数复杂度为: O ( 1 ) \mathcal O(1) O(1)
算法不含转向(循环、调用、递归等),必顺序执行即复杂度为 O ( 1 ) \mathcal O(1) O(1)

2.对数复杂度为: O ( log ⁡ n ) \mathcal O(\log n) O(logn)
∀ c > 0 , l o g ( n ) = O ( n c ) \forall c>0,log(n)=\mathcal O(n^c) c>0,log(n)=O(nc),因此对数复杂度无限接近于常数

3.多项式复杂度: O ( n c ) \mathcal O(n^c) O(nc)

4.指数复杂度: O ( a n ) \mathcal O(a^n) O(an)
计算成本增长极快,通常认为不可以接受

复杂度增长速度
在这里插入图片描述

复杂度分析

算法分析的两个主要任务 = 正确性(不变性×单调性) + 复杂度

C++ 等高级语言的基本指令,均等效于常数条 RAM 的基本指令;在渐进意义下,两者相当。

复杂度分析的主要方法:
1.迭代:级数求和;
2.递归:递归追踪 + 递推方程;

实例:冒泡排序

问题:给定 n 个整数,将它们按(非降)序排列
观察:有序/无序序列中,任意/总有一对相邻元素顺序/逆序
思路:(扫描交换)依次比较每一个相邻元素,如果必要,交换之,若整躺扫描都没有进行交换,则排序完成;否则,再做一趟扫描交换。

void bubblesort(int A[],int n){for(bool sorted = false; sorted = !sorted; n--){	// 逐躺扫描交换,直至完全有序for(int i = 1; i< n; i++){	// 自左向右,逐对检查A[0,n)内各相邻元素if(A[i-1]>A[i]){	// 若逆序,则swap(A[i-1], A[i]);	//令其互换,同时sorted = false; //清楚(全局)有序标志	}}}
}

不变性:经过 k 轮扫描交换后,最大的 k 个元素必然就位;
单调性:经过 k 轮扫描交换后,问题规模缩减至 n-k;
正确性:经过最多 n 躺扫描后,算法必然终止,且能正确解答。

迭代与递归

递归跟踪分析:检查每个递归实例,累计所需时间(调用语句本身,计入对应的子实例),其总和即算法执行时间。

实例:数组求和(二分递归)

int sum(int A[], int lo, int hi){	//区间范围A[lo, hi]if(lo == hi) return A[lo];	//base caseint mi = (lo + hi) >> 1;	//右移一位,相当于除以2 只有正数适用,而负数不适用return sum(A, lo, mi) + sum(A, mi+1, hi);
}	//入口形式为 sum(A,0,n-1)

master theorem

在这里插入图片描述

动态规划

实例:Fibonacci 序列

F ( 1 ) = 1 , F ( 2 ) = 1 , F ( n ) = F ( n − 1 ) + F ( n − 2 ) ( n > = 3 , n ∈ N ∗ ) F(1)=1,F(2)=1, F(n)=F(n-1)+F(n-2)(n>=3,n∈N*) F(1)=1F(2)=1,F(n)=F(n1)+F(n2)(n>=3nN)

计算Fibonacci数列的第n项(迭代版):O(n)

__int64 fibI ( int n ) { __int64 f = 1, g = 0; //初始化:fib(-1)、fib(0)while ( 0 < n-- ) { g += f; f = g - f; } //依据原始定义,通过n次加法和减法计算fib(n)return g; //返回
}

计算Fibonacci数列的第n项(二分递归版):O(2^n)

__int64 fib ( int n ) { return ( 2 > n ) ?( __int64 ) n //若到达递归基,直接取值: fib ( n - 1 ) + fib ( n - 2 ); //否则,递归计算前两项,其和即为正解
}

计算Fibonacci数列第n项(线性递归版):O(n)

__int64 fib ( int n, __int64& prev ) { //入口形式fib(n, prev)if ( 0 == n ) //若到达递归基,则{ prev = 1; return 0; } //直接取值:fib(-1) = 1, fib(0) = 0else { //否则__int64 prevPrev; prev = fib ( n - 1, prevPrev ); //递归计算前两项return prevPrev + prev; //其和即为正解}
} //用辅助变量记录前一项,返回数列的当前项,O(n)
//Fib.h
using Rank = unsigned int;class Fib { //Fibonacci数列类
private:Rank f, g; //f = fib(k - 1), g = fib(k)。均为int型,很快就会数值溢出
public:Fib ( Rank n ) //初始化为不小于n的最小Fibonacci项{ f = 1; g = 0; while ( g < n ) next(); } //fib(-1), fib(0),O(log_phi(n))时间Rank get()  { return g; } //获取当前Fibonacci项,O(1)时间Rank next() { g += f; f = g - f; return g; } //转至下一Fibonacci项,O(1)时间Rank prev() { f = g - f; g -= f; return g; } //转至上一Fibonacci项,O(1)时间
};//main.c
#include<ctime>
#include<iostream>
using namespace std;#include "Fib.h"__int64  fibI ( int n ); //迭代版
__int64  fib ( int n ); //二分递归版
__int64  fib ( int n, __int64& f ); //线性递归版int main ( int argc, char* argv[] ) { //测试FIB
// 检查参数if ( 2 > argc ) { fprintf ( stderr, "Usage: %s <Rank>\n", argv[0] ); return 1; }int n = atoi ( argv[1] );
// 依次计算Fibonacci数列各项printf ( "\n------------- class Fib -------------\n" );Fib f ( 0 );for ( int i = 0; i < n; i++, f.next() )printf ( "fib(%2d) = %d\n", i, f.get() );for ( int i = 0; i <= n; i++, f.prev() )printf ( "fib(%2d) = %d\n", n - i, f.get() );printf ( "\n------------- Iteration -------------\n" );for ( int i = 0; i < n; i++ )printf ( "fib(%2d) = %22I64d\n", i, fibI ( i ) );printf ( "\n------------- Linear Recursion -------------\n" );for ( int i = 0; i < n; i++ ) {__int64 f;printf ( "fib(%2d) = %22I64d\n", i, fib ( i, f ) );}printf ( "\n------------- Binary Recursion -------------\n" );for ( int i = 0; i < n; i++ )printf ( "fib(%2d) = %22I64d\n", i, fib ( i ) );return 0;
}

实例:LCS:最长公共子序列

两个字符串中找到最长的子序列,这里明确两个含义:
1.子串:表示连续的一串字符 。
2.子序列:表示不连续的一串字符。

1.两个字符串具有相同尾序,那么同时去掉两者的尾序,不影响它们的距离
2.如果 A 和 B 是不同的符号 ( A ≠ B A≠B A=B),则 L C S ( X A , Y B ) LCS(X^A,Y^B) LCS(XA,YB) 是以下两者的最大者: L C S ( X A , Y ) , L C S ( X , Y B ) LCS(X^A,Y), LCS(X,Y ^B) LCS(XA,Y),LCS(X,YB) ,适用于所有字符串 X 、 Y X、Y XY

给定两个字符串S1和S2,我们需要找到一个最长的子序列,该子序列同时出现在S1和S2中。这个子序列不要求在原字符串中是连续的,但在原字符串中的相对顺序必须与原字符串中的顺序相同。

举例说明:

假设有两个字符串:
S1 = “ABCBDAB”
S2 = “BDCAB”

它们的一个最长公共子序列是"BCAB",它在两个字符串中都出现,而且是最长的。

LCS问题的目标是找到这个最长的公共子序列的长度以及可能的子序列之一。在动态规划中,可以使用一个二维表格来解决这个问题,表格中的值表示两个字符串在不同位置的字符之间的LCS长度。

通过解决LCS问题,我们可以解决许多实际应用,如文本比对、版本控制、DNA序列比对等。这个问题在算法设计和字符串处理中具有重要性。

#include <iostream>
#include <vector>
#include <string>using namespace std;string longestCommonSubsequence(string s1, string s2) {int m = s1.length();int n = s2.length();// 创建DP表,初始化为0vector<vector<int>> dp(m + 1, vector<int>(n + 1, 0));// 填充DP表for (int i = 1; i <= m; ++i) {for (int j = 1; j <= n; ++j) {if (s1[i - 1] == s2[j - 1]) {dp[i][j] = dp[i - 1][j - 1] + 1;} else {dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);}}}// 回溯构建最长公共子序列string lcs = "";int i = m, j = n;while (i > 0 && j > 0) {if (s1[i - 1] == s2[j - 1]) {lcs = s1[i - 1] + lcs;i--;j--;} else if (dp[i - 1][j] > dp[i][j - 1]) {i--;} else {j--;}}return lcs;
}int main() {string s1 = "ABCBDAB";string s2 = "BDCAB";string result = longestCommonSubsequence(s1, s2);cout << "Longest Common Subsequence: " << result << endl;return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

layui实现数据列表的复选框回显

layui版本2.8以上 实现效果如图&#xff1a; <input type"hidden" name"id" id"id" value"{:g_val( id,0)}"> <div id"tableDiv"><table class"layui-hide" id"table_list" lay-filter…

聊聊如何玩转spring-boot-admin

前言 1、何为spring-boot-admin&#xff1f; Spring Boot Admin 是一个监控工具&#xff0c;旨在以良好且易于访问的方式可视化 Spring Boot Actuators 提供的信息 快速开始 如何搭建spring-boot-admin-server 1、在服务端项目的POM引入相应的GAV <dependency><grou…

【Unity-Cinemachine相机】虚拟相机旋转Composer属性详解

Look At和Aim本质是控制虚拟相机自身的旋转&#xff0c;并不一定非要看着&#xff0c;或者并不一定非要瞄着这里的游戏对象 Look At和Aim是以Look At里面的对象作为参考来旋转自身 Do nothing&#xff1a;表现出来的行为和Look At空出来一样 Composer&#xff1a;是一个标准的…

【RuoYi移动端】uni-app中通过vuex的store来实现全局变量的修改和读取

一、在store文件中新建csjVar.js文件 const csjVar {csjMess: [{aaa:"ok"},{bbb:"no"}] } export default csjVar 二、修改store文件中新建index.js文件 import Vue from vue import Vuex from vuex import user from /store/modules/user import gette…

浅谈城市轨道交通视频监控与AI视频智能分析解决方案

一、背景分析 地铁作为重要的公共场所交通枢纽&#xff0c;流动性非常高、人员大量聚集&#xff0c;轨道交通需要利用视频监控系统来实现全程、全方位的安全防范&#xff0c;这也是保证地铁行车组织和安全的重要手段。调度员和车站值班员通过系统监管列车运行、客流情况、变电…

Redis-Cluster集群的部署(详细步骤)

一、环境准备 本次实操为三台机器&#xff0c;关闭防火墙和selinux 注:规划架构两种方案&#xff0c;一种是单机多实例&#xff0c;这里我们采用多机器部署 三台机器&#xff0c;每台机器上面两个redis实例&#xff0c;一个master一个slave&#xff0c;第一列做主库&#xff…

蝶形运算法

蝶形运算法是一种基于FFT&#xff08;Fast Fourier Transform&#xff09;算法的计算方法&#xff0c;其基本思想是将长度为N的DFT分解成若干个长度为N/2的DFT计算&#xff0c;并通过不断的合并操作得到最终的结果。该算法也称为“蝴蝶算法”&#xff0c;因为它的计算过程中需要…

回归拟合 | 灰狼算法优化核极限学习机(GWO-KELM)MATLAB实现

这周有粉丝私信想让我出一期GWO-KELM的文章&#xff0c;因此乘着今天休息就更新了(希望不算晚) 作者在前面的文章中介绍了ELM和KELM的原理及其实现&#xff0c;ELM具有训练速度快、复杂度低、克服了传统梯度算法的局部极小、过拟合和学习率的选择不合适等优点&#xff0c;而KEL…

【算法系列篇】分治-归并

文章目录 前言什么是归并算法1. 排序数组1.1 题目要求1.2 做题思路1.3 Java代码实现 2. 数组中逆序对2.1 题目要求2.2 做题思路2.3 Java代码实现 3. 计算右侧小于当前元素的个数3.1 题目要求3.2 做题思路3.3 Java代码实现 4. 翻转对4.1 题目要求4.2 做题思路4.3 Java代码实现 总…

QT DAY 4

时钟&#xff1a; #include "widget.h" #include "ui_widget.h"int hour0; int min0; int sec0; int count0; Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this);this->setFixedSize(800,600);timer new …

【MySql】数据库的约束

写在最前面的话 哈喽&#xff0c;宝子们&#xff0c;今天给大家带来的是MySql数据库的约束&#xff0c;约束是自动的对数据的合法性进行校验检查的一系列机制&#xff0c;目的就是为了保证数据库中能够避免被插入/修改非法的数据。因为有的时候数据库中的数据是有一定要求的&am…

Android中的view绘制流程,简单理解

简单理解 Android中的View类代表用户界面中基本的构建块。一个View在屏幕中占据一个矩形区域、并且负责绘制和事件处理。View是所有widgets的基础类&#xff0c;widgets是我们通常用于创建和用户交互的组件&#xff0c;比如按钮、文本输入框等等。子类ViewGroup是所有布局&…