复杂性函数的偏序关系

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

复杂性函数的偏序关系
排序算法
本文主要介绍了四种排序方法:插入排序、归并排序、快速排序和堆排序。

插入排序
插入排序相信大家都很熟悉了。

最简单的排序之一,思想就跟我们打牌时,把摸到的牌插入到自己手中正确的位置一样的。

特点:1.稳定;2.最坏情况下比较n*(n-1)/2次,最好情况下比较n-1次;3.第k次排序后,前k个元素已经是从小到大排好序的4.空间复杂度o(1)
functioninsertsort(arr){if(arr ==null|| arr.length
==0){return;}for(var i =1; i < arr.length;
i++){if(arr[i]< arr[i -1]){
tmp = arr[i];
j = i;while(j >0&& arr[j-1]> tmp){
arr[j]= arr[j-1];
j--;}
arr[j]= tmp;}
console.log(arr)}return arr;}var arr
=[7,5,8,3,4,0,9,2,1];var arr1 =null;insertsort(arr); console.log(arr)
通过对插入思路的领悟可以联想到是否可以用递归来改写,刚好再复习下递归:
functioninsertsortrecu(arr, i){if(arr ==null||
arr.length <= i){return;}
tmp = arr[i];
j = i;while(j >0&& arr[j -1]> tmp){
arr[j]= arr[j -1];
j--;}
arr[j]= tmp;insertsortrecu(arr, i +1);}var arr
=[7,5,8,3,4,0,9,2,1];insertsortrecu(arr,1)
console.log(arr)
归并排序
原理归并排序是用分治思想,将待排序元素分成大小大致相同的2个子集合,分别对2个子集合进行排序,最终将排好序的子集合合并。

特点
defmerge(arr1,arr2,arr):"""将两个列表是arr1,arr2按顺序融合为一个列表arr,arr为原列表"""
i = j =0while i+j<len(arr):if
j==len(arr2)or(i<len(arr1)and arr1[i]<arr2[j]):
arr[i+j]= arr1[i]
i +=1else:
arr[i+j]= arr2[j]
j +=1defmerge_sort(arr):"""归并排序"""
n =len(arr)if n <2:return
mid = n //2
arr1 = arr[0:mid]
arr2 = arr[mid:n]
merge_sort(arr1)
merge_sort(arr2)
merge(arr1,arr2,arr)
arr =[7,5,8,3,4,0,9,2,1]
merge_sort(arr)print(arr)
快速排序
原理:快速排序实现的重点在于数组的拆分,通常我们将数组的第一个元素定义为比较元素,然后将数组中小于比较元素的数放到左边,将大于比较元素的放到右边,这样我们就将数组拆分成了左右两部分:小于比较元素的数组;大于比较元素的数组。

我们再对这两个数组进行同样的拆分,直到拆分到不能再拆分,数组就自然而然地以升序排列了。

在快速排序中,记录的比较和交换是从两端向中间进行的,关键字较大的记录一次就能交换到后面单元,关键字较小的记录一次就能交换到前面单元,记录每次移动的距离较大,因而总的比较和移动次数较少。

defquick_sort(arr, start, end):"""快速排序"""if
start >= end:return
mid = arr[start]
low = start
high = end while low < high:while low < high and arr[high]>= mid:
high -=1
arr[low]= arr[high]while low < high and
arr[low]< mid:
low +=1
arr[high]= arr[low]
arr[low]= mid
quick_sort(arr, start, low -1)
quick_sort(arr, low +1, end)
arr =[7,5,8,3,4,0,9,2,1]
quick_sort(arr,0,len(arr)-1)print(arr)
堆排序
步骤:1、将待排序的数组初始化为大顶堆。

2、将堆顶元素与最后一个元素进行交换,除去最后一个元素外可以组建为一个新的大顶堆。

3、由于第二部堆顶元素跟最后一个元素交换后,新建立的堆不是大顶堆,需要重新建立大顶堆。

重复上面的处理流程,直到堆中仅剩下一个元素。

特点:1、最坏、平均时间复杂度为o(nlgn)。

2、由于建初始堆所需的比较次数
较多,所以堆排序不适宜于记录数较少的文件。

3、空间复杂度o(1),4、不稳定。

