双向链表的定义
双向链表也是链表的一种,它每个数据结点中都有两个结点,分别指向其直接前驱和直接后继。所以我们从双向链表的任意一个结点开始都可以很方便的访问其前驱元素和后继元素。
第一就是头节点的前驱指针指向NULL空指针。
第二就是尾节点的后驱指针指向NULL指针。
双向链表的结构:
双向链表的操作
- 创建双链表
- 新增节点(在链表尾插入)
- 从头遍历链表
- 判断链表是否为空
- 计算链表长度
- 从链表删除节点
- 删除整个链表
- 获取某个节点对象
- 从一个节点向前遍历
- 从一个节点向后遍历
节点类型定义
一个节点有一个前驱节点、一个后驱节点,和数据。
typedef struct doubleLink doubleLinkNode; struct doubleLink{ int value ; doubleLinkNode *pre; doubleLinkNode *next; };
创建双链表的头节点
创建节点,必须使用malloc为节点分配内存,头节点不存储数据,且前驱指针为空,此时刚创建双链表,所以头指针的后驱指针也为空。
//创建双链表的头节点 doubleLinkNode * create(){ doubleLinkNode *head; head = (doubleLinkNode*)malloc(sizeof(doubleLinkNode)); head->pre = NULL; head->next =NULL; return head; }
新增节点,在链表尾插
第一条判断语句是判断头指针为NULL时,返回-1,当链表被删除,头指针为NULL,然后新建node节点,它的后驱指针为NULL,它的前驱指针为原来的最后的节点,所以原来最后的节点的后驱指针变为它,它为双链表的最后一个节点。
int add(doubleLinkNode *head,int value){ if(head == NULL)return -1; doubleLinkNode *t=head; doubleLinkNode *node; node = (doubleLinkNode*)malloc(sizeof(doubleLinkNode)); //此节点的后驱指针为NULL,添加后,它为最后一个节点 node->next =NULL; node->value =value; //找到最后一个节点,最后一个节点的后驱指针为空 while(t->next!=NULL){ t=t->next; } //此节点的前驱指针指向原本的最后一个节点,它成为最后一个节点 node->pre = t; //原本最后节点的后驱指针此节点 t->next = node; return 1; }
从头遍历链表
最后一个指针的NULL为空,这是我们判断的依据,所以判断t->next,直至判断到最后一个节点,这里双链表的头指针是没有数据的,但是直接判断完t->next,然后对t赋值,是不会打印头指针的。
int PrintLink(doubleLinkNode *head){ if(head == NULL)return -1; printf("\n"); doubleLinkNode *t = head; while(t->next!= NULL){ //这里这样做可以不打印第一个节点:头节点,先将t赋值下一个节点 t= t->next; printf("%d ",t->value); } return 1; }
判断链表是否为空
1.双链表被删除,头指针为NULL。
2.双链表存在头指针,但是没有其它节点,为空。
3.双链表的头指针的后驱指针不为NULL,链表不为空。
int Isempty(doubleLinkNode *head){ if(head == NULL)return -1; if(head->next !=NULL)return 1; return 0; }
计算链表长度
注意:双链表的头指针不算第一个,所以头指针的后驱节点为第一个。
int Length(doubleLinkNode *head){ if(head == NULL)return -1; doubleLinkNode *t = head; int len = 0; while(t->next !=NULL){ len++; t = t->next; } return len; }
从链表删除节点
要删除这个节点首先要找打这个节点:
1.如果一直遍历到最后一个节点,还没有到达index,就是没有找到。
2.如果还没有遍历完双链表,就到达index,此时i==index,找到该节点,删除。
这里需要注意:
如果该节点为最后一个节点,既然删除最后一个节点,只需要更改最后一个节点的前一个节点的后驱指针为NULL。
如果该节点不是最后一个节点,就需要更改这个节点左右两边的节点,这个节点的前一个节点的后驱指针指向这个节点的后一个节点,这个节点的后一个节点的前驱指针指向这个节点的前一个节点。
删除使用free()
int deletenode(doubleLinkNode *head,int index){ if(head == NULL)return -1; //找到这个节点 doubleLinkNode *t = head; int i = 0; while((t->next!=NULL)&&(i<index)){ t = t->next; i++; } //这个节点存在 if((i==index)&&(i!=0)){ //且这个节点为最后一个节点 if(t->next==NULL){ t->pre->next =NULL; }else{//不为最后一个节点 t->pre->next = t->next; t->next->pre = t->pre; } //删除节点 free(t); } return 0; }
删除整个链表
doubleLinkNode * delete(doubleLinkNode *head){ if(head == NULL)return NULL; doubleLinkNode *t = head; while(t->next!=NULL){ t = t->next; free(t->pre); } //删除最后一个节点 free(t); head = NULL; return head; }
获取某个节点对象
和删除某个节点类型。
doubleLinkNode * GetNode(doubleLinkNode *head,int index){ if(head == NULL)return NULL; doubleLinkNode *t = head; int i =0; while((t->next!=NULL)&&(i<index)){ t = t ->next; i++; } if((i==index)&&(i!=0)){ return t; } return NULL; }
从一个节点向前遍历
向前遍历,需要注意,不能打印头指针,头指针没有数据,所以这里先打印,后赋值。
int Printpre(doubleLinkNode *node){ if(node == NULL)return -1; printf("\n"); while(node->pre!=NULL){ //头指针不存储值,放中间,不打印第一个节点 printf("%d ",node->value); node = node ->pre; } return 1; }
从一个节点向后遍历
int Printnext(doubleLinkNode *node){ if(node == NULL)return -1; printf("\n"); printf("%d ",node->value); while(node->next!=NULL){ node = node ->next; printf("%d ",node->value); } return 1; }
#include<stdio.h> #include<malloc.h> // 每个节点都包含两个指针,一个指针指向上一个节点,一个指针指向下一个节点。这里有两个特殊的地方,第一就是头节点的一个指针指向NULL空指针(没有前驱节点),第二就是尾节点的一个指针指向NULL指针(没有后继节点)。 //单链表:最后一个节点指针为NULL //双链表:第一个节点的前驱指针为NULL,最后一个节点的后驱指针为NULL typedef struct doubleLink doubleLinkNode; struct doubleLink{ int value ; doubleLinkNode *pre; doubleLinkNode *next; }; //创建双链表的头节点 doubleLinkNode * create(){ doubleLinkNode *head; head = (doubleLinkNode*)malloc(sizeof(doubleLinkNode)); head->pre = NULL; head->next =NULL; return head; } //新增节点,在链表尾插 int add(doubleLinkNode *head,int value){ if(head == NULL)return -1; doubleLinkNode *t=head; doubleLinkNode *node; node = (doubleLinkNode*)malloc(sizeof(doubleLinkNode)); //此节点的后驱指针为NULL,添加后,它为最后一个节点 node->next =NULL; node->value =value; //找到最后一个节点,最后一个节点的后驱指针为空 while(t->next!=NULL){ t=t->next; } //此节点的前驱指针指向原本的最后一个节点,它成为最后一个节点 node->pre = t; //原本最后节点的后驱指针此节点 t->next = node; return 1; } ////打印链表 int PrintLink(doubleLinkNode *head){ if(head == NULL)return -1; printf("\n"); doubleLinkNode *t = head; while(t->next!= NULL){ //这里这样做可以不打印第一个节点:头节点,先将t赋值下一个节点 t= t->next; printf("%d ",t->value); } return 1; } ////判断链表是否为空 int Isempty(doubleLinkNode *head){ if(head == NULL)return -1; if(head->next !=NULL)return 1; return 0; } //计算链表长度 int Length(doubleLinkNode *head){ if(head == NULL)return -1; doubleLinkNode *t = head; int len = 0; while(t->next !=NULL){ len++; t = t->next; } return len; } //从链表删除节点 int deletenode(doubleLinkNode *head,int index){ if(head == NULL)return -1; //找到这个节点 doubleLinkNode *t = head; int i = 0; while((t->next!=NULL)&&(i<index)){ t = t->next; i++; } //这个节点存在 if((i==index)&&(i!=0)){ //且这个节点为最后一个节点 if(t->next==NULL){ t->pre->next =NULL; }else{//不为最后一个节点 t->pre->next = t->next; t->next->pre = t->pre; } //删除节点 free(t); } return 0; } //删除整个链表,释放内存 doubleLinkNode * delete(doubleLinkNode *head){ if(head == NULL)return NULL; doubleLinkNode *t = head; while(t->next!=NULL){ t = t->next; free(t->pre); } //删除最后一个节点 free(t); head = NULL; return head; } //获取某个节点对象 doubleLinkNode * GetNode(doubleLinkNode *head,int index){ if(head == NULL)return NULL; doubleLinkNode *t = head; int i =0; while((t->next!=NULL)&&(i<index)){ t = t ->next; i++; } if((i==index)&&(i!=0)){ return t; } return NULL; } //中间:打印从自己到最前或者最后一个节点以前的节点 //后面:不能打印自己,从自己前面或者后面的节点打印到最前或者最后一个节点 //从一个节点,向前、向后遍历 int Printpre(doubleLinkNode *node){ if(node == NULL)return -1; printf("\n"); while(node->pre!=NULL){ //头指针不存储值,放中间,不打印第一个节点 printf("%d ",node->value); node = node ->pre; } return 1; } //打印自己到最后一个节点 int Printnext(doubleLinkNode *node){ if(node == NULL)return -1; printf("\n"); printf("%d ",node->value); while(node->next!=NULL){ node = node ->next; printf("%d ",node->value); } return 1; } //测试双链表 int main() { doubleLinkNode *head = create(); //添加节点 add(head,2); add(head,3); add(head,4); add(head,7); add(head,8); //打印双链表 PrintLink(head); printf("双链表长度:%d\n",Length(head)); //删除双链表中的第二个节点 deletenode(head,2); PrintLink(head); printf("双链表长度:%d\n",Length(head)); doubleLinkNode *node = GetNode(head,1); Printpre(node); Printnext(node); return 0; }