双链表的构建
单链表从某个结点出发只能顺指针向后寻查其他的结点。若要寻查结点的直接前驱,则必须从表头指针出发。位克服单链表的单向性的缺点,可利用双链表。
双链表的模型:
typedef struct Node{ //定义双链表节点类型
ElemType data; //数据域
struct Node *prior,*next; //前驱和指针域
}Node,*LinkList;
双链表也可以有循环表
初始化双链表(带头结点)
bool InitLinkList(LinkList &L){
L=(Node*)malloc(sizeof(NMode)); //分配头结点
if(L==NULL){ //内存不足,分配失败
return false;
}
L->prior=NULL; //头结点的p永远指向NULL
L->next=NULL; //头结点之后暂时没有节点
return true;
}
void testLinkList(){
//初始化双链表
LinkList L;
InitLinkList(L);
}
判断双链表是否为空(带头结点)
bool Empty(LinkList L){
if (L->next == NULL)
return true;
else
return false;
}
双链表的插入
在p结点之后插入s结点
bool InsertNextNode(Node *p,Node *s){
if (p==NULL || s==NULL )
return false;
s->next=p->next; //将结点*s插入到结点*p之后
if (p->nezt != NULL) //如果p结点后面有后继结点
p->next->prior=s;
s->prior=p;
p->next=s;
}
前插操作可以看作是前驱节点的后插
例如:在a2前面插入节点可以转换成a1的后插操作
双链表的删除
删除结点不是尾结点:
删除p的后继结点q
p->next=q->next;
q->next->prior=p;
free(p);
改进做法:判断删除节点是否为尾结点
//删除p结点的后继结点
bool DeleteNextNode(Node *p){
if (p == NULL)
return false;
Node *q = p->next; //找到p的后继结点q
if (q == NULL)
return false; //p没有后继
p->next=q->next;
if (q->next != NULL) //q结点不是最后一个结点
q->next->prior=p;
free(q); //释放结点空间
return true;
}
销毁一个双链表
void DestoryList(LinkList &L){
//循环释放各个数据结点
while (l->next != NULL)
DeleteNextNode(L);
free(L); //释放头节点
L=NULL; //头指针指向NULL
}
双链表的遍历
向后遍历
while(p!=NULL){
//对结点p做出相应的处理,如打印
p=p->next;
}
向前遍历
while(p!=NULL){
//对结点p做出相应的处理
p=p->prior;
}
双链表不可随机存取,按位查找、按值查找操作都只能用遍历的方式实现。
时间复杂度O(n)。