defmaxheap(heap,heapsize,i):"""大顶堆"""
left =2*i+1
right =2*i+2
larger = i
if left<heapsize and heap[larger]<heap[left]:
larger = left
if right<heapsize and heap[larger]<heap[right]:
larger = right
if larger != i:
heap[i],heap[larger]= heap[larger],heap[i]
maxheap(heap,heapsize,larger)defbuildmaxheap(heap):
size =len(heap)for i inrange((size-1)//2,-1,-1):
maxheap(heap,size,i)defmaxheapsort(heap):
buildmaxheap(heap)for i inrange(len(heap)-1,-1,-1): heap[0],heap[i],= heap[i],heap[0]
maxheap(heap,i,0)return heap
arr =[7,5,8,3,4,0,9,2,1]
maxheapsort(arr)print(arr)"""---------------------------------------------"""defminheap(heap,heapsize,i):"""小顶堆"""
left =2*i+1
right =2*i+2
larger = i
if left<heapsize and heap[larger]>heap[left]:
larger = left
if right<heapsize and heap[larger]>heap[right]:
larger = right
if larger != i:
heap[i],heap[larger]= heap[larger],heap[i]
minheap(heap,heapsize,larger)defbuildminheap(heap):
size =len(heap)for i inrange((size-1)//2,-1,-1):
minheap(heap,size,i)defminheapsort(heap):
buildminheap(heap)for i inrange(len(heap)-1,-1,-1): heap[0],heap[i],= heap[i],heap[0]
minheap(heap,i,0)return heap
minheapsort(arr)print(arr)
递归、动态规划
动态规划基本步骤
以下步骤都是要求背诵的:
•找出最优解的性质,并刻划其结构特征。

•递归地定义最优值。

•以自底向上的方式计算出最优值。

•根据计算最优值时得到的信息,构造最优解。

动态规划特点:
•最优子结构性质。

如果问题的最优解所包含的子问题的
解也是最优的,我们就称该问题具有最优子结构性质
(即满足最优化原理)。

最优子结构性质为动态规划算
法解决问题提供了重要线索。

•没有后遗症。

即一个子问题的解一旦确定,就不会改
变,也不会受到其后包含它的更大问题的解决策的影
响。

•子问题的重叠性。

子问题的重叠性是指当采用递归算法自上而下求解问题时,每次产生的子问题并不总是新问
题,有些子问题会重复计算多次。

动态规划算法利用了
这个子问题的重叠性,每个子问题只计算一次,然后将
其计算结果保存在一个表中。

当需要再次计算已经计算
好的子问题时,它只需查看表中的结果,从而获得更高
的效率。

贪心算法特点:
贪婪选择的本质是指问题的整体最优解可以通过一系列局部最优选择来实现,即贪婪选择。

这是贪婪算法可行的第一个基本要素,也是贪婪算法与动态规划算法的主要区别。

在贪心算法中,仅在当前状态下做出最好选择,即局部最优选择。

它不依赖于将来所做的选择,也不依赖于子问题的解。

动态规划算法通常以自底向上的方式解各子问题,而贪心算法则通常以自顶向下的方式进行,以迭代的方式作出相继的贪心选择,每作一次贪心选择就将所求问题简化为规模更小的子问题。

对于一个具体问题,要确定它是否具有贪心选择性质,必须证明每一步所作的贪心选择最终导致问题的整体最优解。

递归特点:
•递归就是方法里直接或简洁调用自身。

•在使用增减策略时,必须有一个确定的递归结束条件,称为递归出口。

•解题通常显得很简洁,但运行效率较低。

所以一般不提
倡用递归算法设计程序。

•在递归调用的过程当中系统为每一层的返回点、局部量
等开辟了栈来存储。

递归次数过多容易造成栈溢出等,
所以一般不提倡用递归算法设计程序。

分治特点:
•将一个复杂的问题分成两个或更多的相同或相似的子问
题,再把子问题分成更小的子问题
•将最后子问题可以简单的直接求解
•将所有子问题的解合并起来就是原问题的解
hanoi塔(汉诺塔)
汉诺塔问题算是非常经典的递归例题了,可以说搞懂了这个,也就懂了大半部分的递归~
设a,b,c是3个塔座。

开始时,在塔座a上有一叠共n个圆盘,这些圆盘自下而上,由大到小地叠在一起。

各圆盘从小到大编号为1,2,…,n,现要求将塔座a上的这一叠圆盘移到塔座b上,并仍按同样顺序叠置。

在移动圆盘时应遵守以下移动规则:
规则1:每次只能移动1个圆盘;规则2:任何时刻都不允许将较大的圆盘压在较小的圆盘之上;规则3:在满足移动规则1和2的前提下,可将圆盘移至a,b,c中任一塔座上。

1.首先理解题目,输入参数: a、b、c三个塔外加加盘子个数n输出:总移动次数,(当然移动过程也可以输出)
2.开始找规律,将问题变成数学问题1)n=1时:只有1个盘子,a–>b ,总共1次
2)n=2时:2个盘子,总共3次
第一次 1号盘 a-->c
第二次 2号盘 a-->b
第三次 1号盘 c-->b
3)n=3时:3个盘子,总共7次
第1次 1号盘 a---->b
第2次 2号盘 a---->c
第3次 1号盘 b---->c
第4次 3号盘 a---->b
第5次 1号盘 c---->a
第6次 2号盘 c---->b
第7次 1号盘 a---->b
不难发现规律,移动总次数为:2n - 1
移动规律为:
1.把n-1个盘子由a 移到 c
2.把第n个盘子由 a移到 b
3.把n-1个盘子由c 移到 b
cnt =0defmove(n, x, y):global cnt
cnt+=1print(f'第{str(cnt)}次, move{str(n)}号盘:{x}--->{y}')defhanoi(n, a, b, c):if n ==1: move(1, a, b)else:
hanoi(n -1, a, c, b)
move(n, a, b)
hanoi(n -1, c, b, a)
hanoi(3,'a','b','c')print(f'共计{str(cnt)}次')"""
第1次, move1号盘:a--->b
第2次, move2号盘:a--->c
第3次, move1号盘:b--->c
第4次, move3号盘:a--->b
第5次, move1号盘:c--->a
第6次, move2号盘:c--->b
第7次, move1号盘:a--->b
共计7次
"""
最长回文子串
给定一个字符串 s,找到 s中最长的回文子串。

