One-way flight problem

You are making a one-way indirect trip, which includes billions of an unknown very large number of transfers.

  • You do not stop twice at the same airport.
  • You have 1 ticket for each part of the trip.
  • Each ticket contains src and dst airport.
  • All tickets are sorted randomly.
  • You forgot the original departure airport (the very first src) and your destination (last dst).

Create an algorithm to restore your trip with minimal complexity < .




Trying to solve this problem, I started using the symmetric difference of the two sets, Srcs and Dsts:

1) Sort all src keys in the Srcs array
2) Sort all dst keys in a Dsts array
3) Create a union set of both arrays to find no duplicates - this is your first src and last dst
4) Now, having the starting point, go through both arrays using binary search.

But I believe that there should be another effective method.

+54
c algorithm
Jun 07 2018-10-10T00-06-07
source share
18 answers

Build a hash table and add each airport to the hash table.

<key,value> = <airport, count>

The graph for the airport increases if the airport is either a source or destination. Thus, for each airport, the score will be 2 (1 for src and 1 for dst), with the exception of the source and destination of your trip, which will have a score of 1.

You need to see each ticket at least once. So the complexity is O (n).

+29
Jun 07 '10 at 18:17
source share

Summary: A one-pass algorithm is provided below . (Ie, not only linearly, but each ticket looks exactly once, which, of course, is the optimal number of visits per ticket). I set up a resume because there are many seemingly equivalent solutions, and it would be difficult to determine why I added another one. :)

I was asked this question in an interview. The concept is extremely simple: each ticket is a singleton list, with conceptually two elements, src and dst.

We index each such list in a hash table using its first and last elements as keys, so we can find in O (1) if the list starts or ends at a specific element (airport). For each ticket, when we see that it starts where another list ends, just link the lists (O (1)). Similarly, if it ends when another list starts, another list joins. Of course, when we link two lists, we basically destroy them and get one. (A chain of N tickets will be built after N-1 such links).

It is necessary to maintain the invariant so that the hash table keys are exactly the first and last elements of the remaining lists.

In general, O (N).

And yes, I replied that in place :)

Edit I forgot to add an important point. Each mentions two hash tables, but one does the trick, because the invariant of the algorithms includes that no more \ strong> one list of tickets starts or starts in any one city (if there are two of them, we immediately join the lists in this city, and remove this city from the hash table). Asymptotically there is no difference, it is simply simpler.

Edit 2 It is also interesting that, compared to solutions using 2 hash tables with N elements each, this solution uses one hash table with no more than N / 2 entries (what if we see tickets in the order, say, 1, 3, 5 etc.). Thus, it uses about half the memory, except that it is faster.

+20
Jun 20 '10 at 0:14
source share

Build two hash tables (or trying), one of which is src and the other is dst. Select one random case and look at its dst in the src-hash table. Repeat this process for the result until you reach your goal (final destination). Now find its src in the hash table with dst-keyed. Repeat the process for the result until you click on the start.

Building hash tables takes O (n), and building a list takes O (n), so the whole algorithm is O (n).

EDIT: in fact, you only need to build one hash table. Let's say you build a hash table with the src key. Choose one ticket at random and, as before, create a list that will lead to the final destination. Then select another random ticket from tickets that are not yet on the list. Follow your destination until you click on the ticket you started from the beginning. Repeat this process until you have built the entire list. It is still O (n), because in the worst case, you select tickets in the reverse order.

Edit: got the names of the tables replaced in my algorithm.

+10
Jun 07 '10 at 18:18
source share

This is basically a dependency graph in which each ticket represents a node and src and dst airport represents directional links, so use topological sorting to determine the flight order.

EDIT: Although, since this is an airline ticket, and you know that you actually made a route that you could physically follow, sort by date and time of departure in UTC.

EDIT2: Assuming you have a ticket at each airport that uses a three-character code, you can use the algorithm described here ( Find three numbers that appear only once ) to determine two unique airports by combining all the airports.

EDIT3: Here are some C ++ to solve this problem using the xor method. The general algorithm is as follows: provided that the unique encoding from the airport is an integer (either if there is a three-letter airport code, or to encode the location of the airport in integer size using latitude and longitude):

