拓扑排序

拓扑排序

其实只需要讲解清楚拓扑排序的原理就可以了.
每次将入度为 0 的数入队, 然后将它所指向的数的入度 -1.再重复将入度为 0 的数如队, 直到所有的数都入队结束

如图, 因为 1 的入度为 0 , 现将 1 入队, 接下来将入度为 0 的 2 入队, 再是 4 ,再是 3 , 最后 5 入度
这里写图片描述

整体的理解有了, 我们依旧使用链表将其每个数保存, 当然, 要快速的搜寻我们应该用到链表数组才行

链表Node保存的是一个数所指向的下一个数的角标, 而num_arr代表一个数的入度


struct Node;
struct Arr;
typedef struct Node *node;
typedef int Replace_Int;

const int N = 100;

/*
num_node : 保存所指向的数,即若 1 -> 2, 1 -> 3; 则num_node = 2; num_node = 3;
*/
struct Node
{
    int num_node;
    node next;
    Node() : next(NULL) {}
};

/*
num_arr : 保存着该数的 入度 ; 即, 若 1 -> 2, 3 -> 2; 4 -> 2; 则 num_arr = 3;
arr_node : 指向链表 Node, 保存指向的数, 即, 若 2 -> 6, 2 -> 8, 2 -> 9; 则node链表保存 6, 8, 9;
*/
struct Arr
{
    int num_arr;
    node arr_node;
    Arr() : arr_node(), num_arr(-1) {}
}arr[N];

插入, 将被指向的数的入度 +1, 然后指向的数的链表记录下被指向数的角标即可


//插入一组有向图, 即,若有向图为 1 -> 2, 输入(1, 2)
void Inset(Replace_Int a, Replace_Int b)
{
    node T = new Node;
    T->num_node = b;

    //将被指向的 b 插入到 a 所指向的链表里;
    T->next = arr[a].arr_node->next;
    arr[a].arr_node->next = T;

    //当 a 未被使用, 则将 a 的num_arr 初始化为0, 表示 a 被使用
    if (arr[a].num_arr == -1)
        arr[a].num_arr = 0;

    //当 b 未被使用, 则将 b 的num_arr 初始化为0, 表示 b 被使用
    if (arr[b].num_arr == -1)
        arr[b].num_arr = 0;

    //将 b 入度 ++;
    arr[b].num_arr++;
}

将入度的为 0 的数入队, 然后搜寻它的链表所指向的角标, 将其指向的角标的数的入度都 -1.重复


//将有向图排序
void Sort()
{
    //sort 队;
    int sort[N] = { 0 };

    //head, last 保存sort队 的位置
    int head = 0, last = 0;

    //将使用过的数且 入度为 0 的保存进入队; 并将其num_arr = -1;以免下次重复保存
    for (int i = 0; i < N; i++)
        if (arr[i].num_arr == 0)
        {
            sort[last++] = i;
            arr[i].num_arr = -1;
        }

    //当没有入队的数时, head == last
    while (head != last)
    {
        //将入队所指向的数的 入度减一
        for (int i = head; i < last; i++)
        {
            //T : 入队的数将它所指向的链表保存
            node T = arr[sort[i]].arr_node->next;

            //将入队所指向的数的 入度减一
            while (T != NULL)
            {
                arr[T->num_node].num_arr--;
                T = T->next;
            }
        }

        head = last;

        //将使用过的数且 入度为 0 的保存进入队; 并将其num_arr = -1;以免下次重复保存
        for (int i = 0; i < N; i++)
            if (arr[i].num_arr == 0)
            {
                sort[last++] = i;
                arr[i].num_arr = -1;
            }
    }

    for (int i = 0; i < last; i++)
        printf("%d ", sort[i]);
}

源代码


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

struct Node;
struct Arr;
typedef struct Node *node;
typedef int Replace_Int;

const int N = 100;

/*
num_node : 保存所指向的数,即若 1 -> 2, 1 -> 3; 则num_node = 2; num_node = 3;
*/
struct Node
{
    int num_node;
    node next;
    Node() : next(NULL) {}
};

/*
num_arr : 保存着该数的 入度 ; 即, 若 1 -> 2, 3 -> 2; 4 -> 2; 则 num_arr = 3;
arr_node : 指向链表 Node, 保存指向的数, 即, 若 2 -> 6, 2 -> 8, 2 -> 9; 则node链表保存 6, 8, 9;
*/
struct Arr
{
    int num_arr;
    node arr_node;
    Arr() : arr_node(), num_arr(-1) {}
}arr[N];

//将整个数组初始化;
void Create(Arr arr[])
{
    for (int i = 0; i < N; i++)
        arr[i].arr_node = new Node;
}

//插入一组有向图, 即,若有向图为 1 -> 2, 输入(1, 2)
void Inset(Replace_Int a, Replace_Int b)
{
    node T = new Node;
    T->num_node = b;

    //将被指向的 b 插入到 a 所指向的链表里;
    T->next = arr[a].arr_node->next;
    arr[a].arr_node->next = T;

    //当 a 未被使用, 则将 a 的num_arr 初始化为0, 表示 a 被使用
    if (arr[a].num_arr == -1)
        arr[a].num_arr = 0;

    //当 b 未被使用, 则将 b 的num_arr 初始化为0, 表示 b 被使用
    if (arr[b].num_arr == -1)
        arr[b].num_arr = 0;

    //将 b 入度 ++;
    arr[b].num_arr++;
}

