变治法

变治法

变治法的基本思想

  • 首先是”变“, 将问题的实例变得更容易求解;然后是”治“,对问题的实例进行求解。
  • 变治法有三个变形:
    • 实例化简——同样问题
    • 改变表现——同样实例
    • 问题化简——另一问题

预排序

1、检验数组中元素的唯一性
 
PresortElementUniqueness(A[0..n-1])
    //先对数组排序再验证唯一性
    //输入:数组A
    //输出:若A没有相等的元素,返回“true”,否则返回“false”.
    对数组排序; 
    for i=0 to n-2 do
         if A[i]=A[i+1] return false
     return true

Gauss消去法

科学计算中通常需要解多个变量的方程组,这些方程组当中最简单的是线性方程组,也就是变量的次数均为1次的。
求解线性方程的方法通常有利用高斯消元的直接法以及迭代法。这里仅仅给出高斯消元法解线性方程组的介绍,它还可以应用于矩阵的求逆以及行列式的计算。

一般的线性方程组是指如下形式的方程组

在这里插入图片描述

分消元过程和回代过程。消元过程将原方程组变为上三角方程组,回代过程得到方程组的解。

在这里插入图片描述

高斯消元法举例:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  GaussElimination(A[n..n], b[1..n])
    // 输入:系数矩阵A及常数项 b
    // 输出:方程组的增广矩阵等价的上三角矩阵
  for i=1 to n do
    	A[i][n+1] =b[i]
      for i =1 to n-1 do
  	       for j= i+1 to n do
              for k = i to n+1 do
               A[j][k] = A[j][k] – A[i][k]*A[j][i]/A[i][i]

对于这段伪代码,我们要指出两个重要的事实.第一,它并不总是正确的:如果A[i,j]=0,我们不能以它为除数。第二个观察事实是,最内层循环的效率十分低。

算法  BetterGaussElimination(A[1…n,1…n],b[1…n])
//用部分选主元法实现高斯消去法
//输入:矩阵A[1…n,1…n]和列向量b[1…n]
//输出:一个代替A的上三角形等价矩阵图,右边的值位于第(n+1)列

