单链表---邻接表

近段时间在学习线段树,二叉树,字典树,网络流,图,都需要动态内存分配和建立链式结构,每次都是要看好长时间,一个朋友告诉我,好好看看单链表,把最基础的抓住,其他的就会迎刃而解。

动态内存分配对建立表、树、图和其他链式结构是特别有用的。

 单链表       

结构体声明类型:

struct node

{

  int date;

  node *next;

};

单链表的建立

struct node *p, *head;

head=NULL;

p=(struct node *)malloc(sizeof(struct node));//为节点分配内存空间

scanf("%d", &p->date);//把数据存储到节点中或者直接赋值 p->date=a;

p->next=head; head=p;//把节点插入到链表中,此方法为从头插入,如果你输入的是5 4 3 2 1,那么里面的顺序会是 1 2 3 4 5

 

单链表的遍历

struct node *p;//定义一个指针在链表之间移动;

p=head;//首先指向表中的第一个元素

while(p)//如果没有表尾就执行循环

{

  printf("%d", p->date);//输出p所指节点的元素值

  p=p->next;//让p指向下一个节点

}

 

 单链表的插入.

一般从中间插入.此时就应该找到要插入的是哪一个结点后面,则称此节点为要插入节点的前驱节点,用pre表示

struct node *p, *pre;

p=(struct node *)malloc(sizeof(struct node));//创建一个新结点

p->next=pre->next;//将新节点的next指向前驱节点next指向的那个节点

pre->next=p;//将前驱节点的next指针指向新节点

 

单链表的删除

删除p点,此时我们就要找到p点的前驱节点

if(pre==NULL)

  head=head->next;//如果要删除的是第一个节点,则改变头指针的方向

else

  pre->next=p->next;//否则改变前一个节点,使它绕过要删除的节点

free(p);//释放要删除的p这个节点的空间

 

 邻接表      

写过之后我仍然无法明白,邻接表的原理,再接再厉;

朋友不建议我直接用vector 类,他说我这样用现成的会阻挡我的步伐,虽说现在进度是快了点,但如果最基础的如何建立都不明白,又何谈灵活的应用呢,根本转化不成自己的东西,他说让我先从最基础的学起,尽量不用那些C++里面的函数库,他说让我自己实现,这样我的代码和思维能力就会有很大的提高,加油!

说实话,我觉得邻接表很神奇,如果是我我可能就不会如此建立,这是参考别人代码,才知道这样建立的

 邻接表,其实也要结构体类型声明

struct node

{

int date;

node *next;

}G[N];

int head[N];

memset(head, -1, sizeof(head));

int cnt=0;

void addedge(int a, int b)

{

  G[cnt].date=b;

  G[cnt].next=head[a];

  head[a]=cnt;

  cnt++;

}

说实话,我是看了两天才明白了最基础的,智商不够啊,很可惜,没有人真正的指点我,不过我搜了百度文库,看不明白;

 

给你一组数据代表a与b相连

1 2

1 4

2 4

2 3

3 4

cnt0         1            2          3           4           
11223
G[cnt].date=b24434
next=head[a]-10-12-1

head[a]现在的值

也就是cnt++之后的值

head[1]=0head[1]=1head[2]=2head[2]=3head[3]=4

 

 

 

 

 

 

我们建立的邻接表其实是这样的(单向的)

1  与2 4相连

2  与4 3相连

3  与4   相连

 

head[0]=-1;

head[1]=1;

head[2]=3;

head[3]=4;

head[4]=-1;

 

你们又没有发现什么,类似与栈,也就是说cnt 0->1  如果遍历的话,就for(j=head[a]; j!=-1; j=G[j].next) 一般a从start开始一般是1,到end;这时候观察一下,head[1]=1;此时G[j] 刚好就是G[1]也就是与1相连的4,之后j=0;刚好是与1相连的2;类似与栈,我们将1压入栈底,之后压入4;所以我们寻找的时候首先搜索到的head[a]最后进栈的,之后再向前推,由此,你明白为什么G[cnt].next=head[a];

head[a]=cnt;也就是说这里的G[cnt].next存的是上一个与u相连的值,而head[a]=cnt就是记录此时的下标,方便为下一个的next赋值;

观察紫色与紫色数字之间的关系,红色与 红色数字之间的关系,再 看他们这两行 的‘a’值,竟然是相同的

而且head[a]=cnt;就本列来说; 所以你就会明白 其实next=head[a]的含义就是建立关系,即使是在G数组中

我建立的是单向的,如果是双向路这样做也可以,只用添加反边就行了;

  通俗的说 与节点 a相连的第一条边是G[cnt].date,那么‘a’相连的下一条边是G[cnt].next;而G[cnt].next又等于下一条边的数组的下标

G[cnt1].next=head[a]; head[a]=cnt2; 你们看看领悟领悟  这里cnt1 与cnt2 就是与 ‘a’相连的边的数组的下标;

这里推荐一个博客,写的简洁明了,我搜的时候搜到的;http://blog.csdn.net/u014303647/article/details/38865357

 

 大家可以观察一下,next这一行, 与cnt这一行的关系。其实就可以发现next其实代表与  ‘1’  相连的第一个数是 ‘2’  第二个数也就是(next)下一个数是 ‘4’;这就是为什么a不存入其中的原因;

同理 与‘2’ 相连的是‘4’ 之后的是‘3’;依次进入表中,看过这个大家也就明白为什么next=head[a]了吧,每次head[a]都会更新为cnt现在的值也就是记录,与‘a'相连的这个数‘b’所存在的位置;

当我们遍历的时候,就可通过寻找G[i].next来寻找与‘a’相连的其他数;因此每次addage这个函数里面 可以总结两条 更新过后的head[a]=cnt;

 

之前用的是#include<vector>这个头文件,但是其实这样不好,本末倒置了,如果我先理解上面那一种,我理解vector类就会更清晰,但是先理解vector类在理解基础的建立过程就会很艰难;

(1)vector<int>a;//相当于申请一个一维数组

a.push_back(i);//压入

a.size();//得到长度

a.clear();//清空

(2)vector<int>G[N];相当于vector<vector<int >>G//相当于申请一个二维数组

G.resize(n+1);//申请内存

G[i].push_back(j);

G[i].size();

G[i].clear();

转载于:https://www.cnblogs.com/wazqWAZQ1/p/4729025.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值