//将有向图排序
void Sort()
{
    //sort 队;
    int sort[N] = { 0 };

    //head, last 保存sort队 的位置
    int head = 0, last = 0;

    //将使用过的数且 入度为 0 的保存进入队; 并将其num_arr = -1;以免下次重复保存
    for (int i = 0; i < N; i++)
        if (arr[i].num_arr == 0)
        {
            sort[last++] = i;
            arr[i].num_arr = -1;
        }

    //当没有入队的数时, head == last
    while (head != last)
    {
        //将入队所指向的数的 入度减一
        for (int i = head; i < last; i++)
        {
            //T : 入队的数将它所指向的链表保存
            node T = arr[sort[i]].arr_node->next;

            //将入队所指向的数的 入度减一
            while (T != NULL)
            {
                arr[T->num_node].num_arr--;
                T = T->next;
            }
        }

        head = last;

        //将使用过的数且 入度为 0 的保存进入队; 并将其num_arr = -1;以免下次重复保存
        for (int i = 0; i < N; i++)
            if (arr[i].num_arr == 0)
            {
                sort[last++] = i;
                arr[i].num_arr = -1;
            }
    }

    for (int i = 0; i < last; i++)
        printf("%d ", sort[i]);
}



int main()
{
    Create(arr);
    Inset(1, 2);
    Inset(1, 3);
    Inset(2, 4);
    Inset(3, 4);
    Inset(3, 2);
    Sort();

    system("pause");
    return 0;
}

下面的拓扑排序用来Hash的字符串计算, 因为有时候输入的是A, B, C 而不是 1 ,2 ,3 就不行, 所以在此添上了散列的一些元素


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

struct Node;
struct Arr;
typedef struct Node *node;
typedef char* Replace_Char_Arr;

const int N = 1 << 10 + 1;

/*
num_node : 保存所指向的数,即若 1 -> 2, 1 -> 3; 则num_node = 2; num_node = 3;
*/
struct Node
{
    int num_node;
    node next;
    Node() : next(NULL) {}
};

/*
num_arr : 保存着该数的 入度 ; 即, 若 1 -> 2, 3 -> 2; 4 -> 2; 则 num_arr = 3;
arr_node : 指向链表 Node, 保存指向的数, 即, 若 2 -> 6, 2 -> 8, 2 -> 9; 则node链表保存 6, 8, 9;
*/
struct Arr
{
    Replace_Char_Arr ch;
    int num_arr;
    node arr_node;
    Arr() : arr_node(), num_arr(-1){}
}arr[N];

//用散列来保存每个字符串的位置;
unsigned int Hash(Replace_Char_Arr key)
{
    unsigned hash_size = 0;
    while (*key != '\0')
        hash_size = (hash_size << 5) + *key++;

    return hash_size % N;
}

//将整个数组初始化;
void Create(Arr arr[])
{
    for (int i = 0; i < N; i++)
        arr[i].arr_node = new Node;
}

//插入一组有向图, 即,若有向图为 1 -> 2, 输入(1, 2)
void Inset(Replace_Char_Arr a, Replace_Char_Arr b)
{
    unsigned int hash_size_a = Hash(a);
    unsigned int hash_size_b = Hash(b);

    node T = new Node;
    T->num_node = hash_size_b;

    //将被指向的 b 插入到 a 所指向的链表里;
    T->next = arr[hash_size_a].arr_node->next;
    arr[hash_size_a].arr_node->next = T;

    //当 a 未被使用, 则将 a 的num_arr 初始化为0, 表示 a 被使用
    if (arr[hash_size_a].num_arr == -1)
    {
        arr[hash_size_a].num_arr = 0;
        arr[hash_size_a].ch = a;
    }


    //当 b 未被使用, 则将 b 的num_arr 初始化为0, 表示 b 被使用
    if (arr[hash_size_b].num_arr == -1)
    {
        arr[hash_size_b].num_arr = 0;
        arr[hash_size_b].ch = b;
    }

    //将 b 入度 ++;
    arr[hash_size_b].num_arr++;
}

//将有向图排序
void Sort()
{
    //sort 队;
    int sort[N] = { 0 };
    char *sort_char[N];

    //head, last 保存sort队 的位置
    int head = 0, last = 0;

    //将使用过的数且 入度为 0 的保存进入队; 并将其num_arr = -1;以免下次重复保存
    for (int i = 0; i < N; i++)
        if (arr[i].num_arr == 0)
        {
            sort_char[last] = arr[i].ch;
            sort[last++] = i;
            arr[i].num_arr = -1;
        }

    //当没有入队的数时, head == last
    while (head != last)
    {
        //将入队所指向的数的 入度减一
        for (int i = head; i < last; i++)
        {
            //T : 入队的数将它所指向的链表保存
            node T = arr[sort[i]].arr_node->next;

            //将入队所指向的数的 入度减一
            while (T != NULL)
            {
                arr[T->num_node].num_arr--;
                T = T->next;
            }
        }

        head = last;

        //将使用过的数且 入度为 0 的保存进入队; 并将其num_arr = -1;以免下次重复保存
        for (int i = 0; i < N; i++)
            if (arr[i].num_arr == 0)
            {
                sort_char[last] = arr[i].ch;
                sort[last++] = i;
                arr[i].num_arr = -1;
            }
    }

    for (int i = 0; i < last; i++)
        printf("%s ", sort_char[i]);

    /*
    for (int i = 0; i < last; i++)
        printf("%s ", arr[sort_char[i]].ch);
    */
}

int main()
{
    Create(arr);
    Inset("a", "b");
    Inset("a", "c");
    Inset("b", "c");
    Inset("d", "b");
    Inset("a", "e");
    Sort();

    system("pause");
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值