示例:输
入:s = “babad”输出:“bab”解释:“aba” 同样是符合题意的答案。

动态规划一般解题思路:填dp表、当前ij状态、过去ij状态、如何联合得到输出、边界条件
定义状态:题目让求什么,就把什么设置为状态题目求s中最长的回文子串,那就判断所有子串是否为回文子串,选出最长的因此:dp[i][j]表示s[i:j+1]是否为回文子串(这里+1是为了构造闭区间)
状态转移方程:对空间进行分类讨论(当前ij状态、过去ij 状态如何联合得到输出)当前ij状态:头尾必须相等
(s[i]==s[j])
过去ij状态:去掉头尾之后还是一个回文(dp[i+1][j-1] is true)
边界条件:只要是找过去ij状态的时候,就会涉及边界条件(即超出边界情况处理)当i==j时一定是回文j-1-(i+1)<=0,即j-i<=2时,只要当s[i]==s[j]时就是回文,不用判断
dp[i+1][j-1]dp[i][j] 为截取的子串
初始状态:这里已经直接判断j-i<=2的情况了,因此用不到初始状态,可以不设
输出内容:每次发现新回文都比较一下长度,记录i与长度
deflongestpalindrome(s:str)->str:
size =len(s)if size ==1:return s
dp =[[falsefor _ inrange(size)]for _ inrange(size)] max_len =1
start =0for j inrange(1, size):for i inrange(j):if j-i <=2:if s[i]== s[j]:
dp[i][j]=true
cur_len = j-i+1else:if s[i]==
s[j]and dp[i+1][j-1]:
dp[i][j]=true
cur_len = j-i+1if dp[i][j]:if
cur_len > max_len:
max_len = cur_len
start = i
return s[start:start+max_len]
复杂性函数的偏序
写出下列复杂性函数的偏序关系(即按照渐进阶从低到高排序):
递归式求解——主定理
4、递归式求解方法。

