简体   繁体   中英

Finding all non overlapping cycles in an undirected graph

I need to find all simple non overlapping cycles on undirected graph. To find all existing cycles I made an Objective-C version of the algorithm that I found here:

Finding all cycles in undirected graphs

@interface HSValue : NSObject
@property (nonatomic, assign) CGPoint point;
@end
@implementation HSValue
@end


@interface CyclesFinder ()
@property (nonatomic, strong) NSMutableArray <NSArray<HSValue *>*> *cycles;
@property (nonatomic, strong) NSArray <NSArray<HSValue*>*> *edges;
@end

@implementation CyclesFinder

-(void)findCyclesInGraph:(NSArray <NSArray<HSValue*>*> *)edges {
   self.edges = edges;
   for (NSInteger i=0; i < self.edges.count; i++) {
        for (NSInteger j=0; j < self.edges[i].count; j++) {
            [self findNewCycles:@[self.edges[i][j]]];
        }
    }
}

-(void)findNewCycles:(NSArray <HSValue *> *)path {

    HSValue *startNode = path[0];
    HSValue *nextNode;
    NSArray <HSValue *> *sub;

    for (NSInteger i=0; i < self.edges.count; i++) {
        NSArray <HSValue *> *edge = self.edges[i];
        if ([edge containsObject:startNode]) {
            if ([edge[0] isEqual:startNode]) {
                nextNode = edge[1];
            }
            else {
                nextNode = edge[0];
            }
        }
        else {
            nextNode = nil;
        }

        if (![path containsObject:nextNode] && nextNode) {
            sub = @[nextNode];
            sub = [sub arrayByAddingObjectsFromArray:path];
            [self findNewCycles:sub];
        }
        else if (path.count > 2 && [nextNode isEqual:path.lastObject]) {
            if (![self cycleExist:path]) {
                [self.cycles addObject:path];
                break;
            }
        }
    }
}

-(BOOL)cycleExist:(NSArray <HSValue*> *)path {
    path = [path sortedArrayUsingSelector:@selector(compare:)];
    for (NSInteger i=0; i < self.cycles.count; i++) {
        NSArray <HSValue *> *cycle = [self.cycles[i] sortedArrayUsingSelector:@selector(compare:)];
        if ([cycle isEqualToArray:path]) {
            return TRUE;
        }
    }

    return FALSE;
}

Above algorithm works fine (even if it is not very efficient) and it finds all the possible cycles from the graph on the attached picture (please see picture below):

ABHGFDEA (valid)

BCIHB (valid)

GHILKG (valid)

FGKJF (valid)

FGHILKJF (invalid)

ABCIHGFDEA (invalid)

ABCILKJFDEA (invalid)

ABCIHG--KJFDEA (invalid)

ABHILKGFDEA (invalid)

ABHGKJFDEA (invalid)

ABCILKGFDEA (invalid)

BCILKGHB (invalid)

BCILKJFGHB (invalid)

However when I run the above algorithm I want to end up with only those cycles that I highlighted with coloured polygons on the left hand side example. What I don't want are the cycles like the one on the right hand side example.

在此处输入图片说明

My first thought was that overlapping cycle will be a cycle that includes all the points from any other cycles, but this is not always true. Can someone point me into the right direction? Is it possible to modify the above algorithm so it finds only non-overlapping cycles or if not what should I do after finding all cycles to filter them?

There isn't enough information just in the undirected graph itself to determine which cycles are which. For example, consider that the following 2 diagrams yield identical undirected graphs:

A-----B      E-------------F
|     |       \           /
C-----D        \ A-----B /
|     |         \|     |/
E-----F          C-----D

But for the diagram on the left, you want the cycles ABDCA and CDFEC, while for the diagram on the right, you want the cycles ABDCA and EFDBACE. Thus the undirected graph inferred from the diagram isn't enough -- you need to somehow incorporate spatial information from the original diagram.

I'm working on this same problem and a lot of your comments were helpful, especially the comment that all edges will have an area on each side. Thus you could say that each edge has a "left area" and a "right area".

You can add all graph edges to a queue in any order. Peek at the first edge, pick its vertex closer to your origin. Move to the neighbor that is the most counter-clockwise. continue this until you have reached your starting vertex. All of these edges bound your first area. I would give it a unique ID and assign it to a "left area" property of those edges.

Peek at the first edge in the queue and check if it has a "left area". If it does check if it has a "right area" if it does not proceed in a clockwise manner and find the right area. If it has both areas assigned dequeue it and grab the next one.

should be O(e+v) so pretty quick, right?

This is a little bit stream of consciousness but I wanted to get it written down. I'll be writing the algorithm for my actual app and I'll make tweaks as I find problems in it.

Of course I'm open to feedback and suggestions :)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM