Dijkstra

Dijkstra 算法

Dijkstra算法是典型的单源最短路径算法,用于计算一个节点到其他所有节点的最短路径。主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止。Dijkstra算法是很有代表性的最短路径算法,在很多专业课程中都作为基本内容有详细的介绍,如数据结构,图论,运筹学等等。注意该算法要求图中不存在负权边。

在无向图 G=(V,E) 中,假设每条边 E[i] 的长度为 w[i],找到由顶点 V0 到其余各点的最短路径。(单源最短路径)

这里写图片描述

这个算法是通过为每个顶点 v 保留目前为止所找到的从s到v的最短路径来工作的。初始时,原点 s 的路径权重被赋为 0 (d[s] = 0)。若对于顶点 s 存在能直接到达的边(s,m),则把d[m]设为w(s, m),同时把所有其他(s不能直接到达的)顶点的路径长度设为无穷大,即表示我们不知道任何通向这些顶点的路径(对于所有顶点的集合 V 中的任意顶点 v, 若 v 不为 s 和上述 m 之一, d[v] = ∞)。当算法结束时,d[v] 中存储的便是从 s 到 v 的最短路径,或者如果路径不存在的话是无穷大。


struct Node;
typedef struct Node *T;
typedef char *Inspace_char;
const int N = (1 << 10) + 1;
//M : 顶点的个数
const int M = 7;

//Node : 链表.
//ch : 字符串
//num_Node : 到该字符串的权, 也就是路径长
//next : 下一节点;
struct Node
{
    Inspace_char ch;
    int num_Node;
    T next;
    //链表初始化
    Node() : next(NULL), num_Node(0) {}
};

//指向 Node 的链表
//num_Dijk : 初始为-1, 表示未被使用的链表, 1 表示使用的链表; 当然也可以使用bool
//ch_Dik : 字符串;
//next_Node : 指向链表 Node 的指针;
struct Dijk
{
    Inspace_char ch_Dik;
    int num_Dijk;
    T next_Node;
    //链表初始化
    Dijk() : next_Node(new Node), num_Dijk(-1) {}
}dijk[N];

因为输入的顶点不一定是数字, 可能是字符串, 所以在此也使用散列的部分代码


//散列计算
unsigned int Hash(char *ch)
{
    unsigned int hash_size = 0;
    while (*ch != '\0')
        hash_size = (hash_size << 10) + *ch++;

    return hash_size % ((1 << 9) + 1);
}

这里写图片描述
接下来是将字符的插入, 详细的思路代码里呈现, 不过整体跟拓扑排序一样


//插入;
//ch2 : 被指向的字符串, 如 : v1 -> v2, 则ch1 = v1, ch2 = v2;
void Inset(Inspace_char ch1, Inspace_char ch2, int num)
{
    //将ch1 ch2转成数;
    unsigned int a = Hash(ch1);
    unsigned int b = Hash(ch2);

    //将ch2插入到ch1的 Dijk指向的Node链表里;
    T temp = new Node;
    temp->num_Node = num;
    temp->ch = ch2;
    temp->next = dijk[a].next_Node->next;
    dijk[a].next_Node->next = temp;

    //如果ch1未被使用过, 将其变为 1 代表使用
    if (dijk[a].num_Dijk == -1)
    {
        dijk[a].num_Dijk = 1;
        dijk[a].ch_Dik = ch1;
    }

    //如果ch2未被使用过, 将其变为 1 代表使用
    if (dijk[b].num_Dijk == -1)
    {
        dijk[b].num_Dijk = 1;
        dijk[b].ch_Dik = ch2;
    }
}

最后是关键的排序了


//Dijkstra排序 : 按当前从哪个位置出发每步最优, 不一定是所有路径最优
void DijkSort(Inspace_char ch)
{
    //temp : 每次都指向被保存字符串的指向 Node 的链表
    //t : t = temp; 每次遍历t;
    T temp = new Node, t = new Node;

    //a[N] : 将每次最优的字符对应的编号保存
    //sum_min : 最优的路径长;
    int a[N] = { 0 }, i = 0, sum_min = 0;

    //将起始的字符串保存;
    a[i] = Hash(ch);
    //将ch1指向 Node 的链表保存, 保存后i++;
    temp = dijk[a[i++]].next_Node;
    t = temp->next;

    //将所有顶点保存完退出;
    while (i < M)
    {
        int min = N;

        //ch_temp : 整个链表中的权最小;
        Inspace_char ch_temp;
        //这里temp->next != NULL 是必要的, 不能为temp != NULL ,要保证退出去后 temp != NULL
        while (temp->next)
        {
            if (min > temp->next->num_Node && temp->next->num_Node != 0)
            {
                min = Min(min, temp->next->num_Node);
                ch_temp = temp->next->ch;
            }
            temp = temp->next;
        }

        //将下次最短的字符串保存起来;
        a[i] = Hash(ch_temp);
        sum_min += min;

        //遍历 temp 链表, 将所有的含有保存过的字符串num_Node清 0 , 表示未使用
        for (int j = 0; j < N; j++)
            if (dijk[j].num_Dijk == 1)
            {
                T t = dijk[j].next_Node->next;
                while (t)
                {
                    if (t->ch == ch_temp)
                        t->num_Node = 0;
                    t = t->next;
                }
            }

        //将刚保存的字符串所指向的 Node的链表放在temp 的最后;
        //因为 temp->next == NULL ,所以temp != NULL, 保证下一个链表接在现在的链表最后;
        temp->next = dijk[a[i]].next_Node->next;
        temp = t;

        while (temp)
        {
            if (a[i] == Hash(temp->ch))
                temp->num_Node = 0;
            temp = temp->next;
        }
        temp = t;
        i++;
    }

    for (i = 0; i < M; i++)
        printf("%s ", dijk[a[i]].ch_Dik);
}

