Data structure course design -- Design of traffic consulting system

Posted by pranesh on Sun, 31 Oct 2021 02:17:02 +0200

1, Problem Description:

Design of traffic consultation system

Design purpose:

Master dijestra algorithm and Floyd algorithm, and be able to use them to solve the shortest path problem.
Master the depth and breadth traversal algorithm of the graph.
Master quick sorting algorithm.

Content:

Design a traffic consulting system by reading National Urban distance map ( http://pan.baidu.com/s/1jIauHSE , please dynamically load it into memory when the program is running, and you can convert excel into csv for easy reading),
realization:

  • 1. Please verify whether there are no more than two provinces (provincial capitals) between other provincial capitals (excluding Hong Kong, Macao, two treasure islands, Taipei and Haikou) and Wuhan? (it is precisely because Wuhan is in the center of the country that the epidemic spread so widely);
  • 2. Allow users to query the shortest path from any city to another city (both algorithms should be implemented, and you can choose on the interface)
    And all non repetitive feasible paths (up to 10 nodes can be limited),
    , each result must contain path information and total length. Try to compare the sorted results with the results output by dijestra algorithm and Floyd algorithm;
  • 3. Suppose you need to bypass a specific city (user input or selection, for example) when solving the shortest path between two cities
    Wuhan), how should it be realized?
  • 4. Not based on the result of function 2 traversal, how to directly solve the first K-shortest path between two cities, for example, Wuhan to north
    The third shortest path between Beijing and Beijing.

2, Demand analysis:

First, you need to read and write the file and pay attention to the given format.

When verifying that there are no more than two provinces in Wuhan, a new two-dimensional array is created, and all the weights in the original two-dimensional array are changed to 1. In this way, the path with the smallest weight obtained by dijkstra algorithm passes through the path with the least nodes.

When you need to bypass a city, you only need to create a new two-dimensional array and delete all the information of the edges related to the city in the original two-dimensional array (all the weights are changed to 0).

Solving the first K short path of two cities can not directly call the above results, so it is necessary to design a new algorithm to solve it directly.

3, Outline design:

1. Data structure definition:

The city information in the program is mainly stored by graphs. The definition of graph class is as follows:
"Undirected graph" CC_ Construction of group class:
Data members are:

int C_Adj[MaxSize][MaxSize];
int C_Visited[MaxSize];
int C_PointNum;// Number of vertices
int C_ArcNum;// Number of sides

C_Adj array is used to store edge information, C_ If adj [i] [j] is not zero, it means that there is an edge (I, j) and the weight on this edge is C_ Value of adj [i] [j].
C_Visited[i] is used to store whether the node is accessed. If it is 0, it is not marked, and if it is 1, it has been marked. All defaults to zero.
C_Point indicates the number of nodes in the current graph
C_ArcNum represents the number of edges in the current graph
The initialization of the diagram is completed with a file:

//Read file initialization diagram
void CC_Graph::CreateByFile(int* PointNum, int* arcNum, int* C_BLoc, int* C_ELoc) {
    
    CC_File2();//Use file structure diagram
    *PointNum = 34;
    for (int i = 0; i < *PointNum; i++) {
        for (int j = 0; j < *PointNum; j++)
           if(FilePath[i+1][j+1]==0){//No connection
               C_Adj[i][j] = 0;
           }
           else{
               C_Adj[i][j] = FilePath[i + 1][j + 1];
           }
    }
}

All paths in the second function use the linked list as the storage structure, and each linked list stores a path:

typedef struct LinkList
{
    int Data = -999;
    LinkList* next = NULL;
}LinkList;
LinkList* CCListHead[MaxNum] ;
LinkList* Current = new LinkList;
int PathNum=0;//Number of paths
int A[MaxNum];//For sorting

2. Module design:

Main program module: the main function is designed as follows:
First, create an object of the graph class and initialize it with a file.

  int PointNum = 0, arcNum = 0, C_BLoc = 0, C_ELoc = 0;
 //   CC_ Graph G(&PointNum, &arcNum, &C_BLoc, &C_ELoc);// Input parameter structure diagram
    CC_Graph G;
    G.CreateByFile(&PointNum, &arcNum, &C_BLoc, &C_ELoc);

Then there is a cycle. The exit condition is to enter function 0. Other functions have corresponding functions, and the function calls involved are in the corresponding functions.

