IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> (十七)深入学习 -- 3. 算法分析 -> 正文阅读

[数据结构与算法](十七)深入学习 -- 3. 算法分析

3. 算法分析

3.1 评估算法效率

  • 算法的计算复杂性
    字母N表示问题规模,当N值变大时,N和算法运行时间之间的关系被称为该算法的计算复杂性(computational complexity)。


3.2 O标记

  • O标记
    计算机科学家用–种特殊记号表示算法的计算复杂性。这个符号为O标记(读作Big Oh)。

这个符号由一个大写的字母O及其后的一个用圆括号括起的公式组成,该公式表示运行时间随问题规模变化的函数。

字母O代表单词order(顺序),指的是近似值。


  • 恒定时间
    如果一个操作所需的时间与问题规模无关,则说该操作以恒定时间(constant time) 运行。

在O标记中,恒定时间用O(1)表示。O(1)的意思是当N变大时,程序的运行时间是常数,这是恒定时间操作的一个显著特征。


Dequeue的实现用下列for循环把该队列中每一项向数组头方向移一步:

for (i=1; i<queue->len; i++) {
	queue->array[i-1] = queue->array[i];
}

如果队列包含N项,这个for循环就要执行N个周期。随着N的增加,for循环执行时间成正比例增加。

  • 线性时间
    如果一个操作的运行时间与问题规模成正比,则称此操作以线性时间(linear time)运行,用O标记表示为O(N)。


3.3 再看选择排序

如果给出一个有N个元素的数组,选择排序算法要访问每一个数组元素的位置,并确定该位置应该存放的值。

总的运行时间的公式可化简为:

N 2 + N 2 \frac {N^{2}+N}{2} 2N2+N?

当使用O标记来估计一个算法的计算复杂性时,目的是提供一种量化手段,以便衡量当N变大时,N的变化是如何影响算法的性能。

因为O标记并非一种精确的量化手段,所以我们最好能简化括号中的表达式,以便用最简单的形式量化算法的行为。

对圆括号里的公式应用下列步骤,可以简化O标记:
1)消去公式中任何随N变大而变得无足轻重的项。
2)消去任何常数系数。

用来表示选择排序的复杂性的表达式应为:
O ( N 2 ) O(N2) O(N2)

  • 平方时间
    运行性能为O(N2)表示的算法被称为以平方时间(quadratic time)运行。


3.4 分而治之策略

一旦发现怎样在一个层面上改善性能,就能用同样的算法递归地为每一个子数组排序。

  • 分而治之算法
    把一个问题分解成基本相等的子问题,并递归地解决每一个子问题, 这样的递归算法称为分而治之(divide-and-conquer)算法。

要确定分而治之方法是否适用于排序问题,来看一个有8个元素的数组的例子。

如果把这个有8个元素的数组分成2个有4个元素的数组,然后对每一个子数组排序。将得到如下结果:


3.5 合并两个数组

  • 合并
    把已排序的子数组重组成一个完整数组,这种技术叫合并(merging)。

它基于一个事实,即完整的排序的第一个元素只能是arr1或arr2的第一个元素,但要取更小的那个元素作为第一个元素。

在这个例子中, 新数组中的第一个元素是arr1中的26。如果把这个元素放入array[0],实际上,就是把它从arr1中划掉,结果得到以下结构:

接着,下一个元素只能是两个子数组中第一个未用过的元素。把arr1中的31和arr2中的53比较,并选择前者:

可以继续在arr1、arr2中选择较小值的过程,直到整个数组被填满为止。



3.6 合并排序算法

  • 合并排序
    合并操作与递归分解相结合,产生了一个新的排序算法——合并排序(mergesort)。

实现这个算法的方法如下:

函数SortIntegerArray首先判定数组的大小。如果数组没有元素或只有一个元素,那么数组必然已经被排过序。

如果数组包含的元素多于1个,就需要执行下列步骤。
1)把数组分成两个较小的子数组,每一个数组的大小是原数组的一半。
2) 递归调用Sort Integer Array为每一个小数组排序。
3) 合并两个小数组,写回原数组。


函数SortIntegerArray本身的代码如下:

void SortIntegerArray(int array[], int n) {
	int i, n1, n2;
	int *arr1, *arr2;

	if (n>1) {

		n1 = n/2;
		n2 = n-n1;

		arr1 = NewArray(n1, int);
		arr2 = NewArray(n2, int);

		for (i=0; i<n1; i++) {
			arr1[i] = array[i];
		}
		for (i=0; i<n2; i++) {
			arr2[i] = array[n1+i];
		}
		
		SortIntegerArray(arr1, n1);
		SortIntegerArray(arr2, n2);

		Merge(array, arr1, n1, arr2, n2);
		
		FreeBlock(arr1);
		FreeBlock(arr2);

	}
}

在这个实现中,arr1和arr2是较小的数组,它们有效长度分别为n1和n2,所有困难的工作都由Merge完成。


Merge实现如下:

static void Merge(int array[], int arr1[], int n1, int arr2[], int n2) {
	int p, p1, p2;
	
	p = p1 = p2 = 0;
	while (p1<n1 & p2<n2) {
		if (arr1[p1]<arr2[p2]) {
			array[p++]=arr1[p1++];
		} else {
			array[p++]=arr2[p2++];
		}
	}
	while (p1<n1) array[p++]=arr1[p1++];
	while (p2<n2) array[p++]=arr1[p2++];
}

Merge函数的参数有目标数组以及较小的数组arr1和arr2,还有它们的有效长度n1和n2。

下标p1和p2标志着每一个子数组的进度,p是array的下标。

在每一个循环周期中,函数从arr1或arr2中选择一个较小元素,把该值复制到array中。一旦任何一个子数组的元素用完,该函数就直接把另一个子数组中剩下的元素复制到目标数组中。



3.7 合并排序的计算复杂性

当调用合并排序的实现(SortIntegerArray函数)为N个数字排序时,运行时间可分成下面两部分:

  1. 在当前的递归分解层次上,执行操作所需的所有时间。

  2. 执行递归调用的时间。

在递归分解的最上面一层, 执行非递归操作所消耗的时间与N成正比。

任何一次SortIntegerArray调用的复杂性(如果不考虑其中的递归调用)需要O(N)次操作。


递归操作所需的总时间是递归分解的每一层所消耗的时间之和。总的来说,分解的结构如图17-3所示:

每一层的工作量总是和N直接成正比的。因此,确定工作总量就转化为确定层数的问题。

在递归层次结构的每一层中,N值都除以2。总层数等于在N=1之前将N除以2的次数。

用数学术语表述这个问题,就是说必须找到一个这样的k值,使得:
N = 2 k N=2^{k} N=2k

得到k的值为:
k = l o g 2 N k=log_2N k=log2?N

因为层数为 k = l o g 2 N k=log_2N k=log2?N,且每一层的工作量与N成正比,所以总工作量与 N l o g 2 N Nlog_2N Nlog2?N成正比。


在计算机科学中,总是使用二进制对数(binary logarithm),即以2为底数的对数。

谈到计算复杂性的问题时,可以忽略对数的底数。这样,合并排序的计算复杂性可以写为:
O ( N l o g N ) O(NlogN) O(NlogN)

  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2021-07-28 00:18:38  更:2021-07-28 00:19:12 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年12日历 -2024/12/27 9:46:08-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码
数据统计