双向链表的学习
2015-09-13 16:29
253 查看
本文章主要是学习双向链表的创建,显示数据,插入和删除结点,“逆置"(引号是因为双向链表有2个方向,所以我们要实现逆置,只需要利用它的前指针即可)
我们还是用代码来说明吧
1.结构体的创建
双向链表比单向链表多了一个指针域就是代码中的 pre指针,其实这些名称是为了我们好理解,指针只是存放地址,它不会知道哪个是前哪个是后,只是我们创建了这样一个结构体,给了它2个指针域,可以存放2个地址,就这样它就可以前后想通,即可以存放它前一个数据的地址,又可以存放后一个数据的地址,这样就方便我们去查询。
2.双向链表的创建
我们还是创建了带头结点的双向链表,头结点的前指针指向NULL.
这里我就不画图了,原理跟单向链表一样,只是多了一个指针域,例如:p->next=l;这个代码是单向链表建立联系的关键,这里我们多了一句 l->pre=p,这样就成了一个双向链表,新建的l结点可以查找它上一个结点的地址了,而不是只有它下一个结点的地址。
3.链表数据显示
这个没什么好说的,还是和单向链表一样
4.双向链表的结点插入
p->next->pre=new;
new->next=p->next;
p->next=new;
new->pre=p;
这就是实现插入的方法,其实方法有很多很多,关键的问题是你要知道你是操作的地址,所以我们必须得保存一个关键地址就是p->next,这个地址必须得最后操作,如果你这样写:
p->next=new;
new->pre=p;
p->next->pre=new;
new->next=p->next;
前面2句你是建立了这个新结点和p结点的联系,但是当你去想建立新结点和p后面那个结点的联系的时候(后面2句),你会发现已经找不到地址了,因为p结点和它后面的那个结点的联系你已经切断了,找不到它后面结点的位置了,所以切记,这里就是双向链表中插入的一个陷阱。
如果你想这么做也行,你就得再建立一个临时指针,去存放p->next的地址,把p和它原来后面的那个结点的地址保存下来,这样你就可以先建立p和新结点的联系了。
5.双向链表的删除
方法是很多种的,这里只是我的一种方法,你也可以找到p 前面那个结点的地址,再和p后面的那个结点建立联系,大同小异。
6.双向链表的”逆置“
最后是我们的main函数
我们还是用代码来说明吧
1.结构体的创建
typedef struct double_link { int data;//数据域 struct double_link *pre;//指针域,存放前一个数据地址 struct double_link *next;//指针域,存放后一个数据地址 }node;
双向链表比单向链表多了一个指针域就是代码中的 pre指针,其实这些名称是为了我们好理解,指针只是存放地址,它不会知道哪个是前哪个是后,只是我们创建了这样一个结构体,给了它2个指针域,可以存放2个地址,就这样它就可以前后想通,即可以存放它前一个数据的地址,又可以存放后一个数据的地址,这样就方便我们去查询。
2.双向链表的创建
node * creat_double_link(int n) { node *head,*l,*p; int i; head=(node *)malloc(sizeof(node)); head->next=NULL; head->pre=NULL; p=head; for(i=0;i<n;i++) { l=(node*)malloc(sizeof(node)); printf("intput the %d data:\n",i+1); scanf("%d",&l->data); p->next=l; l->pre=p; p=l; } p->next=NULL; return head; }
我们还是创建了带头结点的双向链表,头结点的前指针指向NULL.
这里我就不画图了,原理跟单向链表一样,只是多了一个指针域,例如:p->next=l;这个代码是单向链表建立联系的关键,这里我们多了一句 l->pre=p,这样就成了一个双向链表,新建的l结点可以查找它上一个结点的地址了,而不是只有它下一个结点的地址。
3.链表数据显示
void printlist(node * head) { node *p; int i=1; p=head->next; while(p!=NULL) { printf("the (%d) data=%d\n",i,p->data); i++; p=p->next; } }
这个没什么好说的,还是和单向链表一样
4.双向链表的结点插入
void insert_list(node *head,int i) { node * new,*p; p=head; int j=0; while(j<i)//遍历结点,寻找我们要插的结点的位置,这里是在这个结点后面插,这里还是跟我单向链表一样,你输入i=1的时候就是第一个数据结点,如果想在头结点插的话还是输入i=0; { p=p->next; j++; } new=(node*)malloc(sizeof(node)); printf("input the data:\n"); scanf("%d",&new->data); if(p->next!=NULL)//这里是判断我们插入的结点是不是最后一个结点,如果不这样做的话当我们插入最后结点后面的时候报出现段错误,原因也很好分析,因为如果是最后一个结点,那么p->next就是null了,一个空指针你去操作它肯定有问题的 { p->next->pre=new; new->next=p->next; p->next=new; new->pre=p; } else//如果是最后一个结点就这样操作 { new->pre=p; p->next=new; new->next=NULL; } }链表的插入的关键就是下面:
p->next->pre=new;
new->next=p->next;
p->next=new;
new->pre=p;
这就是实现插入的方法,其实方法有很多很多,关键的问题是你要知道你是操作的地址,所以我们必须得保存一个关键地址就是p->next,这个地址必须得最后操作,如果你这样写:
p->next=new;
new->pre=p;
p->next->pre=new;
new->next=p->next;
前面2句你是建立了这个新结点和p结点的联系,但是当你去想建立新结点和p后面那个结点的联系的时候(后面2句),你会发现已经找不到地址了,因为p结点和它后面的那个结点的联系你已经切断了,找不到它后面结点的位置了,所以切记,这里就是双向链表中插入的一个陷阱。
如果你想这么做也行,你就得再建立一个临时指针,去存放p->next的地址,把p和它原来后面的那个结点的地址保存下来,这样你就可以先建立p和新结点的联系了。
5.双向链表的删除
void delete_list(node * head,int i) { node * p,*q; int j=0; p=head; while(j<i) { p=p->next; j++; } if(p->next!=NULL) { q=p->next; q->pre=p->pre; p->pre->next=q; } else { p->pre->next=NULL; } free(p); }这里是先找到我们要删除的结点p,然后再将p结点后面一个结点的地址赋值给q,再建立q和p前面一个结点的联系即可,最后free掉p即可。
方法是很多种的,这里只是我的一种方法,你也可以找到p 前面那个结点的地址,再和p后面的那个结点建立联系,大同小异。
6.双向链表的”逆置“
void reverse_display_list(node * head) { node *p; int i=1; p=head; while(p->next!=NULL) { p=p->next; } while(p->pre) { printf("the (%d) data=%d\n",i,p->data); i++; p=p->pre; } }这里我只是先遍历到最后一个结点然后用它的pre指针一个个显示数据
最后是我们的main函数
void main() { node * head; int n,i; printf("intput the number of the data:\n"); scanf("%d",&n); head=creat_double_link(n); printlist(head); printf("input the position of the insert the list\n"); scanf("%d",&i); insert_list(head,i); printlist(head); printf("input the position of the delete the list\n"); scanf("%d",&i); delete_list(head,i); printlist(head); printf("the reverse_display_listlist:\n"); reverse_display_listlist(head); }最后上图
相关文章推荐
- java volatile关键字
- vim 的三种模式的用法
- 使用slick的codegen生成table code等
- hibernate3(1)
- Spring MVC XmlViewResolver example
- io流中的字节和字符
- 201 Bitwise AND of Numbers Range
- 回溯法:字符串转换
- 什么是关系型数据库
- WM_NOTIFY Message
- 软工视频总结
- 给UITextView添加PlaceHolder
- [转]如何卸载eclipse中的ADT
- TextureView+SurfaceTexture+OpenGL ES来播放视频(一)
- 【开源项目解析】QQ“一键下班”功能实现解析——学习Path及贝塞尔曲线的基本使用
- hdu 1131 Count the Trees 卡特兰数+java
- 关系型数据库笔试面试题
- 一次上线记录
- hdu 1131 Count the Trees 卡特兰数+java
- C#在Winform中改变Textbox高度三种方法