switch (choice) 
    {
    case 0: {
        system("cls");
        cout << "\n\n\nThank You \n\n\n";
        return 0;
    }
    case 1: {
        function1(G, &PointNum, &arcNum, &C_BLoc, &C_ELoc); 
        break; }
    case 2: {
        function2( G,&PointNum, &arcNum, &C_BLoc, &C_ELoc);
        break; }
    case 3: {
        function3(G, &PointNum, &arcNum, &C_BLoc, &C_ELoc); 
        break; }
    case 4: {
        function4(G, &PointNum, &arcNum, &C_BLoc, &C_ELoc);
        break; } 
    }//switch end

Dijkstra: set whether the saved Visited [] is marked, Dis [] saves the length of the node path from the user's starting point to the next label, and Father [] saves the previous node on the shortest path.
The main body of the algorithm has three steps:

  • 1 update: find two vertices, i.e. position I has been accessed, position J has not been accessed and ij is connected, assign the smaller of (0,i) + (I, J) and (0,j) to j, and Father[j] = i, i.e. I is the precursor node of J.

  • 2 access: after updating Dis in each round, judge the access of the smallest unmarked vertex (vis = 1;)

  • Repeat steps 1 and 2 until the value of Visited [] is all 1.

3. Calling relationship between modules:

4, Detailed design

Main algorithm design:

Implementation of Dijkstra algorithm for finding the shortest path:

Set whether the Visited [] save is marked, Dis [] saves the length of the node path from the user's starting point to the lower label, and Father [] saves the previous node on the shortest path. Others are similar to Prim algorithm.
The main body of the algorithm has three steps:

  • 1. Update: find two vertices, i.e. position I has been accessed, position J has not been accessed and ij is connected, assign the smaller of (0,i) + (I, J) and (0,j) to j, and Father[j] = i, i.e. I is the precursor node of J.
  • 2. Access: after updating Dis in each round, judge the access of the smallest unmarked vertex (vis = 1;)
  • 3. Repeat steps 1 and 2 until the value of Visited [] is all 1.

Then the output statement:

  1. Output the shortest path length from the user input position to each other vertex:
    Directly output the first n bits of Dis array in a loop
  2. Output the route corresponding to the shortest path from the user input position to each other vertex:
    First, set the cycle of i from 0 to n, assign i to VVV as the end point each time, and v is the starting point position entered by the user.
while (Father[vvv] != -1 && Father[vvv] != v)
    {
        cout << Father[vvv] << "<--";
        vvv = Father[vvv];
    }

Output the route according to the precursor relationship of the nodes in the Father array memory.
The above is the basic part of dijestra algorithm,

In this program, this algorithm has been called many times. In order to reduce the repeated code and improve the reusability of the code, an interface is added in this algorithm, that is, a switch case switch statement. When calling dijestra, the parts that need to be entered need to be given in the parameter list to realize different functions.

 switch (choice)//Different functions enter different interfaces when calling this function
    {
    case 0: {break; }

    case 2: {

        cout << "\n" << City[BLoc] << "reach" << City[ELoc] << "The shortest path length is:" << Dis[ELoc];
        cout << "   The trajectory is:";
        int E = ELoc;
        cout << City[E] << "<--";
        while (Father[E] != -1 && Father[E] != BLoc)
        {
            cout << City[Father[E]] << "<--";
            E = Father[E];
        }
        cout << City[BLoc];
        break;
    }
    case 3: {break; }
    case 4: {//For A * algorithm
   
        for (int i = 0; i < n; i++) 
            dis[i] = Dis[i];
        
        break; }
    }

CC_Floyd:

  • 1. Start from any unilateral path. The distance between all two points is the weight of the edge. If there is no edge connected between two points, the weight is infinite.
  • 2. For each pair of vertices u and v, see if there is a vertex w, so that the path from u to w and then to v is shorter than the known path. If so, update it.

The graph is represented by the adjacency matrix g. if there is a path reachable from Vi to Vj, G[i][j]=d, d represents the length of the path; Otherwise, G[i][j] = infinity.
Define a matrix D to record the information of the inserted point. D[i][j] represents the point to pass from Vi to Vj. Initialize D[i][j]=j. Insert each vertex into the graph and compare the distance between the inserted point and the original distance. G[i][j] = min (G[i][j], G [i] [k] + G [k] [J]). If the value of G[i][j] becomes smaller, D[i][j]=k.
G contains the information of the shortest path between two points, while D contains the information of the shortest path.
For example, find the path from V5 to V1. According to D, if D(5,1)=3, it means that V3 passes from V5 to V1, and the path is {V5,V3,V1}. If D(5,3)=3, it means that V5 is directly connected with v3. If D(3,1)=1, it means that V3 is directly connected with v1.

