贪心算法、DijKstra算法

算法设计思想
    1.贪心、贪婪算法
    2.分而治之 → 快排 + 拓展的归并
    3.动态规划
    4.回溯        → 递归  n皇后问题
    5.分支定界

基本思想

通过作出在当前看来最优的选择(贪心选择),将原问题规模缩小,如此反复,直至得到最终解
贪心算法并非对所有问题都能得到整体最优解
贪婪算法每一步所选的结果都是局部的最优解
这么做的目的就是希望经过所有的步骤所得到的解答会是全局最优解
不过事实上的结果并不一定能达到全局最优解
贪婪算法所花的时间通常较少,且就算不是全局最优解,但最后所得结果也不会太差
贪婪准则:最小方案  最大方案  最值思想

背包问题: (货物装载问题)
给定n种物品和一个背包。物品i的重量是Wi,其价值为Vi,背包的容量为C。应如何选择装入背包的物品,使得装入背包中物品的总价值最大?

在选择装入背包的物品时,对每种物品i只有2种选择,即装入背包或不装入背包。不能将物品i装入背包多次,也不能只装入部分的物品 i

用贪心算法解背包问题的基本步骤
首先计算每种物品单位重量的价值Vi/Wi,然后,依贪心选择策略,将尽可能多的单位重量(小)价值最高的物品装入背包。若将这种物品全部装入背包后,背包内的物品总重量未超过C,则选择单位重量价值次高的物品并尽可能多地装入背包。依此策略一直地进行下去,直到背包装满为止。

最短路径问题

从A点出发到B点,如何找出最短的路径?

2 + 1 + 6 + 1 + 2 == 12,但是 12 不是最佳选择,3 + 1 + 2 + 1 + 2 == 9,所以贪心算法得到的答案不一定是最优解

DijKstra:迪杰斯特拉算法
使用贪心算法的思想,从起点到顶点的最短路径
通过不断的新选择距离最近的顶点,来逐渐扩大最短路径权值,
直到覆盖了所有的顶点

有 5 个顶点,每个顶点之间有一个权值,选定顶点 1 作为入口,1 ~  2 的距离是 10,当前 1 ~ 3  没有直接连通,用无穷大表示,以此类推 . . . 接着扩充第 2 个顶点,做连通状态,1 ~ 2 是连通状态,2 ~ 3 也是连通状态,1 ~ 3 的距离是  10 + 50 == 60,扩充顶点 4,1 ~ 3 有一个新的距离:1 - 4 - 3 距离 50 < 60,根据最短路径原则,把 1 ~ 3 的距离更新为 50,每次保留从当前节点到另一个节点的最小距离,不断更新当前顶点与其他顶点的最短路径,最终得到:到每一个顶点的最短距离

        1        2        3        4

1              10

2                        20

3

4

[ 1 ][ 2 ] != ∞

[ 2 ][ 3 ] != ∞

1 ~ 2 是连通的状态,2 ~ 3 是连通的状态

如果当前位置的列数等于另一个位置的行数就是连通的状态,1 ~ 3 就是连通的状态

Huffman编码

哈夫曼编码是广泛地用于数据文件压缩的十分有效的编码方法。其压缩率通常在20%~90%之间。哈夫曼编码算法用字符在文件中出现的频率表来建立一个用0,1串表示各字符的最优表示方式。
给出现频率高的字符较短的编码,出现频率较低的字符以较长的编码,可以大大缩短总码长。

数据结构 --- 哈夫曼树基础
前缀码
对每一个字符规定一个0,1串作为其代码,并要求任一字符的代码都不是其它字符代码的前缀。这种编码称为前缀码。
      前缀约束:一个字符的编码必须不能是另一个编码
      的前缀,将数组以最优二叉树的方式存储。
       (规定左节点>右节点)
      算法步骤:
       step1:找出现频率最小的字符b,d。
       step2:将b,d合成一个新的子节点x1,并使f(x1)=f(b)+f(d)
       step3:在剩下的字符a,c,e和x1 按step1中方法找到两个新的子节点。直到所有的字符都包含在二叉树中。