For i = 1 to n do A[1..n,1..n] ,b[1..n]//把b作为最后一列添加到A中
For i = 1 to n-1 do 
      Pivotorow=i
      For  j=i+1 to n do
         If | A[j,i] |>|A[pivotrow,i] | pivotrow j
      For ki to n+1 do 
         Swap(A[i,k],A[pivotrow,i] | pivotrowj
         temp =A[j][i]/A[i][i]
      For j=i+1 to n+1 do
       A[j,k]=A[j,k]-A[i,k]*temp
  • 高斯消元法的效率分析
  • 基本操作:乘法
  • 执行次数:易见,三重循环
    C(n)∈Θ(n3)

LU分解法

记原方程组为 A X =b

若能将 A分解为下三角矩阵L与上三角矩阵U之积:
A=LU
则原方程组的求解转化为两个三角形方程组的求解了:
LY=b —— 下三角方程组
UX=Y ——上三角方程组

将A分解为 A=LU
在这里插入图片描述

计算矩阵的逆

求矩阵 A 的逆矩阵,转化为求解n个方程组
A Xj =bj

其中, bj是单位矩阵
在这里插入图片描述

的第j列,而Xj 则是逆矩阵的第j列。

平衡查找树

在介绍AVL树以前,首先一个核心的概念是二叉搜索树的平衡因子(balance factor)。它的定义为:对于二叉搜索树上任意结点,它的平衡因子是该结点左子树高度与右子树的高度差。

在这里插入图片描述

AVL树的平衡旋转

  • 对AVL树的一些操作如插入一个结点可能会导致AVL树左右子树不再平衡,此时需要通过AVL树的平衡旋转,使其重新平衡化。
  • 平衡旋转有4种类型,分别为:
    • 右单旋转(R-rotation),
    • 左单旋转(L-rotation),
    • 先左后右旋转(LR-rotation),
    • 先右后左旋转(RL-rotation)。
右单旋转

在这里插入图片描述
在这里插入图片描述

先左后右旋转

在这里插入图片描述

2-3树

2-3树是一种特殊的高度平衡树,它的结点与AVL树不同,允许结点最多包含两个关键字,所以结点的可能形态有两种,分别为两个元素的结点2-node与三个元素的结点3-node,如图所示。

在这里插入图片描述

2-3树的搜索与插入

对2-3树的搜索算法不难实现,主要由遇到的结点是2-node还是3-node类型决定。如果待搜索树的根是2-node型结点,搜索操作与二叉搜索树搜索操作相同;如果待搜索树的根是3-node型结点,最多只需要比较两次就可以知道是搜索成功还是需要向左右子树继续递归搜索

当一个结点x需要插入到2-3树中的时候,总是根据它的大小关系,把其插入到叶结点中。插入前首先调用搜索算法找到待插入的叶结点,如果该叶结点是2-node型的,则直接插入即可;如果该叶结点是3-node型的,在按序插入到叶结点后,需要把叶结点拆分(因为插入后使得叶结点的关键字个数为3,不满足2-3树的要求)。拆分过程首先在三个关键字挑选值在中间的关键字,提到上一层,或者作为新结点,或者插入原来的内结点中;关键字最小的作为左子树,关键字最大的作为右子树。如果内结点的插入导致结点过大,按照上述规则继续拆分。

在这里插入图片描述

堆和堆排序

“堆(heap)”概念的引入对于所谓的“优先级队列”的实现十分方便。优先级队列与普通的先进先出队列都是队尾加入,队头删除。不同之处在于优先级队列的删除总是把具有最大值(或者最小值)从队头删除,而堆正是这样的队列的最好实现结构。由此衍生而来的堆排序也是一种重要且高效的排序算法。

“堆”数据结构从本质上来说也是一棵二叉树,同时还要求这棵二叉树必须是一棵完全二叉树,树上的任意结点的值必须大于或等于其子结点的值,这样的堆通常称为“最大堆”。与之相对的,如果树上任意结点的值均小于或等于子结点的值,那么这样的堆通常称为“最小堆”。

在这里插入图片描述

堆的构建与堆结点的插入

堆的构建方法有两种,一种称为“自底向上构造法(bottom-up heap construction)”,一种称为“自顶向下构造法(top-down heap construction)”。其中“自底向上构造法”首先把数组按序填充到堆中各个结点,然后按照自下而上,从右至左的顺序,逐个结点考察是否都满足子结点比父结点小的约束条件。如果不满足则调换父子结点的位置,直到根结点结束。
在这里插入图片描述

算法 HeepBottom(H[1..n])
    //用自底向上算法,从给定数组的元素中构造一个堆
    //输入:一个可排序的数组H(1..n)
    //输出: 一个堆H(1..n)
    先对数组排序;
    for i ←          downto 1 do
       k ←i; v ←H[k]
       heap ←false
       while not heap and 2*k<=n do
            j ←2*k
            if  j<n //存在两个子女
                if  H[j]<H[j+1]  j ←j+1
            if  v>=H[j]
                 heap ←true
            else  H[k] ←H[j]; k ←j
    H[k] ←v

在最坏的情况下,该算法的效率是怎样的呢?为了简单起见,我们假设堆的树是满树,高度为h,也就是说,在每一层上,结点的数量都达到了最多。在堆构造算法的最坏情况下,每个位于树的第i层的键会移动到叶子层h中。因为移动到下一层需要进行两次比较——一次找出较大的子女,另一次确定是否需要交换——位于层i的键总共会需要2(h-i)次键值比较。所以,在最坏情况下,总的键值比较次数是

在这里插入图片描述

另一种算法(效率较低)通过把新的键连续插入预先构造好的堆,来构造一个新堆,称为自顶向下堆构造算法。首先,把一个包含键K的新节点附加在当前堆的最后一个叶子后面。然后按照下面的方法把K筛选到它的适当位置。拿K和它父母的键作比较:如果后者大雨等于K,算法停止(该结构已经是一个堆了);否则,交换这两个键并把K和他的新父母做比较。这种交换一直持续到K不大于它的最后一个父母,或者达到了树的根为止。在这个算法中,我们也可以把一个空节点向上筛选,知道达到合适的位置,才把K大值赋给它。

在这里插入图片描述

堆结点的删除

  • 把待删除结点与堆中索引最大的结点对调。
  • 执行删除操作并把堆的大小减一。
  • 对删除后的堆进行调整直到满足堆的约束条件。

在这里插入图片描述

堆排序

由于在一个堆中根结点数据总是所有数据中之最大者,利用堆阵排序的方法是从根结点逐个取出数据,每次将新的元素再提到根结点,如此反复进行。为了节约存储,要求排序得到的有序数据序列仍存放于原数组中,故将从根结点取出的数据由数组的末端起逐单元存放。每存放一个数据,同时将原在该单元的数据换到根结点,但这样互换后一般会破坏堆的条件,为此,需对根结点再做一次筛运算,就又可形成新的满足条件的堆。随着数组末端存放的由堆中取出的数据越来越多,堆的结点数逐渐减少,当到取出了(n-1)个数据,堆阵只剩下一个根结点,此最后一个数据一定是全部数据中的最小者,堆阵排序过程即全部结束。

Horner法则

计算n次多项式的值的算法。

在这里插入图片描述

二进制幂

计算an的算法,有两种方法:

从左到右逐位扫描算法:例求a13, 13=1101

13的二进制1101
中间结果a(a^2)*a = a^3(a3)2 = a^6(a6)2 * a = a^13

从右到左逐位扫描算法:例求a13, 13=1101

在这里插入图片描述

问题化简

求最小公倍数lcm(m,n)

由定义,求lcm(m,n)需要连续素数的表;利用求最大公约数gcd(m,n)的高效的欧几里德算法,可以简化求lcm(m,n)的计算过程。
可以验证 lcm(m,n)=m·n/gcd(m,n)

计算图中的路径数

可利用邻接矩阵,可以证明:
图G中顶点vi到顶点vj之间长度为k的路径数量等于AK的第(i,j)个元素,其中A是图G的邻接矩阵。

优化问题的化简

最大化与最小化:
min f(x)=-max[-f(x)]
函数最优化:
把最优化问题转化为函数极值问题,再由 f’(x)=0求临界点。

线性规划

许多决策优化问题可以转化为线性规划问题。线性规划问题有经典的单纯形法。Karmarkar算法也是一个好算法

简化为图论问题

许多问题用图表示后,求解很容易。通常用图的顶点表示问题的状态,边表示状态之间的可能转变。表示问题的图称为状态空间图。

例如过河问题:
一个农夫希望用一条小船把一只狼,一头羊,一篮白菜从河的北岸渡到河的南岸,由于船小只能够容纳人狼羊菜中的两个。需要考虑的约束条件是:在没有人的情况下,狼和羊不能在一起,羊和白菜不能单独在一起。求解一个渡船的方案,把狼、羊、白菜都运过去。

对过河问题,画出人、狼、羊、菜的状态空间图后即可以发现有两条路径,这两条路径就是问题的两个解。

  • 6
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
根据提供的引用内容,减治法拓扑排序是一种基于DFS的算法,可以用于解决拓扑排序问题。下面是一个使用C语言实现减治法拓扑排序的例子: ```c #include <stdio.h> #include <stdlib.h> #define MAX_VERTEX_NUM 100 // 最大顶点数 typedef struct ArcNode { int adjvex; // 邻接点在数组中的位置下标 struct ArcNode *nextarc; // 指向下一个邻接点的指针 } ArcNode; typedef struct VNode { int data; // 顶点信息 ArcNode *firstarc; // 指向第一个邻接点的指针 int indegree; // 顶点的入度 } VNode, AdjList[MAX_VERTEX_NUM]; typedef struct { AdjList vertices; // 邻接表 int vexnum, arcnum; // 顶点数和弧数 } ALGraph; // 初始化邻接表 void InitGraph(ALGraph *G) { int i; G->vexnum = G->arcnum = 0; for (i = 0; i < MAX_VERTEX_NUM; i++) { G->vertices[i].firstarc = NULL; G->vertices[i].indegree = 0; } } // 添加一条边 void AddArc(ALGraph *G, int u, int v) { ArcNode *p = (ArcNode *)malloc(sizeof(ArcNode)); p->adjvex = v; p->nextarc = G->vertices[u].firstarc; G->vertices[u].firstarc = p; G->vertices[v].indegree++; G->arcnum++; } // 减治法拓扑排序 void TopoSort(ALGraph *G) { int i, j, k, count = 0; int topo[MAX_VERTEX_NUM]; // 存储拓扑序列的数组 ArcNode *p; for (i = 0; i < G->vexnum; i++) { if (G->vertices[i].indegree == 0) { // 入度为0的顶点入队 topo[count++] = i; } } for (i = 0; i < count; i++) { // 依次删除队头顶点及其所有出边 k = topo[i]; printf("%d ", G->vertices[k].data); // 输出拓扑序列 for (p = G->vertices[k].firstarc; p; p = p->nextarc) { j = p->adjvex; if (--G->vertices[j].indegree == 0) { // 入度为0的顶点入队 topo[count++] = j; } } } } int main() { ALGraph G; int i, u, v; printf("请输入顶点数和弧数:"); scanf("%d%d", &G.vexnum, &G.arcnum); printf("请输入%d个顶点的值:", G.vexnum); for (i = 0; i < G.vexnum; i++) { scanf("%d", &G.vertices[i].data); } printf("请输入%d条弧的起点和终点:", G.arcnum); for (i = 0; i < G.arcnum; i++) { scanf("%d%d", &u, &v); AddArc(&G, u, v); } printf("拓扑序列为:"); TopoSort(&G); printf("\n"); return 0; } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值