然后看三种渐进界:定义看完直接上例子来说明:
例题
case1:case2:case3:
网络流
网络流:所有弧上流量的集合f={f(u,v)},称为该容量网络的
一个网络流.
可行流:在容量网络g中满足以下条件的网络流f,称为可行流.
1.弧流量限制条件: 0<=f(u,v)<=c(u,v);
2:平衡条件:即流入一个点的流量要等于流出这个点的流
量,(源点和汇点除外).
若网络流上每条弧上的流量都为0,则该网络流称为零流.
伪流:如果一个网络流只满足弧流量限制条件,不满足平衡条件,则这种网络流为伪流,或称为容量可行流.(预流推进有用)
最大流:在容量网络中,满足弧流量限制条件,且满足平衡条件并且具有最大流量的可行流,称为网络最大流,简称最大流.
例题
边上的数字为该条边的容量,即在该条边上流过的量的上限值。

最大流问题就是在满足容量限制条件下,使从起点s到终点t的流量达到最大。

做题前先看两个概念:残存网络上图为流网络,下图为残存网络,其中流网络中边上的数字分别是流量和容量,如4/16,那么4为边上的流量,16为边的容量。

残存网络中可能会存在一对相反方向的边,与流网络中相同的
边代表的是流网络中该边的剩余容量,在流网络中不存在的边代表的则是其在流网络中反向边的已有流量,这部分流量可以通过“回流”减少。

例如,下图残存网络中,边<s,v1>的剩余容量为12,其反向边<v1.s>的值为4。

在残存网络中,值为0的边不会画出,如边<v1,v2>。

增广路径接下来的ford-fulkerson方法中,正是通过在残存网络中寻找一条从s到t的增广路径,并对应这条路径上的各边对流网络中的各边的流进行修改。

如果路径上的一条边存在于流网络中,那么对该边的流增加,否则对其反向边的流减少。

增加或减少的值是确定的,就是该增广路径上值最小的边。

第一步:**第二步:**选择增广路径,更新流网络图和残存网络图第三步:第四步:第五步:所以,最大流为23.
多阶段决策的应用
最短路径问题:
线性规划的概念和构建
线性规划是研究在一组线性不等式或线性等式约束下使得某一线性目标函数取最大(或最小)的极值问题。

变量x x, x x, x x, … , x x满足所有约束条件的一组值称为线性规划问题的一个可行解。

所有可行解构成的集合称为线性规划问题的可行区域。

得到目标函数极值的可行解称为最优解。

目标函数在最优解处的值称为最优值。

如果一个线性规划没有可行解,称为不可行,否则称为可行。

有些情况下,可能不存在最优解。

通常有两种情况:•根本没有可行解,即给定的约束条件之间是相互排斥
的,可行区域为空集。

•目标函数没有极值,也就是说,在n维空间中的某个方
向上,目标函数值可以无限增大或减少,而仍满足约束
条件,此时目标函数无界。

回溯法和分支限界法的概念和主要特征
问题很多。

当需要找出它的解集或问什么解是满足某些约束的最佳解时,经常使用回溯法。

回溯法的基本做法是搜索,或是一种组织得井井有条的,能避免不必要搜索的穷举式搜索法。

这种方法适用于解一些组合数相当大的问题。

回溯法在问题的解空间树中,按深度优先策略,从根结点出发搜索解空间树。

•算法搜索至解空间树的任意一点时,先判断该结点是否包含问题的解。

•如果肯定不包含,则跳过对该结点为根的子树的搜索,逐层向其祖先结点回溯;•否则,进入该子树,继续按深度优先策略搜索
回溯法的基本思想(1)针对所给问题,定义问题的解空间;(2)确定易于搜索的解空间结构;(3)以深度优先方式搜索解空间,并在搜索过程中用剪枝函数避免无效搜索。

分支定界法与回溯法的区别在于:(1)求解目标:回溯法的目标是在解空间树中寻找所有满足约束条件的解,而分支定界法的目标是寻找满足约束条件的解或在满足约束条件的解中寻找某
种意义下的最优解。

(2)不同的搜索方法:回溯法先搜索深度的解空间树,分支限界法先搜索广度或最小代价的解空间树。

常见的两种分支限界法(1)队列式(fifo)分支限界法按照队列先进先出(fifo)原则选取下一个节点为扩展节点。

(2)优先队列式分支限界法按照优先队列中规定的优先级选取优先级最高的节点成为当前扩展节点。

相关文档
最新文档