数据结构 C++语言版 清华大学第三版 学习笔记

2024-08-22 04:08

本文主要是介绍数据结构 C++语言版 清华大学第三版 学习笔记,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

绪论

绪论一道冒泡排序拍懵我了,我以为 O ( n 2 ) O(n^2) O(n2) 复杂度的经典冒泡排序没有优化空间了,结果一个bool标识打脸,可以提前终止冒泡,如果已经是按顺序了的数组的话:

 void bubblesort1A(int A[], int n) { //起泡排序算法(版本1A):0 <= n bool sorted = false; //整体排序标志,首先假定尚未排序 while (!sorted) { //在尚未确认已排序之前,逐行扫描交换 sorted = true; //假定已经排序 for (int i = 1; i < n; i++) { //自左向右逐对检查弼前范围A[0, n)内癿各相邻元素 if (A[i - 1] > A[i]) { //一旦A[i - 1]不A[i]逆序,则 swap(A[i - 1], A[i]); //交换 sorted = false; //因整体排序不能保证,需要清除排序标志 } } n--; //至此末元素必然就位,故可以缩短待排序序列癿有效长度 } } //布尔型标志位sorted,可及时提前退出,而不致总是蛮力地做n - 1趟扫描交换 

看来数据结构,路漫漫其修远兮,吾将上下而求索

算法具备的要素:

  1. 输入与输出
  2. 基本操作、确定性与可行性
  3. 有穷性与正确性
  4. 退化与鲁邦性
  5. 重用性(便捷的用于其他场合,如冒泡可推广至float和char)

1.4 递归

以下将从递归的基本模式入手,循序渐进地介绍如何选择和应用(线性递归、二分递归和多分支递归等)不同的递归形式,以实现(遍历、分治等)算法策略,以及如何利用递归跟踪和递
推方程等方法分析递归算法的复杂度。

1.4.1 线性递归

算法sum()可能朝着更深一层进行自我调用,且每一递归实例对自身的调用至多一次。于是,
每一层次上至多只有一个实例,且它们构成一个线性的次序关系。此类递归模式因而称作“线性递归”(linear recursion),它也是递归的最基本形式。

  int sum(int A[], int n) { //数组求和算法(线性递归版) if (1 > n) //平凡情况,递归基 return 0; //直接(非逑弻式)计算 else //一般情冴 return sum(A, n - 1) + A[n - 1]; //递归:前n - 1顷和,再累计第n - 1顷 } //O(1)递归深度 = O(1)*(n + 1) = O(n) 

1.4.2 递归分析

整个sum()算法的运行时间为:
(n + 1) X O(3) = O(n)
sum()算法的空间复杂度又是多少呢?在创建了最后一个递归实例(即到达递归基)时,占用的空间量达到最大。准确地说,等于所有递归实例各自所占空间量的总和。
这里每一递归实例所需存放的数据,无非是调用参数(数组A的起始地址和长度n)以及用于累加总和的临时变量。这些数据各自只需常数规模的空间,其总量也应为常数。故此可知,sum()算法的空间复杂度线性正比于其递归的深度,亦即O(n)

求解 p o w e r ( 2 , n ) = 2 n power(2,n) = 2^n power(2,n)=2n
power2(n) = 1 n==0
power2(b) = 2*power2(n-1) else
每层只有一个实例,这是一个线性递归, O(n)的空间复杂度,时间复杂度

优化:
power2(n) =
1            n==0
power2(n/2)^2 * 2     n奇数
power2(n/2) *2       n偶数
(n/2后按二进制展开)
还是线性递归,因为二者只选一条路。但是O(logn)的复杂度

1.4.4递归消除

空间成本 由于递归深度,空间占用往往较大;系统创建、销毁实例需要时间;因而往往写成等价的非递归版本(一般是利用栈结构)

尾递归及消除 线性递归算法中恰好以最后一步操作的形式出现(即所有实例都会终止在这一个递归调用)。称作尾递归tail recursion,这类均可转换为迭代

1.4.5 二分递归

分而治之。

        int mi = (lo + hi) >> 1; //以居中单元为界,将原区间一分为二 return sum(A, lo, mi) + sum(A, mi + 1, hi); //递归对各子数组求和,然后合计 

算法启动后经连续m = log 2 n次递归调用,每一刻活跃的实例不会超过m+1个,m为深度3,到达2^k的任一个递归实例之前,已执行的递归调用总比递归返回多m-k次
因此任意时刻实例总数不会超过m+1,是常数,空间复杂度O(m)即O(logn),比线性少。
每一次递归中非递归时间是常数,递归实例共2n-1,时间复杂度O(2n-1)即O(n)

必须保证子问题相互独立,可独立求解。否则就会变成递归的斐波那契数列。
O ( 2 n ) O(2^n ) O(2n) 的时间复杂度

  1. 借助辅助空间,子问题求解够记录下来(制表策略)
  2. 从递归基触发向上推(动态规划)
__int64 fibI(int n){__int64 f=0 , g=1;while(0 < n--){g +=f ; f = g-f;}return f;
}

1.5 抽象数据类型abstract data type , ADT

数据集合和对应操作超脱于具体程序设计语言,催生了面向对象程序设计语言

数据结构大致分为线性结构,半线性结构、非线性结构
线性结构中,按逻辑词语与物理存储地址的对应,区分
向量: 物理存放位置与逻辑次序吻合,逻辑次序也叫秩rank
列表: 采用间接定址的方式通过封装后的位置相互引用
数组,严格对应A0 A1 A2

向量

vector 容量:私有变量_capacity确定,当前实际规模由_size指示
向量中秩为r的元素,对应于内部数组中的_elem[r],其物理地址为_elem + r


若以复制形式copyFrom()初始化,则采用双倍现有容量来申请空间,O(n)时间(因为要一个一个的复制过去)(共有好几种构造函数方式可选)

需强调的是,由于向量内部含有动态分配的空间,默认的运算符"="不足以支持向量之间的
直接赋值。

(只允许有一种析构函数)O(1)时间
向量中元素可能不是程序语言直接支持的基本类型。所以,向量析构之前应当释放各元素所指的对象,这个由上层调用者负责

动态空间管理

2.4.2 可扩充向量extendable vector

图2.1
申请更大的B,把A复制过来,再删除A

分摊复杂度,分摊运行时间 与平均运行时间不同,后者称为期望运行时间。前者,比较复杂,具体问题再看。

最坏情况下,考虑不断连续insert,插入到很大很大的n时,共做过log2n次扩容,累计时间2N+4N+…+capacity(n) < 2*n = O(n)
平摊到n次操作,单次操作分摊运行时间O(1),令人满意

2.4.5缩容

_size/_capacity < 0.25时缩小一半,同上,单次运行确实是O(n),但是平摊之后变成O(1)。当然,阈值,策略有需求时可以改。

向量重载了A[i]的取值方式, return _elem[i] (其中0<=i < _size) ,取代了get()这种不方便的方式

这篇关于数据结构 C++语言版 清华大学第三版 学习笔记的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

Windows下C++使用SQLitede的操作过程

《Windows下C++使用SQLitede的操作过程》本文介绍了Windows下C++使用SQLite的安装配置、CppSQLite库封装优势、核心功能(如数据库连接、事务管理)、跨平台支持及性能优... 目录Windows下C++使用SQLite1、安装2、代码示例CppSQLite:C++轻松操作SQ

C++中RAII资源获取即初始化

《C++中RAII资源获取即初始化》RAII通过构造/析构自动管理资源生命周期,确保安全释放,本文就来介绍一下C++中的RAII技术及其应用,具有一定的参考价值,感兴趣的可以了解一下... 目录一、核心原理与机制二、标准库中的RAII实现三、自定义RAII类设计原则四、常见应用场景1. 内存管理2. 文件操

C++中零拷贝的多种实现方式

《C++中零拷贝的多种实现方式》本文主要介绍了C++中零拷贝的实现示例,旨在在减少数据在内存中的不必要复制,从而提高程序性能、降低内存使用并减少CPU消耗,零拷贝技术通过多种方式实现,下面就来了解一下... 目录一、C++中零拷贝技术的核心概念二、std::string_view 简介三、std::stri

C++高效内存池实现减少动态分配开销的解决方案

《C++高效内存池实现减少动态分配开销的解决方案》C++动态内存分配存在系统调用开销、碎片化和锁竞争等性能问题,内存池通过预分配、分块管理和缓存复用解决这些问题,下面就来了解一下... 目录一、C++内存分配的性能挑战二、内存池技术的核心原理三、主流内存池实现:TCMalloc与Jemalloc1. TCM

C++ 函数 strftime 和时间格式示例详解

《C++函数strftime和时间格式示例详解》strftime是C/C++标准库中用于格式化日期和时间的函数,定义在ctime头文件中,它将tm结构体中的时间信息转换为指定格式的字符串,是处理... 目录C++ 函数 strftipythonme 详解一、函数原型二、功能描述三、格式字符串说明四、返回值五

C++作用域和标识符查找规则详解

《C++作用域和标识符查找规则详解》在C++中,作用域(Scope)和标识符查找(IdentifierLookup)是理解代码行为的重要概念,本文将详细介绍这些规则,并通过实例来说明它们的工作原理,需... 目录作用域标识符查找规则1. 普通查找(Ordinary Lookup)2. 限定查找(Qualif

Go学习记录之runtime包深入解析

《Go学习记录之runtime包深入解析》Go语言runtime包管理运行时环境,涵盖goroutine调度、内存分配、垃圾回收、类型信息等核心功能,:本文主要介绍Go学习记录之runtime包的... 目录前言:一、runtime包内容学习1、作用:① Goroutine和并发控制:② 垃圾回收:③ 栈和

Android学习总结之Java和kotlin区别超详细分析

《Android学习总结之Java和kotlin区别超详细分析》Java和Kotlin都是用于Android开发的编程语言,它们各自具有独特的特点和优势,:本文主要介绍Android学习总结之Ja... 目录一、空安全机制真题 1:Kotlin 如何解决 Java 的 NullPointerExceptio

C/C++ chrono简单使用场景示例详解

《C/C++chrono简单使用场景示例详解》:本文主要介绍C/C++chrono简单使用场景示例详解,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友... 目录chrono使用场景举例1 输出格式化字符串chrono使用场景China编程举例1 输出格式化字符串示

C++/类与对象/默认成员函数@构造函数的用法

《C++/类与对象/默认成员函数@构造函数的用法》:本文主要介绍C++/类与对象/默认成员函数@构造函数的用法,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录名词概念默认成员函数构造函数概念函数特征显示构造函数隐式构造函数总结名词概念默认构造函数:不用传参就可以