大家好,我是刘天昊,那么这次的题目厉害了
因为之前我的blog也没有关于图结构的操作,这次特地找了leetcode总不多的图结构的操作题
先看题目
Clone an undirected graph. Each node in the graph contains a label
and a list of its neighbors
.
Nodes are labeled uniquely.
We use#
as a separator for each node, and
,
as a separator for node label and each neighbor of the node.
As an example, consider the serialized graph {0,1,2#1,2#2,2}
.
The graph has a total of three nodes, and therefore contains three parts as separated by #
.
0
. Connect node 0
to both nodes 1
and 2
.1
. Connect node 1
to node 2
.2
. Connect node 2
to node 2
(itself), thus forming a self-cycle.Visually, the graph looks like the following:
1 / \ / \ 0 --- 2 / \ \_/
1.图中节点的值是唯一的--也就是说不会重复
2.图中是有环的
3.看看题目中给的结构
/**
* #define NEIGHBORS_MAX_SIZE 100
* struct UndirectedGraphNode {
* int label;
* struct UndirectedGraphNode *neighbors[NEIGHBORS_MAX_SIZE];
* int neighborsCount;
* };
*/
label是节点中的值,
neighbors是这个节点连接的其他节点
neighborsCount就是数量
举个栗子
{0,0,0}这就是一个带环的图,都指向自己(这也是测试集中的一个例子,可能有点奇怪)
ok接下来厉害了
网上都是java,c++的代码我来给大家带来c的代码直接po代码po完讲解
/**
* #define NEIGHBORS_MAX_SIZE 100
* struct UndirectedGraphNode {
* int label;
* struct UndirectedGraphNode *neighbors[NEIGHBORS_MAX_SIZE];
* int neighborsCount;
* };
*/
typedef struct QueueNode
{
struct UndirectedGraphNode *Gnode;
struct QueueNode *next;
}QueueNode;
typedef struct Queue
{
QueueNode *front;
QueueNode *tail;
int Size;
}Queue;
Queue *InitQueue()
{
Queue *queue=(Queue *)malloc(sizeof(Queue ));
queue->front=NULL;
queue->tail=NULL;
queue->Size=0;
return queue;
}
void PushQueue(Queue *queue,struct UndirectedGraphNode *Gnode)
{
QueueNode *Qnode=(QueueNode *)malloc(sizeof(QueueNode ));
Qnode->Gnode=Gnode;
Qnode->next=NULL;
if(queue->Size==0)
{
queue->front=Qnode;
}
else
{
queue->tail->next=Qnode;
}
queue->tail=Qnode;
queue->Size++;
}
QueueNode *PopQueue(Queue *queue)
{
QueueNode *p=queue->front;
queue->front=p->next;
queue->Size--;
return p;
}
typedef struct HashNode
{
int key;
int pos;
}HashNode;
typedef struct HashMap
{
int Size;
HashNode **storage;
}HashMap;
HashMap *InitHash(int Size)
{
HashMap *hashmap=(HashMap *)malloc(sizeof(HashMap ));
hashmap->Size=Size;
hashmap->storage=(HashNode **)calloc(Size,sizeof(HashNode *));
return hashmap;
}
int PushHash(HashMap *hashmap,int key)
{
int addr=abs(key)%hashmap->Size;
HashNode *node;
while(node=hashmap->storage[addr])
{
if(key==hashmap->storage[addr]->key)
{
return 0;
}
if(addrSize-1)
{
addr++;
}
else
{
addr=0;
}
}
node=(HashNode *)malloc(sizeof(HashNode));
node->key=key;
hashmap->storage[addr]=node;
return 1;
}
void PushHashRelation(HashMap *hashmapRelation,int key,int pos)
{
int addr=abs(key)%hashmapRelation->Size;
HashNode *node;
while(node=hashmapRelation->storage[addr])
{
if(addrSize-1)
{
addr++;
}
else
{
addr=0;
}
}
node=(HashNode *)malloc(sizeof(HashNode));
node->key=key;
node->pos=pos;
hashmapRelation->storage[addr]=node;
}
int GetHashPostion(HashMap *hashmapRelation,int key)
{
int addr=abs(key)%hashmapRelation->Size;
HashNode *node;
while(node=hashmapRelation->storage[addr])
{
if(key==hashmapRelation->storage[addr]->key)
{
return hashmapRelation->storage[addr]->pos;
}
if(addrSize-1)
{
addr++;
}
else
{
addr=0;
}
}
return 0;
}
struct UndirectedGraphNode *cloneGraph(struct UndirectedGraphNode *graph)
{
if(graph==NULL)
{
return NULL;
}
HashMap *hashmap=InitHash(2048);
HashMap *hashmapRelation=InitHash(2048);
Queue *queue=InitQueue();
PushQueue(queue,graph);
int flag=PushHash(hashmap,graph->label);
struct UndirectedGraphNode *newGraph[2048];
int save[8096];
int p=0;
int q=0;
while(queue->Size)
{
QueueNode *node=PopQueue(queue);
struct UndirectedGraphNode *newnode=(struct UndirectedGraphNode *)malloc(sizeof(struct UndirectedGraphNode ));
newnode->label=node->Gnode->label;
newnode->neighborsCount=node->Gnode->neighborsCount;
newGraph[p]=newnode;
PushHashRelation(hashmapRelation,newnode->label,p);
p++;
for(int i=0;iGnode->neighborsCount;i++)
{
if(PushHash(hashmap,node->Gnode->neighbors[i]->label))
{
PushQueue(queue,node->Gnode->neighbors[i]);
}
save[q]=node->Gnode->neighbors[i]->label;
q++;
}
}
int ptr=0;
for(int i=0;ineighborsCount;j++)
{
newGraph[i]->neighbors[j]=newGraph[GetHashPostion(hashmapRelation,save[ptr])];
ptr++;
}
}
return newGraph[0];
}
如果你要用自带的stl库自然是不用写这么多代码,其实用数组来表示队列,然后改变原图的一些结构也不用这么多代码,但是我的这些代码理解了
那么你就等于理解了(链式队列+hash表+图结构)所以我这么写主要目的是为了让自己更深的理解数据结构
那么开始讲解
看过我先前的bolg的读者应该知道如果去BFS一个树 ,但是树的BFS是不用考虑它往回走的,但是图不同你要去判断之前的点是否访问过
我们的大思路是先BFS一遍,生成所有的结点,并且同时存储关系,(注意不是两者同时,因为你存储的关系中可能有你还未访问过的结点)
我们建立一个队列用类似于先前的思想
1.入队列第一个图结点
2.出队列判断图的邻居结点是否访问
3.访问过了,不管
4.没访问过入队列返回2步骤
于此同时我们开了save【】的数组去记录邻居结点的label保存下来
newGraph【】用来保存所有的结点的因为我连哈希表都是自己写的所以你要看明白我这题的代码最好c语言上有点功底
这是我们的大思路,接着我们有几个要注意的,这题每个结点的值是唯一的,直接用结点的值作为Key。用hash表判断结点是否访问过也就是
int PushHash(HashMap *hashmap,int key)
这个函数
接着我们在寻找关系的时候也不可以遍历的去判断这个结点的位置我们用这两个函数
void PushHashRelation(HashMap *hashmapRelation,int key,int pos)
void PushHashRelation(HashMap *hashmapRelation,int key,int pos)
可以快速的得到结点的位置
总结下
一遍创建所有结点,保存关系
一遍对应结点的关系
大思路(图的BFS)
这次的代码很长,因为是c语言自己写的很多结构,没有用STL库,也没有动态的去申请内存,所以这题我估计很多看观都不会去写c语言的
那么这里c++和java的思路和我的是差不多的,代码可能就20来行,所以这题就这样也不推荐我的代码,毕竟长了点,这里我还有个大牛的c代码不长不po了
/**
* #define NEIGHBORS_MAX_SIZE 100
* struct UndirectedGraphNode {
* int label;
* struct UndirectedGraphNode *neighbors[NEIGHBORS_MAX_SIZE];
* int neighborsCount;
* };
*/
// Note q means queue, qb means queue begin, the next entry's index, qe means
// queue end, the last element's index in the queue plus one.
struct UndirectedGraphNode *q[12345];
struct UndirectedGraphNode *newnodes[12345];
struct UndirectedGraphNode *cloneGraph(struct UndirectedGraphNode *graph) {
if (graph == NULL) return NULL;
// Traverse the graph and copy individual nodes into newnodes in the same
// order as in the array q. Use negative neighborsCount to mark visited nodes,
// and the label field for index into q and newnodes arrays. The newnodes copy
// will contain the actual label value.
int qb = 0, qe = 0;
q[qe++] = graph;
graph->neighborsCount = -(graph->neighborsCount + 1);
while (qb != qe) {
struct UndirectedGraphNode *newnode, *oldnode;
newnode = malloc(sizeof(*newnode));
newnodes[qb] = newnode;
oldnode = q[qb];
newnode->label = oldnode->label;
oldnode->label = qb;
int neighbors = -(oldnode->neighborsCount + 1);
qb++;
for (int i = 0; i < neighbors; i++) {
struct UndirectedGraphNode *n = oldnode->neighbors[i];
if (n->neighborsCount < 0) continue;
q[qe++] = n;
n->neighborsCount = -(n->neighborsCount + 1);
if (qe >= 12345) {
puts("Graph too large, aborting.");
abort();
}
}
}
// Copy the neighbor info into the newnodes array and restore the
// neighborsCount field in the original nodes.
for (int i = 0; i < qe; i++) {
struct UndirectedGraphNode *newnode, *oldnode;
newnode = newnodes[i];
oldnode = q[i];
int neighbors = -(oldnode->neighborsCount + 1);
oldnode->neighborsCount = neighbors;
newnode->neighborsCount = neighbors;
for (int j = 0; j < neighbors; j++) {
newnode->neighbors[j] = newnodes[oldnode->neighbors[j]->label];
}
}
// Now restore the label info in the old nodes.
for (int i = 0; i < qe; i++) {
q[i]->label = newnodes[i]->label;
}
return newnodes[0];
}