短作业调度问题(操作系统的调度算法)

采用最短处理时间作业优先的贪心选择策略
可以设计出解多机调度问题的较好的近似算法。
按此策略,首先将n个作业依其所需的处理时间从大到小排序。
然后依此顺序将作业分配给空闲的处理机。
(优先队列)

迪杰斯特拉算法实现

直接用数组描述有向图    数据结构 --- 图的存储

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#define NO 0xFFFFFF	        //用无穷大表示不连通状态
#define MAX 10		        //不能直接定义太大数组-> 堆栈溢出
//图的结构体描述
typedef struct graph 
{
	char vexs[MAX];			//顶点数组
	int vexnum;				//顶点数
	int arcnum;				//边数
	int matrix[MAX][MAX];	//权值数组
}GRAPH,*LPGRAPH;
//遍历的图 选定入口 存储每一个最短距离-> 把每一个最短距离都记录下来
void DijKstra(GRAPH map, int in, int dist[]) 
{
	int i = 0;
    //成功获取路径的标记-> 标记数目由顶点数决定
	int flag[MAX];		   
	//求出当前节点到其他节点的距离
	for (int i = 0; i < map.vexnum; i++) 
	{
        //先把所有的标记置为0
		flag[i] = 0;    
        //当前节点到其他节点距离-> 遍历第一行权值数组
		dist[i] = map.matrix[in][i];
	}
    //in进来了做标记-> 已经扩充了in顶点
	flag[in] = 1;
    //in的距离置为 0
	dist[in] = 0;		
    //求扩充一个顶点后的最短路径-> 最小值
	int min;
	int k = 0;
	int j;  
	//2.扩充顶点-> 判断连通状态-> 每次扩充顶点都是距离最小的
	//数组存储顶点从下标 0 存储
	for (i = 1; i < map.vexnum; i++)    //i = 1本质是扩充第二个顶点
	{
		min = NO;					    //不连通状态-> 假设最小值为无穷大
        //如果存在比min小的值就是连通的状态-> 遍历扩充顶点的列 看有没有连通的
		for (j = 1; j < map.vexnum; j++) 
		{
            //连通的状态判断
			if (flag[j] == 0 && dist[j] < min) 
			{
				min = dist[j];          //改变当前最小值 
				k = j;					//连通的状态
			}
		}
        //把当前标记置为1
		flag[k] = 1;
        //找以 k 为行找列是否有值-> 有值说明 i 和 j 顶点是连通的
        //如果扩充节点产生新路径的距离[k,j]的值 + 原来的距离min < 原来连通的路径dist,就需要更新dist
		for (j = 1; j < map.vexnum; j++) 
		{
            //判断访问标记是不是扩充了
			if (flag[j] == 0 && (min + map.matrix[k][j]) < dist[j]) 
			{
                //更新最短路径
				dist[j] = min + map.matrix[k][j];
			}
		}
	}
	printf("\n");
    //打印路径
	for (int i = 1; i < map.vexnum; i++) 
	{
        //从入口开始到当前顶点的最短路径
		printf("最短路径:(%c,%c)=%d\n", map.vexs[in], map.vexs[i], dist[i]);
	}

}
int main() 
{
    //创建图
	GRAPH map = { {'1','2','3','4','5'},5,7,
		{
			{NO,10,NO,30,100},
			{NO,NO,50,NO,NO},
			{NO,NO,NO,NO,10},
			{NO,NO,20,NO,60},
			{NO,NO,NO,NO,NO}
		}
	};
    //选定入口从'1'这个顶点进来,找到其他顶点的距离 0号顶点当做入口
	int in = 0;		
    //存储所有的距离
	int dist[MAX];	
	DijKstra(map, in, dist);
	return 0;
}

/*输出*/


最短路径:(1,2)=10
最短路径:(1,3)=50
最短路径:(1,4)=30
最短路径:(1,5)=60
  • 7
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

qiuqiuyaq

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值