1、贪心选择性质:只在当前状态下做最优选择,即局部最优选择,再自顶向下,去解做出这个选择后产生的相应子问题。每做一次选择,问题就转化为规模更小的子问题。对于一个具体问题,要确定它是否具有贪心选择性质,必须证明每一步做出的选择最终导致问题的整体最优解。
2、最优子结构性质:问题的最优解包含子问题的最优解。
贪心算法的几个应用:
哈夫曼编码:二叉树&最小优先级队列
dijktra:原始集合、选中节点集合、dist
最小生成树:prim:原始集合、选中几点集合、closest、lowcost
最小生成树:kruskul:并查集&最小优先级队列
具体代码:
dijktra:
设邻接矩阵:a[][],有n个节点。
1.初始化:dist[i] = a[v][i],原始集合中只有v。
2.取dist的最小值,加入原始集合中,更新其他dist,更新n-1次
具体代码如下:
/*
5
0 10 10000 30 100
10000 0 50 10000 10000
10000 10000 0 10000 10
10000 10000 20 0 60
10000 10000 10000 10000 0
1
*/
#include<stdio.h>
#include<stdlib.h>
#define NUM 10
#define MAX_VALUE 10000
int dist[NUM];
int s[NUM];
int pre[NUM];
void dijkstra(int v,int a[NUM][NUM], int n){
//初始化
int i=1,j=1;
for(i=1; i<=n; i++){
dist[i] = a[v][i];
s[i] = false;
if(a[v][i] == MAX_VALUE){
pre[i] = 0;
}else{
pre[i] = v;
}
}
s[v] = true;
dist[v] = 0;
//更新n-1次
int min = 0,tmp=v;
for(i=1; i<n; i++){
//找最小的dist
min = MAX_VALUE;
for(j=1; j<=n; j++){
if(!s[j] && min>dist[j]){
min = dist[j];
tmp = j;
}
}
s[tmp] = true; //tmp的初始值是v,不能为0或者随意
//用最小的dist更新已有的dist
for(j=1; j<=n; j++){
if(!s[j] && a[tmp][j] < MAX_VALUE && (dist[tmp]+a[tmp][j] < dist[j])){
dist[j] = dist[tmp]+a[tmp][j];
pre[j] = tmp;
}
}
}
}
int main(){
int i=1,j=1,v,n,a[NUM][NUM];
//输入邻接矩阵
scanf("%d",&n);
for(i=1; i<=n; i++){
for(j=1; j<=n; j++){
scanf("%d",&a[i][j]);
}
}
//输入起始节点
scanf("%d",&v);
//计算dijkstra
dijkstra(v,a,n);
//打印源点到各点的最短距离
int tmp = 0;
for(i=1; i<=n; i++){
printf("从%d到%d的距离是%d\n",v,i,dist[i]);
tmp = i;
printf("从%d到%d的最短路经过:");
while(pre[tmp]>0 && pre[tmp] != v){
printf("%d ",pre[tmp]);
tmp = pre[tmp];
}
printf("%d\n",v);
}
system("pause");
return 0;
}
时间复杂度分析:使用邻接矩阵,o(n*n)
最小生成树:prim
/*
6
0 6 1 5 10000 10000
6 0 5 10000 3 10000
1 5 0 5 6 1
5 10000 5 0 10000 2
10000 3 6 10000 0 6
10000 10000 1 2 6 0
*/
#include<stdio.h>
#include<stdlib.h>
#define NUM 10
#define MAX_VALUE 10000
int closest[NUM]; //closest[j]记录j和S中的邻接节点中距离最近的节点
int lowcost[NUM]; //lowcost[j]记录j和S中最近邻接点的距离
int s[NUM]; //s[j] = true,标记j在S中
int prim(int a[NUM][NUM],int n){
//初始化
int i,j,sum=0;
for(i=1; i<=n; i++){
closest[i] = 1;
lowcost[i] = a[i][1];
s[i] = false;
}
s[1] = true;
for(j=1; j<n; j++){
//寻找离S中节点最近的节点及距离
int min = MAX_VALUE,v=1;
for(i=1; i<=n; i++){
if(!s[i] && min > lowcost[i]){
min = lowcost[i];
v = i;
}
}
s[v] = true;
sum += lowcost[v];
//每添加一个新的节点到S,比较节点i和S中以前节点的最短距离 和 i和新增节点的最短距离,更新closest和c
for(i=1; i<=n; i++){
if(!s[i] && a[i][v] < lowcost[i]){
lowcost[i] = a[i][v];
}
}
}
return sum;
}
int main(){
int i=1,j=1,n,a[NUM][NUM];
//输入邻接矩阵
scanf("%d",&n);
for(i=1; i<=n; i++){
for(j=1; j<=n; j++){
scanf("%d",&a[i][j]);
}
}
printf("%d",prim(a,n));
system("pause");
return 0;
}
时间复杂度是:o(n*n)。
说明:最小生成树prim和dijkstra相似,dijkstra保存了各点和源点的最近距离(dist),prim保存了各点和S中节点的最近邻接点及和最近邻接点的距离(closest, lowcost)。程序的书写过程相似,先初始化,再找最小的距离,再更新数组。