源代码

这里写图片描述


#include <stdlib.h>
#include <stdio.h>

#define Min(a, b) (a > b ? b : a)

struct Node;
typedef struct Node *T;
typedef char *Inspace_char;
const int N = (1 << 10) + 1;
//M : 顶点的个数
const int M = 7;

//Node : 链表.
//ch : 字符串
//num_Node : 到该字符串的权, 也就是路径长
//next : 下一节点;
struct Node
{
    Inspace_char ch;
    int num_Node;
    T next;
    //链表初始化
    Node() : next(NULL), num_Node(0) {}
};

//指向 Node 的链表
//num_Dijk : 初始为-1, 表示未被使用的链表, 1 表示使用的链表; 当然也可以使用bool
//ch_Dik : 字符串;
//next_Node : 指向链表 Node 的指针;
struct Dijk
{
    Inspace_char ch_Dik;
    int num_Dijk;
    T next_Node;
    //链表初始化
    Dijk() : next_Node(new Node), num_Dijk(-1) {}
}dijk[N];

//散列计算
unsigned int Hash(char *ch)
{
    unsigned int hash_size = 0;
    while (*ch != '\0')
        hash_size = (hash_size << 10) + *ch++;

    return hash_size % ((1 << 9) + 1);
}

//插入;
//ch2 : 被指向的字符串, 如 : v1 -> v2, 则ch1 = v1, ch2 = v2;
void Inset(Inspace_char ch1, Inspace_char ch2, int num)
{
    //将ch1 ch2转成数;
    unsigned int a = Hash(ch1);
    unsigned int b = Hash(ch2);

    //将ch2插入到ch1的 Dijk指向的Node链表里;
    T temp = new Node;
    temp->num_Node = num;
    temp->ch = ch2;
    temp->next = dijk[a].next_Node->next;
    dijk[a].next_Node->next = temp;

    //如果ch1未被使用过, 将其变为 1 代表使用
    if (dijk[a].num_Dijk == -1)
    {
        dijk[a].num_Dijk = 1;
        dijk[a].ch_Dik = ch1;
    }

    //如果ch2未被使用过, 将其变为 1 代表使用
    if (dijk[b].num_Dijk == -1)
    {
        dijk[b].num_Dijk = 1;
        dijk[b].ch_Dik = ch2;
    }
}

//Dijkstra排序 : 按当前从哪个位置出发每步最优, 不一定是所有路径最优
void DijkSort(Inspace_char ch)
{
    //temp : 每次都指向被保存字符串的指向 Node 的链表
    //t : t = temp; 每次遍历t;
    T temp = new Node, t = new Node;

    //a[N] : 将每次最优的字符对应的编号保存
    //sum_min : 最优的路径长;
    int a[N] = { 0 }, i = 0, sum_min = 0;

    //将起始的字符串保存;
    a[i] = Hash(ch);
    //将ch1指向 Node 的链表保存, 保存后i++;
    temp = dijk[a[i++]].next_Node;
    t = temp->next;

    //将所有顶点保存完退出;
    while (i < M)
    {
        int min = N;

        //ch_temp : 整个链表中的权最小;
        Inspace_char ch_temp;
        //这里temp->next != NULL 是必要的, 不能为temp != NULL ,要保证退出去后 temp != NULL
        while (temp->next)
        {
            if (min > temp->next->num_Node && temp->next->num_Node != 0)
            {
                min = Min(min, temp->next->num_Node);
                ch_temp = temp->next->ch;
            }
            temp = temp->next;
        }

        //将下次最短的字符串保存起来;
        a[i] = Hash(ch_temp);
        sum_min += min;

        //遍历 temp 链表, 将所有的含有保存过的字符串num_Node清 0 , 表示未使用
        for (int j = 0; j < N; j++)
            if (dijk[j].num_Dijk == 1)
            {
                T t = dijk[j].next_Node->next;
                while (t)
                {
                    if (t->ch == ch_temp)
                        t->num_Node = 0;
                    t = t->next;
                }
            }

        //将刚保存的字符串所指向的 Node的链表放在temp 的最后;
        //因为 temp->next == NULL ,所以temp != NULL, 保证下一个链表接在现在的链表最后;
        temp->next = dijk[a[i]].next_Node->next;
        temp = t;

        while (temp)
        {
            if (a[i] == Hash(temp->ch))
                temp->num_Node = 0;
            temp = temp->next;
        }
        temp = t;
        i++;
    }

    for (i = 0; i < M; i++)
        printf("%s ", dijk[a[i]].ch_Dik);
}

//保存的有向图数据
void Input_Inset()
{
    Inset("v1", "v2", 2);
    Inset("v1", "v4", 1);
    Inset("v2", "v4", 3);
    Inset("v2", "v5", 10);
    Inset("v3", "v1", 4);
    Inset("v3", "v6", 5);
    Inset("v4", "v3", 2);
    Inset("v4", "v5", 2);
    Inset("v4", "v6", 8);
    Inset("v4", "v7", 4);
    Inset("v5", "v7", 6);
    Inset("v7", "v6", 1);
}

int main()
{
    Input_Inset();
    DijkSort("v2");


    system("pause");
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值