Cway[i][j] = k;//Indicates that the shortest path from i to j is i - > k - > J
if (Cpath[i][k] + Cpath[k][j] < Cpath[i][j])//If the path length after inserting the intermediate node is shorter than the original one 
{
Cpath[i][j] = Cpath[i][k] + Cpath[k][j];//The modified path is the sum of the two paths
Cway[i][j] = k;//Indicates that the shortest path from i to j is i - > k - > J
}

Similar to dijestra algorithm, Floyd algorithm is called many times in this program, so an interface is designed similarly:

case 2: {//Output AB two cities

    cout << "\n" << City[BLoc] << "reach" << City[ELoc] << "The shortest path length is:" << Cpath[BLoc][ELoc];
    cout << "   The trajectory is:";
    int u = BLoc;//starting point
    while (Cway[u][ELoc] != -1) {//If there is an intermediate node, assign the intermediate node to the starting point and keep cycling
        cout << City[u] << "->";
        u = Cway[u][ELoc];
    }
    cout << City[ELoc]<<endl;

    break; 
}
case 3: {break; }
   
case 4: {break; }
}

Allpath:

Suppose we want to find all the paths from node 3 to node 6, then we set node 3 as the starting point and node 6 as the ending point. The storage structures we need are: a stack for saving paths and an array for saving marked nodes. Then, the steps to find all paths from node 3 to node 6 are as follows:
1. We establish a stack structure of storage nodes, put the starting point 3 on the stack, and mark node 3 as the stack state;
2. Starting from node 3, find the first non stack adjacent node 1 of node 3, and mark node 1 as the stack state;
3. Starting from node 1, find the first non stack adjacent node 0 of node 1, and mark node 0 as the stack state;
4. Starting from node 0, find the first non stack adjacent node 2 of node 0, and mark node 2 as the stack state;
5. Starting from node 2, find the first non stack adjacent node 5 of node 2, and mark node 5 as the stack state;
6. Starting from node 5, find the first non stack adjacent node 6 of node 5, and mark node 6 as the stack state;
7. The top node 6 of the stack is the end point, then we find a path from the start point to the end point and output this path;
8. Pop up node 6 from the top of the stack and mark it as non stack state;
9. Now the top node of the stack is 5, and node 5 has no non stack state nodes except the end point, so pop node 5 from the top of the stack;
10. Now the top node of the stack is 2. In addition to the node 5 just out of the stack, node 2 also has node 6 in the non stack state. Then we stack node 6;
11. Now the top of the stack is node 6, that is, the second path is found, and the output of the whole stack is the second path
12. Repeat steps 2-11 to find all paths from start point 3 to end point 6;
13. The stack is empty and the algorithm ends.

Storage of path:
Cclisthead [i] - > data is the length of the i-th path. Cclisthead [i] - > next - > data are nodes on the path;
Sort: CCListHead[i] (0~n) sort by quick sort

Fast scheduling algorithm:

Quick sort (improved version) (a kind of exchange sort)
Recursive fast scheduling:
Improvement scheme: improve the method of selecting the pivot, that is, select the median in the data set as the pivot each time (the selection of the median can be completed in O(n) time).
Split strategy for fast scheduling:
The first step is to make the pivot element leave the data segment to be divided by exchanging the pivot element with the last element; i starts with the first element and j starts with the penultimate element. When i is to the left of J, we move i to the right over elements smaller than the pivot element, and move J to the left over elements larger than the pivot element. When i and j stop, i points to the large element and j points to the small element. If i is to the left of J, the two elements are interchanged. If i and j have been interleaved at this time, that is, i > J, so they are not exchanged. At this point, the pivot element is exchanged with the element referred to by i.

//Return to median position
int GetMiddleValue(int A[], int low, int high)
{
    //  int mid = low + (high - low) >> 1;
    int mid = (high + low) / 2;

    int y1 = A[low] > A[mid] ? low : mid;
    int y2 = A[low] > A[high] ? low : high;
    int y3 = A[mid] > A[high] ? mid : high;

    if (y1 == y2) return y3;

    else return A[y1] > A[y2] ? y2 : y1;
}

CC_File2:

