数据结构算法浅谈

1.贪心算法

(1)基本要素

贪心选择
贪心选择是指所求问题的整体最优解可以通过一系列局部最优的选择,即贪心选择来达到。这是贪心算法可行的第一个基本要素,也是贪心算法与动态规划算法的主要区别。贪心选择是采用从顶向下、以迭代的方法做出相继选择,每做一次贪心选择就将所求问题简化为一个规模更小的子问题。对于一个具体问题,要确定它是否具有贪心选择的性质,我们必须证明每一步所作的贪心选择最终能得到问题的最优解。通常可以首先证明问题的一个整体最优解,是从贪心选择开始的,而且作了贪心选择后,原问题简化为一个规模更小的类似子问题。然后,用数学归纳法证明,通过每一步贪心选择,最终可得到问题的一个整体最优解。
最优子结构
当一个问题的最优解包含其子问题的最优解时,称此问题具有最优子结构性质。运用贪心策略在每一次转化时都取得了最优解。问题的最优子结构性质是该问题可用贪心算法或动态规划算法求解的关键特征。贪心算法的每一次操作都对结果产生直接影响,而动态规划则不是。贪心算法对每个子问题的解决方案都做出选择,不能回退;动态规划则会根据以前的选择结果对当前进行选择,有回退功能。动态规划主要运用于二维或三维问题,而贪心一般是一维问题。

(2)基本思路

贪心算法的基本思路是从问题的某一个初始解出发一步一步地进行,根据某个优化测度,每一步都要确保能获得局部最优解。每一步只考虑一个数据,他的选取应该满足局部优化的条件。若下一个数据和部分最优解连在一起不再是可行解时,就不把该数据添加到部分解中,直到把所有数据枚举完,或者不能再添加算法停止。
过程:
1.建立数学模型来描述问题;
2.把求解的问题分成若干个子问题;
3.对每一子问题求解,得到子问题的局部最优解;
4.把子问题的解局部最优解合成原来解问题的一个解。

(3)算法特性

贪婪算法可解决的问题通常大部分都有如下的特性:
1.随着算法的进行,将积累起其它两个集合:一个包含已经被考虑过并被选出的候选对象,另一个包含已经被考虑过但被丢弃的候选对象。
2.有一个函数来检查一个候选对象的集合是否提供了问题的解答。该函数不考虑此时的解决方法是否最优。
3.还有一个函数检查是否一个候选对象的集合是可行的,也即是否可能往该集合上添加更多的候选对象以获得一个解。和上一个函数一样,此时不考虑解决方法的最优性。
4.选择函数可以指出哪一个剩余的候选对象最有希望构成问题的解。
5.最后,目标函数给出解的值。
6.为了解决问题,需要寻找一个构成解的候选对象集合,它可以优化目标函数,贪婪算法一步一步的进行。起初,算法选出的候选对象的集合为空。接下来的每一步中,根据选择函数,算法从剩余候选对象中选出最有希望构成解的对象。如果集合中加上该对象后不可行,那么该对象就被丢弃并不再考虑;否则就加到集合里。每一次都扩充集合,并检查该集合是否构成解。如果贪婪算法正确工作,那么找到的第一个解通常是最优的。

(4)例题分析

背包问题

有一个背包,背包容量是M=150kg。有7个物品,物品不可以分割成任意大小。要求尽可能让装入背包中的物品总价值最大,但不能超过总容量。
物品 A B C D E F G
重量 35kg 30kg 6kg 50kg 40kg 10kg 25kg
价值 10$ 40$ 30$ 50$ 35$ 40$ 30$

C++代码示例:

#defian N 8
void fds(int x, int y, int count)
{
       int   i,tx,ty;
       if(count > N*N)
       {
            output_solution();
            return;
       }
       for(i = 0; i < 8; i++)
       {
             tx = hn[i].x;
             ty = hn[i],y;
             s[tx][ty] = count;
             fds(tx, ty, count+1);
             s[tx][ty] = 0;
       }
}

2.穷举算法

(1)基本概述

广度优先搜索

广度优先搜索(Breadth- First- Search)也称为宽度优先搜索,它是一种按”先产生的节点先扩展”的原则进行的搜索。搜索的过程是:从初始节点A开始,逐层地对节点进行扩展并考察它是否为目标节点,在第n层节点没有全部扩展并考察之前,不对第n十1层节点进行扩展。
广度搜索是逐层进行的。它把起始节点放到OPEN中(如果该起始节点为一目标节点,则求得一个解答);如果OPEN表是个空表,则没有解,失败退出;否则继续;把第一个节点(节点n)从OPEN表移出,并把它放入CLOSED扩展节点表中;扩展节点n如果没有后继节点,则转回;把n的所有后继节点放到OPEN表的末端,并提供从这些后继节点回到n指针;如果n的任一个后继节点是个目标节点,则找到解,成功退出;否则转回。
广度优先搜索这种策略是完备的,即如果问题的解存在,用它则一定能找到解,且找到的解还是最优解(即最短的路径),但它的缺点是搜索效率低。