First, XOR all airport codes together. This should be equal to the original source XOR airport at the final destination airport. Since we know that the original airport and the last airport are unique, this value should not be zero. Since it is not equal to zero, at least one bit will be set in this value. This bit corresponds to a bit that is set at one of the airports and not set at another; call it the notation bit.

Then install two buckets, each of which has an XORed value from the first step. Now, for each ticket, enter each airport depending on whether it has a bit indicator or not, and an airport code with a value in the bucket. Also keep an eye on each bucket, how many sources of airports and destination airports went into that bucket.

After processing all the tickets, select one of the buckets. The number of source airports sent to this bucket should be one more or less number of destination airports sent to this bucket. If the number of source airports is less than the number of destination airports, this means that the original source airport (the only unique source airport) was sent to another bucket. This means that the value in the current bucket is an identifier for the original source of the airport! Conversely, if the number of destination airports is less than the number of source airports, the final destination airport was sent to another bucket, so the current bucket is an identifier for the final destination airport.

 struct ticket { int src; int dst; }; int get_airport_bucket_index( int airport_code, int discriminating_bit) { return (airport_code & discriminating_bit)==discriminating_bit ? 1 : 0; } void find_trip_endpoints(const ticket *tickets, size_t ticket_count, int *out_src, int *out_dst) { int xor_residual= 0; for (const ticket *current_ticket= tickets, *end_ticket= tickets + ticket_count; current_ticket!=end_ticket; ++current_ticket) { xor_residual^= current_ticket->src; xor_residual^= current_ticket->dst; } // now xor_residual will be equal to the starting airport xor ending airport // since starting airport!=ending airport, they have at least one bit that is not in common // int discriminating_bit= xor_residual & (-xor_residual); assert(discriminating_bit!=0); int airport_codes[2]= { xor_residual, xor_residual }; int src_count[2]= { 0, 0 }; int dst_count[2]= { 0, 0 }; for (const ticket *current_ticket= tickets, *end_ticket= tickets + ticket_count; current_ticket!=end_ticket; ++current_ticket) { int src_index= get_airport_bucket_index(current_ticket->src, discriminating_bit); airport_codes[src_index]^= current_ticket->src; src_count[src_index]+= 1; int dst_index= get_airport_bucket_index(current_ticket->dst, discriminating_bit); airport_codes[dst_index]^= current_ticket->dst; dst_count[dst_index]+= 1; } assert((airport_codes[0]^airport_codes[1])==xor_residual); assert(abs(src_count[0]-dst_count[0])==1); // all airports with the bit set/unset will be accounted for as well as either the source or destination assert(abs(src_count[1]-dst_count[1])==1); assert((src_count[0]-dst_count[0])==-(src_count[1]-dst_count[1])); int src_index= src_count[0]-dst_count[0]<0 ? 0 : 1; // if src < dst, that means we put more dst into the source bucket than dst, which means the initial source went into the other bucket, which means it should be equal to this bucket! assert(get_airport_bucket_index(airport_codes[src_index], discriminating_bit)!=src_index); *out_src= airport_codes[src_index]; *out_dst= airport_codes[!src_index]; return; } int main() { ticket test0[]= { { 1, 2 } }; ticket test1[]= { { 1, 2 }, { 2, 3 } }; ticket test2[]= { { 1, 2 }, { 2, 3 }, { 3, 4 } }; ticket test3[]= { { 2, 3 }, { 3, 4 }, { 1, 2 } }; ticket test4[]= { { 2, 1 }, { 3, 2 }, { 4, 3 } }; ticket test5[]= { { 1, 3 }, { 3, 5 }, { 5, 2 } }; int initial_src, final_dst; find_trip_endpoints(test0, sizeof(test0)/sizeof(*test0), &initial_src, &final_dst); assert(initial_src==1); assert(final_dst==2); find_trip_endpoints(test1, sizeof(test1)/sizeof(*test1), &initial_src, &final_dst); assert(initial_src==1); assert(final_dst==3); find_trip_endpoints(test2, sizeof(test2)/sizeof(*test2), &initial_src, &final_dst); assert(initial_src==1); assert(final_dst==4); find_trip_endpoints(test3, sizeof(test3)/sizeof(*test3), &initial_src, &final_dst); assert(initial_src==1); assert(final_dst==4); find_trip_endpoints(test4, sizeof(test4)/sizeof(*test4), &initial_src, &final_dst); assert(initial_src==4); assert(final_dst==1); find_trip_endpoints(test5, sizeof(test5)/sizeof(*test5), &initial_src, &final_dst); assert(initial_src==1); assert(final_dst==2); return 0; } 
+5
Jun 07 '10 at 18:13
source share

Create two data structures:

 Route { start end list of flights where flight[n].dest = flight[n+1].src } List of Routes 

And then:

 foreach (flight in random set) { added to route = false; foreach (route in list of routes) { if (flight.src = route.end) { if (!added_to_route) { add flight to end of route added to route = true } else { merge routes next flight } } if (flight.dest = route.start) { if (!added_to_route) { add flight to start of route added to route = true } else { merge routes next flight } } } if (!added to route) { create route } } 
+3
Jun 18 2018-10-18T00:
source share

Put in two hashes: to_end = src โ†’ des; to_beg = des โ†’ src

Choose any airport as your starting point S.

 while(to_end[S] != null) S = to_end[S]; 

S is now your final destination. Repeat with another map to find the starting point.

Without proper validation, this feels O (N) if you have a decent implementation of the Hash table.

+2
Jun 07 '10 at 20:01
source share

The hash table will not work for large sizes (e.g. billions in the original question); anyone who has worked with them knows that they are only good for small sets. Instead, you can use a binary search tree that will give you O (n log n) complexity.

The easiest way is with two passes: The first one adds them all to the tree indexed by src. The second goes through the tree and collects the nodes into an array.

Can we do better? We can, if we really want to: we can do it in one go. Think of each ticket as a node in your favorite list. Initially, each node has null values โ€‹โ€‹for the next pointer. For each ticket, enter both src and dest indexes in the index. If there is a collision, it means that we already have a nearby ticket; Connect the nodes and remove the match from the index. When you are done, you will only make one pass and get an empty index and a linked list of all tickets in order.

This method is much faster: it is only one pass, not two; and the store is significantly smaller (worst case: n / 2, best case: 1, typical case: sqrt (n)), enough for you to actually use the hash instead of the binary search tree.

+2
Jun 07 2018-10-06T00:
source share

Each airport is a node . Each ticket is an edge . Create an adjacency matrix to represent the graph. This can be done as a bit field for edge compression. The starting point will be a node in which there is no path (the column will be empty). Once you know this, you simply follow the paths that exist.

Alternatively, you can build an airport indexable structure. For each ticket, you look at its src and dst . If none of them are found, you need to add new airports to your list. When everyone is found, you set the exit indicator for the departure airport to indicate the destination, and the arrival indicator for the point indicate the departure airport. When you do not have tickets, you must go through the entire list to determine who has no way.

Another way would be to have a list of variable-length mini rides that you connect together when you come across each ticket. Each time you add a ticket, you see if the ends of any existing mini-trip match either src or dest of your ticket. If not, your current ticket becomes his mini-ride and is added to the list. If so, then the new ticket is tied to the end (s) of the existing route (s) that it matches, possibly splicing two existing mini-trips together, in which case it will shorten the list of mini-trips by one.

+1
Jun 07 '10 at 18:20
source share

This is a simple case of the state matrix of the final state. Sorry that the pseudo-code was in C # style, but it was easier to express the idea with objects.

First, we construct the matrix of highways. Read my description of what a trunk matrix is โ€‹โ€‹(don't worry about the FSM answer, just an explanation of the trunk matrix). What are the testing strategies for large state machines? .

However, the limitations you describe make it a random, simple state machine. This is the simplest state machine with full coverage.

For a simple case from 5 airports,
vert nodes = src / entry points,
nodes horiz = dst / exit points.

  A1 A2 A3 A4 A5 A1 x A2 x A3 x A4 x A5 x 

Note that for each row, as well as for each column, there should be no more than one transition.

To get the path to the car, you sort the matrix into

  A1 A2 A3 A4 A5 A2 x A1 x A3 x A4 x A5 x 

Or sort by the diagonal square matrix - an eigenvector of ordered pairs.

  A1 A2 A3 A4 A5 A2 x A5 x A1 x A3 x A4 x 

where ordered pairs are a list of tickets:

a2: a1, a5: a2, a1: a3, a3: a4, a4: a5.

or in a more formal notation,

 <a2,a1>, <a5,a2>, <a1,a3>, <a3,a4>, <a4,a5>. 

Hmmm .. ordered couples huh? A hint of recursion in Lisp?

 <a2,<a1,<a3,<a4,a5>>>> 

There are two modes of operation of the machine:

  • travel planning - you donโ€™t know how many airports there are, and you need a general travel plan for an indefinite number of airports
  • restoring a trip - you have all the tickets for the dead end of your last trip but they are all one big stack in your glove compartment / luggage bag.

I assume your question is about restoring the trip. Thus, you select one ticket after another randomly from this pile of tickets.

We assume that a bunch of tickets are of an undetermined size.

  tak mnx cda bom 0 daj 0 phi 0 

Where 0 value indicates unordered tickets. Let's define an unordered ticket as a ticket, where its dst does not match the src of another ticket.

The next next ticket discovers that mnx (dst) = kul (src) matches.

  tak mnx cda kul bom 0 daj 1 phi 0 mnx 0 

At any time you choose the next ticket, it is likely that it connects two consecutive airports. If this happens, you will create a node cluster of these two nodes:

 <bom,tak>, <daj,<mnx,kul>> 

and the matrix decreases,

  tak cda kul bom 0 daj L1 phi 0 

Where

 L1 = <daj,<mnx,kul>> 

which is a sublist of the main list.

Keep choosing the next random tickets.

  tak cda kul svn xml phi bom 0 daj L1 phi 0 olm 0 jdk 0 klm 0 

Match either existent.dst with new.src
or existent.src in new.dst:

  tak cda kul svn xml bom 0 daj L1 olm 0 jdk 0 klm L2 <bom,tak>, <daj,<mnx,kul>>, <<klm,phi>, cda> 

The above topological exercise is for visual understanding only. The following is an algorithmic solution.

The concept is to cluster ordered pairs in the lists to reduce the load on the hash structures that we will use to place tickets. Gradually there will be more and more pseudo-tickets (formed from united agreed tickets), each of which contains a growing list of ordered destinations. Finally, there will be one pseudo-ticket in his sublist containing the full route vector.

As you can see, perhaps this is best done with Lisp.

However, as an exercise related lists and maps ...

Create the following structures:

 class Ticket:MapEntry<src, Vector<dst> >{ src, dst Vector<dst> dstVec; // sublist of mergers //constructor Ticket(src,dst){ this.src=src; this.dst=dst; this.dstVec.append(dst); } } class TicketHash<x>{ x -> TicketMapEntry; void add(Ticket t){ super.put(tx, t); } } 

So, effectively,

 TicketHash<src>{ src -> TicketMapEntry; void add(Ticket t){ super.put(t.src, t); } } TicketHash<dst>{ dst -> TicketMapEntry; void add(Ticket t){ super.put(t.dst, t); } } TicketHash<dst> mapbyDst = hash of map entries(dst->Ticket), key=dst TicketHash<src> mapbySrc = hash of map entries(src->Ticket), key=src 

When a ticket accidentally gets out of a heap,

 void pickTicket(Ticket t){ // does t.dst exist in mapbyDst? // ie attempt to match src of next ticket to dst of an existent ticket. Ticket zt = dstExists(t); // check if the merged ticket also matches the other end. if(zt!=null) t = zt; // attempt to match dst of next ticket to src of an existent ticket. if (srcExists(t)!=null) return; // otherwise if unmatched either way, add the new ticket else { // Add t.dst to list of existing dst mapbyDst.add(t); mapbySrc.add(t); } } 

Check for existing dst:

 Ticket dstExists(Ticket t){ // find existing ticket whose dst matches t.src Ticket zt = mapbyDst.getEntry(t.src); if (zt==null) return false; //no match // an ordered pair is matched... //Merge new ticket into existent ticket //retain existent ticket and discard new ticket. Ticket xt = mapbySrc.getEntry(t.src); //append sublist of new ticket to sublist of existent ticket xt.srcVec.join(t.srcVec); // join the two linked lists. // remove the matched dst ticket from mapbyDst mapbyDst.remove(zt); // replace it with the merged ticket from mapbySrc mapbyDst.add(zt); return zt; } Ticket srcExists(Ticket t){ // find existing ticket whose dst matches t.src Ticket zt = mapbySrc.getEntry(t.dst); if (zt==null) return false; //no match // an ordered pair is matched... //Merge new ticket into existent ticket //retain existent ticket and discard new ticket. Ticket xt = mapbyDst.getEntry(t.dst); //append sublist of new ticket to sublist of existent ticket xt.srcVec.join(t.srcVec); // join the two linked lists. // remove the matched dst ticket from mapbyDst mapbySrc.remove(zt); // replace it with the merged ticket from mapbySrc mapbySrc.add(zt); return zt; } 

Check for existing src:

 Ticket srcExists(Ticket t){ // find existing ticket whose src matches t.dst Ticket zt = mapbySrc.getEntry(t.dst); if (zt == null) return null; // if an ordered pair is matched // remove the dst from mapbyDst mapbySrc.remove(zt); //Merge new ticket into existent ticket //reinsert existent ticket and discard new ticket. mapbySrc.getEntry(zt); //append sublist of new ticket to sublist of existent ticket zt.srcVec.append(t.srcVec); return zt; } 

I have the feeling that the above has some typos, but the concept should be correct. Any typo found, someone can fix it, plsss.

+1
Jun 25 2018-10-06T00:
source share

The easiest way is with hash tables, but it does not have the best worst complexity ( O (n 2 ) )

Instead

  • Create a bunch of nodes containing (src, dst) O (n)
  • Add nodes to the list and sort by src O (n log n)
  • For each (destination) node, find a list for the corresponding (source) node O (n log n)
  • Find the beginning of a node (for example, using topological sorting or marking nodes in step 3) O (n)

Overall: O (n log n)

(For both algorithms, we assume that the string length is negligible, i.e., a comparison of O (1))

0
Jun 07 '10 at 19:32
source share

No need for hashes or anything like that. ( n), "" (, N) , char , .

k ( k 42), bucketsort n N, k O (n + N + k) . , n <= N () k <= N ( N - , )

  • , , struct , .
  • Bucketsort struct
  • ( 0) . ( ) ( ) ( , src dst ) .
  • src0 .
  • src dst , , , src0 .
  • (= toplogical sort src0 ) .
0
22 . '10 21:35
source share

, (, ):

  • - S D
  • src D
  • , node D node
  • , node S keyed on src
  • 3 src โ†” des, S โ†” D
  • 2 node.

O(n) . , ( - ), , . , ( O(n) ), - . , , (~ O(sqrt(n)) ), , ( ) .

0
23 . '10 14:52
source share

, .

- node, - . .

: .

, , , . , , , .

: node , , . .

, , ( ). , - , , .

, node. O (1), . n , O (N) , O (N) O (N) . O (N). .

, , O (1) .

0
23 . '10 15:03
source share

, only , ( ), , , .

, , , O (1) O (1) (.. -, , , ..).

, , , O (n) ( , , , O (n log n) O (1) )

0
Jun 24 '10 0:06
source share

.

, , . , , .




.

. , . ( , )

: airportX < airportY , , , , .




" ". . , . :

 src: A dst: B 

- :

 A->AB B->AB 

.

, . , , .

0
24 . '10 18:02
source share

The necessary conditions

, - , .

, abcdefg , bcd , .. .

-, , . , Hashtable , , - , . , -.

, , .

, . , x y ( (x,y) ). Check, wheter x - - s ( , ). x , (x,y) s . , x , , t , y . , (x,y) t . t , , (x,y) .

"".

  • subtrip s , (x,y) , x - "-" y - ", ".
  • (x,y) s=(y,...) , y - x - .
  • (x,y) s=(...,x) , x - y - .

, , O (1).

. , (n-1)/2 = O(n) .

. subtrip s=(x,...,y) , - , t=(...,x) , x . , t s . , , s ; , u=(y,...) , y . , s u . , ( - ).

, somtehing, :

  • ( O(n) ) O(n) , O(1) . , - ( subtrips ). - () O(1) . , O(n) .
  • , , O(n) . , , : Hashtable, O(1) , O(1) -.

, O(n) , O -bound, , , , , .

0
25 . '10 5:55
source share

python, src to dst. . O (1), O (n), O (lg (n)), STL-, O (n lg (n))

 import random # actual journey: a-> b -> c -> ... g -> h journey = [('a','b'), ('b','c'), ('c','d'), ('d','e'), ('e','f'), ('f','g'), ('g','h')] #shuffle the journey. random.shuffle(journey) print("shffled journey : ", journey ) # Hashmap to get the count of each place map_count = {} # Hashmap to find the route, contains src to dst mapping map_route = {} # fill the hashtable for j in journey: source = j[0]; dest = j[1] map_route[source] = dest i = map_count.get(source, 0) map_count[ source ] = i+1 i = map_count.get(dest, 0) map_count[ dest ] = i+1 start = '' # find the start point: the map entry with count = 1 and # key exists in map_route. for (key,val) in map_count.items(): if map_count[key] == 1 and map_route.has_key(key): start = key break print("journey started at : %s" % start) route = [] # the route n = len(journey) # number of cities. while n: route.append( (start, map_route[start]) ) start = map_route[start] n -= 1 print(" Route : " , route ) 
0
17 . '11 14:50
source share

:

, 1

1 .

.

.

( ) ( ).

(), , , . , .

 #include<vector> #include<string> #include<unordered_map> #include<unordered_set> #include<set> #include<map> using namespace std; struct StringPairHash { size_t operator()(const pair<string, string> &p) const { return hash<string>()(p.first) ^ hash<string>()(p.second); } }; void calcItineraryRec(const multimap<string, string> &cities, string start, vector<string> &itinerary, vector<string> &res, unordered_set<pair<string, string>, StringPairHash> &visited, bool &found) { if (visited.size() == cities.size()) { found = true; res = itinerary; return; } if (!found) { auto pos = cities.equal_range(start); for (auto p = pos.first; p != pos.second; ++p) { if (visited.find({ *p }) == visited.end()) { visited.insert({ *p }); itinerary.push_back(p->second); calcItineraryRec(cities, p->second, itinerary, res, visited, found); itinerary.pop_back(); visited.erase({ *p }); } } } } vector<string> calcItinerary(vector<pair<string, string>> &citiesPairs) { if (citiesPairs.size() < 1) return {}; multimap<string, string> cities; set<string> uniqueCities; for (auto entry : citiesPairs) { cities.insert({ entry }); uniqueCities.insert(entry.first); uniqueCities.insert(entry.second); } for (const auto &startCity : uniqueCities) { vector<string> itinerary; itinerary.push_back(startCity); unordered_set<pair<string, string>, StringPairHash> visited; bool found = false; vector<string> res; calcItineraryRec(cities, startCity, itinerary, res, visited, found); if (res.size() - 1 == cities.size()) return res; } return {}; } 

Here is a usage example:

  int main() { vector<pair<string, string>> cities = { {"Y", "Z"}, {"W", "X"}, {"X", "Y"}, {"Y", "W"}, {"W", "Y"}}; vector<string> itinerary = calcItinerary(cities); // { "W", "X", "Y", "W", "Y", "Z" } // another route is possible {WYWXYZ}, but the route above is lexicographically smaller. cities = { {"Y", "Z"}, {"W", "X"}, {"X", "Y"}, {"W", "Y"} }; itinerary = calcItinerary(cities); // empty, no way to travel all cities using each ticket exactly one time } 
0
08 . '19 20:58
source share



All Articles