双向链表
双向链表中,每个结点都有两个指针域,一个指向其后继结点,另一个指针指向其前驱结点,如图1.1(a)所示,因此,可以从某个结点开始朝两个方向遍历整个链表。
如果循环链表中每个结点都采用双向指针就构成了双向循环链表。图1.1(c)所示就是一个带头结点的双向循环链表,其尾结点的后继指针指向表的头结点,而表头结点的前驱指针指向表的尾结点。空的双向循环链表的结构如图1.1(b)所示。
双向链表中结点的类型定义如下:
typedef int datatype;
typedef struct node *ptNode;
struct note{
datatypedata;
ptNodeproir,next;
};
双链表是一种对称的结构,既有向前链又有向后链,这就使得双链表的前插和后插及自身删除操作都很方便,只需要修改几个结点的指针域,而不必进行大量的数据交换或遍历操作。下面给出双向链表的插入和删除运算的算法。
1 双向链表的前插运算
在双链表中某个给定的结点P之前插入一个新结点S,其操作步骤如下:
1)。将结点S的后继指向P本身;
2)。将结点S的前驱指向结点P的前驱所指向的结点;
3)。将结点P的前驱结点的后继修改为指向新结点S;
4)。将结点P的前驱修改为指向结点S本身;
插入新结点的过程如图1.2 (a)图所示。
双链表中在结点P之前插入一个新结点S的算法如下:
ptNode Insert_before(ptNode head,ptNode p, intx)
{
ptNode s;
s=(ptNode)malloc(sizeof(node));//建立新结点
s->data=x;
//将x的值赋给s->data
s->next=p;
//1)将新结点S的后继指向结点P
s->prior=p->prior;
//2)将新结点S的前驱指向原结点P的前驱
p->prior->next=s;
//3)原结点P前驱结点的后继指向新结点S
p->prior=s;
//4)原结点P的前驱指向新结点S
return(head);
//返回带头结点双链表的头指针
}
2 双向链表的后插运算
假设双链表中结点P的后继结点为Q,在结点P之后插入一个新结点S,其操作步骤如下:
1)。将结点S的前驱指向P本身;
2)。将结点S的后继指向结点P所指向的后继结点;
3)。将结点P的后继结点Q的前驱结点修改为新结点S本身;
4)。将结点P的后继结点修改为结点S本身;
插入新结点的过程如图1.2 (b)图所示。
双链表中在结点P之后插入一个新结点S的算法如下:
ptNode Insert_after(ptNode head,ptNode p, intx)
{
ptNode s;
s=(ptNode)malloc(sizeof(node));//建立新结点
s->data=x;
//将x的值赋给s->data
s->next=p->next;
//1)将新结点S的后继指向结点P的后继
s->prior=p;
//2)将新结点S的前驱指向原结点P
p->next->prior=s;
//3)原结点P的后继结点的前驱指向新结点S
p->next=s;
//4)原结点P的后继指向新结点S
return(head);
//返回带头结点双链表的头指针
}
3 双向链表的自身删除运算
假设双链表中某给定结点P的前驱结点为Q,其后继结点为S,则双链表中删除结点P的步骤如下:
1)。将结点P的前驱结点Q的后继结点指向结点P的后继结点S;
2)。将结点P的后继结点S的前驱结点指向结点Q;
插入新结点的过程如图1.2 (c)图所示。
在双链表中删除某个结点P自身的算法如下:
ptNode delete_node(ptNode head,ptNode p)
{
if(p!=NULL)
{
p->prior->next=p->next;
p->next->prior=p->prior;
free(p);
return(head);
}
else
{
return(NULL);
}
}
由此可见,自身删除前后被删除的指针并没有改变,只是它后一个结点的向前指针和前一个结点的向后指针发生了变化,不再指向此结点。
这三种运算过程中,指针的变化情况分别如1.2 (a)、(b)、(c)图所示。
完整的双向循环链表的操作:查询、删除、显示、插入 C程序在VC上验证通过:
#include "stdafx.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <conio.h>
#define N 10
typedef struct node *ptNode;
structnode{
charname[20];
ptNodeprior,next;
};
ptNode Creat(int n)
{
ptNodehead,ptr,newnode;
inti;
if((head=(ptNode)malloc(sizeof(node))) ==NULL)
{
printf("分配内存失败!\n");
exit(0);
}
strcpy(head->name,"head");
//head->name[0] ='\0';//头结点初始化
head->prior =NULL;
head->next =NULL;
ptr =head;
for(i=0;i<n;i++)
{
if((newnode=(ptNode) malloc(sizeof(node))) ==NULL)
{
printf("cannot findspace!\n");
exit(0);
}
ptr->next=newnode;//连接新结点
printf("Please input the %d man's name:",i+1);
scanf("%s",newnode->name);
newnode->prior =ptr;//定义newnode的连接关系
newnode->next =NULL;
ptr =newnode;
}//endfor
head->prior =newnode;
ptr->next =head;
return(head);
}
ptNode Search(ptNode head,char *pEle)
{
ptNodeptr;
char*pTemp;
ptr =head->next;
while(ptr !=head)//以是否回到头结点为结束条件
{
pTemp =ptr->name;//pTemp暂存结点的名字地址
if(strcmp(pTemp,pEle) ==0)
{
return(ptr);
}
else
{
ptr =ptr->next;//指向下一结点
}
}
printf("查找不到数据!\n");
returnNULL;
}
//在选定名字前插入
void Insert_before(ptNode head,char *priorEle,char*insertEle)
{
ptNodeptr,newnode;
if((ptr=Search(head,priorEle))==NULL)
{
printf("Can't find the data toinsert!\n");
exit(0);
}
if((newnode=(ptNode)malloc(sizeof(node))) ==NULL)
{
printf("分配内存失败!\n");
exit(0);
}
strcpy(newnode->name,insertEle);//给新结点加载值
newnode->prior =ptr->prior;//前指针指向前结点,先处理以新结点为中心前后关系
newnode->next =ptr;//新结点后指针指向定位结点
(ptr->prior)->next =newnode;//前结点的由指针指向新结点
ptr->prior =newnode;
}
//在选定名字后插入
void Insert_after(ptNode head,char *p,char *insertEle)
{
ptNodeptr,newnode;
if((ptr=Search(head,p))==NULL)
{
printf("Can't find the data toinsert!\n");
exit(0);
}
if((newnode=(ptNode)malloc(sizeof(node))) ==NULL)
{
printf("分配内存失败!\n");
exit(0);
}
strcpy(newnode->name,insertEle);//给新结点加载值
newnode->prior=ptr;
newnode->next=ptr->next;
ptr->next->prior=newnode;
ptr->next=newnode;
}
void Print(ptNode head)
{
ptNodeptr;
ptr =head->next;
//printf("\n正向循环双链表为:\n");
while(ptr !=head)
//while(ptr!= NULL)
{
printf("%s",ptr->name);
ptr =ptr->next;
}
printf("\n");
}
void opposite(ptNode head)//反向输出
{
ptNodeptr;
ptr=head->next;
while(ptr->next!=head)
ptr=ptr->next;
//printf("\n反向循环双链表为:\n");
while(ptr!=head)
{
printf("%s",ptr->name);
ptr=ptr->prior;
}
printf("\n");
}
void Delete(ptNode ptr)
{
(ptr->next)->prior =ptr->prior;//(ptr->next)->prior下一结点的左指针被赋前指针
(ptr->prior)->next =ptr->next;
free(ptr);
}
void main(void)
{
intnumber;
charstudName[20];
charinsertName[20];
ptNodehead,searchpoint;
intflag=1;
charj;
//number =N;
puts("输入双链表的最大结点数:");
scanf("%d",&number);
head =Creat(number);
Print(head);
opposite(head);
while(flag)
{printf("请选择:\n");
printf("1.显示所有元素\n");
printf("2.删除一个元素\n");
printf("3.元素前插入一个元素\n");
printf("4.元素后插入一个元素\n");
printf("5.退出程序
\n");
scanf(" %c",&j);
switch(j)
{
case'1':
printf("\n正向循环双链表为:\n");
Print(head);
printf("\n反向循环双链表为:\n");
opposite(head);
break;
case'2':
printf("\n输入要删除的数据:\n");
scanf("%s",studName);
searchpoint= Search(head,studName);
Delete(searchpoint);
printf("\n删除后的链表为:\n");
Print(head);
break;
case'3':
printf("在双链表中指定一个元素的名字和要插入的数据(如aabb):\n");
//scanf("%s",studName);
//printf("输入想要插入的数据:\n");
scanf("%s%s",studName,insertName);
Insert_before(head,studName,insertName);
Print(head);
break;
case'4':
printf("在双链表中指定一个元素的名字和要插入的数据(如aabb):\n");
//scanf("%s",studName);
//printf("输入想要插入的数据:\n");
scanf("%s%s",studName,insertName);
Insert_after(head,studName,insertName);
Print(head);
break;
default:
flag=0;
printf("程序结束,按任意键退出!\n");
}
}
getch();
}
双向链表中,每个结点都有两个指针域,一个指向其后继结点,另一个指针指向其前驱结点,如图1.1(a)所示,因此,可以从某个结点开始朝两个方向遍历整个链表。
如果循环链表中每个结点都采用双向指针就构成了双向循环链表。图1.1(c)所示就是一个带头结点的双向循环链表,其尾结点的后继指针指向表的头结点,而表头结点的前驱指针指向表的尾结点。空的双向循环链表的结构如图1.1(b)所示。
![](https://img-my.csdn.net/uploads/201207/13/1342142332_7497.jpg)
双向链表中结点的类型定义如下:
typedef int datatype;
typedef struct node *ptNode;
struct note{
};
双链表是一种对称的结构,既有向前链又有向后链,这就使得双链表的前插和后插及自身删除操作都很方便,只需要修改几个结点的指针域,而不必进行大量的数据交换或遍历操作。下面给出双向链表的插入和删除运算的算法。
1 双向链表的前插运算
在双链表中某个给定的结点P之前插入一个新结点S,其操作步骤如下:
1)。将结点S的后继指向P本身;
2)。将结点S的前驱指向结点P的前驱所指向的结点;
3)。将结点P的前驱结点的后继修改为指向新结点S;
4)。将结点P的前驱修改为指向结点S本身;
插入新结点的过程如图1.2 (a)图所示。
双链表中在结点P之前插入一个新结点S的算法如下:
ptNode Insert_before(ptNode head,ptNode
{
}
2 双向链表的后插运算
假设双链表中结点P的后继结点为Q,在结点P之后插入一个新结点S,其操作步骤如下:
1)。将结点S的前驱指向P本身;
2)。将结点S的后继指向结点P所指向的后继结点;
3)。将结点P的后继结点Q的前驱结点修改为新结点S本身;
4)。将结点P的后继结点修改为结点S本身;
插入新结点的过程如图1.2 (b)图所示。
双链表中在结点P之后插入一个新结点S的算法如下:
ptNode Insert_after(ptNode head,ptNode
{
}
3 双向链表的自身删除运算
假设双链表中某给定结点P的前驱结点为Q,其后继结点为S,则双链表中删除结点P的步骤如下:
1)。将结点P的前驱结点Q的后继结点指向结点P的后继结点S;
2)。将结点P的后继结点S的前驱结点指向结点Q;
插入新结点的过程如图1.2 (c)图所示。
在双链表中删除某个结点P自身的算法如下:
ptNode delete_node(ptNode head,ptNode p)
{
}
由此可见,自身删除前后被删除的指针并没有改变,只是它后一个结点的向前指针和前一个结点的向后指针发生了变化,不再指向此结点。
这三种运算过程中,指针的变化情况分别如1.2 (a)、(b)、(c)图所示。
![](https://img-my.csdn.net/uploads/201207/13/1342142337_5860.jpg)
![](https://img-my.csdn.net/uploads/201207/13/1342142342_4612.jpg)
![](https://img-my.csdn.net/uploads/201207/13/1342142347_7245.jpg)
图1.2双向循环链表的前插、后插和自身删除操作示意图
完整的双向循环链表的操作:查询、删除、显示、插入 C程序在VC上验证通过:
#include "stdafx.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <conio.h>
#define N 10
typedef struct node *ptNode;
structnode{
};
ptNode Creat(int n)
{
}
ptNode Search(ptNode head,char *pEle)
{
}
//在选定名字前插入
void Insert_before(ptNode head,char *priorEle,char*insertEle)
{
}
//在选定名字后插入
void Insert_after(ptNode head,char *p,char *insertEle)
{
}
void Print(ptNode head)
{
}
void opposite(ptNode head)//反向输出
{
}
void Delete(ptNode ptr)
{
}
void main(void)
{
}