First read the file "connection" into FilePath[][],0 means no connection, and 1 means there is a connection
Then read the file "distance" into FilePath [] []. If ij is connected, change FilePath[i] [] to the weight read in the file. If ij is not connected, keep FilePath[i] [] to 0.

  ifstream in_file("Adjacency list of provincial capital cities.txt", ios::in);
        hang = 2;//The first line of the original document is blank
        while (in_file)
        {
            string s;
            getline(in_file, s);
            int lie = 1;
            for (int i = 0; i < s.length(); i++)
            {
                if (s[i] != ',')
                {
                    string c = "";
                    while (s[i] != ',')
                    {
                        if (i >= s.length()) break;
                        c += s[i];
                        i++;
                    //    if (i >= s.length()) break;
                    }
                    if (FilePath[hang][lie] == 1 )//1 indicates a connection
                    {
                        FilePath[hang][lie] = stoi(c.c_str());
                        FilePath[lie][hang] = stoi(c.c_str());
                       //InsertEdge(hang, lie, stoi(c.c_str()));
                     //   lie++;
                    }
                    lie++;
                }
            }
            hang++;
        }
        in_file.close();

        for (int i = 0; i <= 34; i++)
            for (int j = 0; j <= 34; j++)
                if (i == j)
                    FilePath[i][j] = 0;
            
    
        for (int i = 1; i <= 34; i++) {
            cout << endl;
            for (int j = 1; j <= 34; j++)
                cout << FilePath[i][j] << "\t";
        }
        cout << "\n Read file processing completed\n";

A_Star:

In the A * algorithm, using the priority queue is to use the heuristic function f(s) to determine the priority of the state in the priority queue. When solving this problem, select h(x)=dt(x), dt(x) is the shortest distance from node x to the target node, which is directly obtained by Dijkstra.
The k-th shortest path can be found by controlling the number of times each node enters (or leaves) the queue as K times.

struct a_star //A * priority queue during search
{
    int v;//Currently indicated node
    int len;//Distance to starting point
    bool operator<(const a_star& a)const    //f(i)=d[i]+h[i] h(i) represents the shortest circuit from I to end (stored in dis [] and given by djikstra)
    {
        return len + dis[v] > a.len + dis[a.v];//Those with low len + dis[v] value have higher priority in the priority queue
    }
};
int C_Astar(int Path[MaxSize][MaxSize],int N,int Begin ,int End ,int K,int ans[MaxSize])
{
    if (Begin == End)
        K++;
    if (dis[Begin] == MaxNum) {
        return -1;
    }

    a_star n1;//a_star
    n1.v = Begin;
    n1.len = 0;
    priority_queue <a_star> q;//Priority queue
    q.push(n1);//Initial state starting point joining the team
    while (!q.empty())
    {
        a_star temp = q.top();//temp is the element that currently has the highest priority
        q.pop();
        ans[temp.v]++;//Counter indicating the number of v node accesses plus one
        if (ans[End] == K)//When the end point is taken for the K-th time, the distance is output
            return temp.len;
        if (ans[temp.v] > K)//If v has joined the team k times, skip directly
            continue;

        int i = temp.v;
        for (int j = 0; j < N; j++)
         if (Path[i][j] != 0) {//Traverse all points j connected to i
            a_star n2;
            n2.v = j;
            n2.len = Path[i][j] + temp.len;
            q.push(n2);
         }

    }
    return -1;
}

Write all path results to file:

 //Output file format: serial number, length, a - > b - > C - > end;
    ofstream out;
    out.open("result.txt");
    for (int i = 0; i < PathNum; i++)
    {
        LinkList* p = CCListHead[i];
        out << i + 1 << ":--long" << p->Data << "    ";
        p = p->next;
        while (p->next!= NULL)
        {
            out << City[p->Data] << "->";
            p = p->next;
        }
        out << City[p->Data] << endl;
    }
    out.close();

5, User manual:

After opening the program, the menu and city information will be displayed. Select the required function input. Note that the city entered in this program is the corresponding number of the city (the corresponding relationship is given in the menu) rather than the city name.

  • Function 1: select this function to verify the number of cities from any city in the country to all other cities. If you want to verify that there are no more than two provinces between other provincial capitals in the country (excluding Hong Kong, Macao and two treasure islands, Taipei and Haikou) and Wuhan, just enter number 22.
  • Function 2: after selecting this function, you need to input the numbers of the two cities to be asked. The program will automatically output the shortest path length and route calculated by the three algorithms, and output all non repeated feasible paths between the two cities (up to 10 nodes) and the number of all paths. After sorting, the program will output the first 15 short paths, And write all path information to the file.

6, Test results:

Table 1 function I

Table 2 function II

Table 3 function III

Table 4 function IV

Topics: Algorithm data structure Graph Theory dijkstra