深度优先搜索

深度优先搜索(Depth- first- Search)亦称为纵向搜索,它是从树根开始一枝一枝逐渐生成,是一种后生成的节点先扩展的搜索方法。首先,扩展最深的节点的结果使得搜索沿着状态空间某条单一的路径从起始节点向下进行;只有当搜索到一个没有后裔的状态时,它才考虑另一条替代的路径(替代路径与前面已经试过的路径不同之处仅仅在于改变最后n步,而且保持n尽可能小)。
深度优先搜索所遵循的搜索策略是尽可能”深”地搜索图,它把起始节点放到未扩展节点OPEN表中,如果此节点为一目标节点,则得到一个解;如果OPEN为一空表,则失败退出;把第一个节点(节点n)从OPEN表移到。,OSED表;如果节点n的深度等于最大深度,则转回;扩展节点n,产生其全部后裔,并把它们放入OPEN表的前头,如果没有后裔,则转回;如果后继节点中有任一个为目标节点,则求得一个解,成功退出;否则转回。深度优先搜索策略是不完备的,带有一定的冒险性,并且应用此策略得到的解不一定是最优解(最短路径)。

有界深度优先搜索

对于许多复杂问题,其状态空间搜索树的深度可能为无限深,或者可能至少要比某个可接受的解答序列的己知深度上限还要深。为了这种情况,常给出一个节点扩展的最大深度——深度界限,即在深度优先策略中引入深度限制,称之为有界深度优先搜索。当从初始节点出发沿某一分枝扩展到限制深度,但还没有找到目标时,就不能再继续向下扩展,而只能改变方向继续搜索。若在限度内没有找到问题的解,且CLOSED表中仍有待扩展的节点,就将这些节点送回OPEN表,同时增大深度限制。

一致代价搜索

在许多实际问题中,状态空间搜索树中的各个边的代价不是完全相同的,为此,需要在搜索树中考虑每条边的代价,根据”代价最小”的原则,优先选用最小代价的搜索路径。宽度优先搜索可被推广用来解决寻找从起始状态至目标状态的具有最小代价的路径问题,这种推广了的宽度优先搜索算法称为一致代价搜索算法。

(2)例题分析

将A、B、C、D、E、F这六个变量排成如图所示的三角形,这六个变量分别取[1,6]上的整数,且均不相同。求使三角形三条边上的变量之和相等的全部解。如图就是一个解。
程序引入变量a、b、c、d、e、f,并让它们分别顺序取1至6的整数,在它们互不相同的条件下,测试由它们排成的如图所示的三角形三条边上的变量之和是否相等,如相等即为一种满足要求的排列,把它们输出。当这些变量取尽所有的组合后,程序就可得到全部可能的解。细节见下面的程序。

C代码示例:

#include &lt;stdio.h&gt;
void main()
{
     int a,b,c,d,e,f;
     for(a = 1; a&lt = 6; a++)
     for(b = 1; b&lt = 6; b++)
     {
          if(b==a) continue;
          for(c = 1; c&lt = 6; c++)
          {
                 if(c==a)||(c==b)continue;
                 for(d == 1; d&lt = 6; d++)
                 {
                      if(d = a)||(d==b)||(d==c)continue;
                      for(e == 1; e&lt = 6; e++)
                      {
                          if(e==a)||(e==b)||(e==c)||(e==d)continue;
                          f=21-(a+b+c+d+e+);
                          if((a+b+c=c+d+e)&amp;&amp;(a+b+c=e+))
                          {
                                printf("%6d", a);
                                printf("%4d%4d", b,f);
                                printf("%2d%4d%4d", c,d,e);
                                scanf(""%*c);
                                
                          }
                      }
                 }
          }
          
     }
}

3.二分查找

(1)基本概念

二分查找也称折半查找(Binary Search),它是一种效率较高的查找方法。但是,折半查找要求线性表必须采用顺序存储结构,而且表中元素按关键字有序排列

(2)查找过程

首先,假设表中元素是按升序排列,将表中间位置记录的关键字与查找关键字比较,如果两者相等,则查找成功;否则利用中间位置记录将表分成前、后两个子表,如果中间位置记录的关键字大于查找关键字,则进一步查找前一子表,否则进一步查找后一子表。重复以上过程,直到找到满足条件的记录,使查找成功,或直到子表不存在为止,此时查找不成功。

(3)C代码示例

 public static int Method(int[] nums, int low, int high, int target);
 {
     while(low <= high)
     {
          int middle = (low + high) / 2;
          if(target == nums[middle]);
          {
              return middle;
          }
          else if( target > nums[middle])
          {
               low = middle+1;
          }
          else if(target < nums[middle])
          {
               high = middle -1;
          }
        }
        return -1;
 }
发布了8 篇原创文章 · 获赞 1 · 访问量 297

猜你喜欢

转载自blog.csdn.net/AJIEIT/article/details/104857865