碎碎念:之前学了单链表,现在尝试了双链表,本以为不会很简单,但是一旦懂了单链表后,双链表感觉也还行。双链表虽然比单链表多了一条链,但是操作的本质不变。
完整记录一下我的双链表,防止哪天不记得了还能回来看。
单链表 | 双链表 | |
区别 | 只有后继,无前驱 | 有前驱,有后继 |
优点: | 增加删除节点方便简单,遍历时候不会死循环。 | 可以双向循环和遍历,可以任意找到一个结点的前驱和后继。 |
缺点: | 只能从头到尾单向读取和遍历。只能找到后继,无法找到前驱。 | 增加删除节点复杂,需要额外多分配内存空间。 |
结构体的创建:
typedef struct node{
int data;
struct node* front; //双链表的前指针
struct node* back; //双链表的后指针
}node,*Listnode; //node时结构体的别名,Listnode是指向结构体的指针
(关于node和*Listnode的解释,可以参考一下下面这一篇,讲的很清晰易懂:(19条消息) 链表结构体定义LinkNode,*LinkList以及typedef_今天天气眞好的博客-CSDN博客_链表的结构体定义)
双链表的初始化:
void InitHeadNode(Listnode &L){
L=(node*)malloc(sizeof(node)); //创建一个新结点(头结点)
L->data=0; //由于我们先创了头指针,并且给头结点的data赋了值,所以此时L->data=0可作为只有一个空结点的标志;
L->front=NULL;
L->back=NULL;
}
头插法:
void HeadInsert(Listnode &L,int data){
Listnode node=(Listnode)malloc(sizeof(node));
node->data=data;
if(L->data==0){
node->back=L->back;
node->front=L;
L->back=node;
}
else{
L->back->front=node; //(1)
node->front=L; //(2)
node->back=L->back; //(3)
L->back=node; //(4)
}
L->data++; //表示头结点后的结点数+1
}
这里呢,头结点后没有其他结点 和 头结点后有其他结点 的操作不相同,所以要分情况,if是当头节点后没有其他元素时,此时头结点的前指针和后指针都为空,所以将L->back等于node->back,表明此时第一个插入的结点(新结点)的后指针为空(后无结点),而将新结点node的前指针指向头结点L(node->front=L),再将头结点L的后指针直接指向新结点(L->back=node)即可。
附图(if语句段):
而当头结点后还有元素时,就稍有些复杂,但还是很好理解(else语句段):
我这里时按照①②③④的顺序解链、接链的(代码按照②①③④的顺序写也可以完成相应操作)。这里要插入的结点是node。要注意的是,我这里①的过程是先断开▲下方的链,③的时候断开▲上方的链子,不可以在①过程就先断噢,不然我们后续就没法接到node1了。
尾插法:
void TailInsert(Listnode &L,int data){
Listnode node=(Listnode)malloc(sizeof(node));
node->data=data;
Listnode sign=L;
sign=L->back;
while(sign->back){
sign=sign->back;
}
node->back=sign->back;
node->front=sign;
sign->back=node;
L->data++; //表示头结点后的结点数+1
}
尾插法和当只有头结点时的结点插入很像,都是直接插入、解链接链,但是多了一步循环找到最尾元素的过程。
新结点插入:
void InsertData(Listnode &L,int i,int data){ //在第i个位置插入结点
if(i>L->data) {
printf("overflow\n");
return;
}
Listnode node=(Listnode)malloc(sizeof(node));
node->data=data;
Listnode sign=L;
sign=L->back;
int n=1;
while(sign){ //找到第i个元素的上一个结点(i-1),方便找目标位置的前后结点
sign=sign->back;
n++;
if(n==i-1) break; //i=i-1退出循环时,sign的下一个结点就是第i个结点
}
sign->back->front=node; //(和上面的1、2、3、4步骤类似)
node->front=sign;
node->back=sign->back;
sign->back=node;
L->data++; //新结点插入后,总结点个数+1
}
删除结点:
void DeleteData(Listnode &L,int i){ //在第i个位置删除结点
if(i>L->data) {
printf("overflow\n");
return;
}
Listnode node;
Listnode sign=L;
sign=L->back;
int n=1;
while(sign){
sign=sign->back;
n++;
if(n==i-1) break; //当n=i-1退出时,sign的下一个元结点就是要删除的目标结点node
}
node=sign->back; //通过sign将node找出来,方便后续释放
sign->back=node->back; //sign的后指针直接指向要删去的node结点的后一个结点
node->back->front=sign; //要删去的node结点的后结点的前指针直接指向sign结点
free(node); //删除node结点,释放内存
L->data--; //链表结点个数减1
}
我的代码:
//双链表
#include<stdio.h>
#include<malloc.h>
#define maxNum 100
typedef struct node{
int data;
struct node* front;
struct node* back;
}node,*Listnode;
void InitHeadNode(Listnode &L){
L=(node*)malloc(sizeof(node));
L->data=0;
L->front=NULL;
L->back=NULL;
}
void HeadInsert(Listnode &L,int data){
Listnode node=(Listnode)malloc(sizeof(node));
node->data=data;
if(L->data==0){
node->back=L->back;
node->front=L;
L->back=node;
}
else{
L->back->front=node;
node->front=L;
node->back=L->back;
L->back=node;
}
L->data++; 表示头结点后还有其他结点
}
void TailInsert(Listnode &L,int data){
Listnode node=(Listnode)malloc(sizeof(node));
node->data=data;
Listnode sign=L;
sign=L->back;
while(sign->back){
sign=sign->back;
}
node->back=sign->back;
node->front=sign;
sign->back=node;
L->data++; //表示头结点后还有其他结点
}
void InsertData(Listnode &L,int i,int data){ //在第i个位置插入结点
if(i>L->data) {
printf("overflow\n");
return;
}
Listnode node=(Listnode)malloc(sizeof(node));
node->data=data;
Listnode sign=L;
sign=L->back;
int n=1;
while(sign){
sign=sign->back;
n++;
if(n==i-1) break;
}
sign->back->front=node;
node->front=sign;
node->back=sign->back;
sign->back=node;
L->data++;
}
void DeleteData(Listnode &L,int i){ //在第i个位置删除结点
if(i>L->data) {
printf("overflow\n");
return;
}
Listnode node;
Listnode sign=L;
sign=L->back;
int n=1;
while(sign){
sign=sign->back;
n++;
if(n==i-1) break;
}
node=sign->back;
sign->back=node->back;
node->back->front=sign;
free(node);
L->data--;
}
void print(Listnode &L){
Listnode sign;
sign=L->back;
while(sign){
printf("%d -> ",sign->data);
sign=sign->back;
}
printf("NULL \n");
}
int main(){
Listnode L;
InitHeadNode(L);
printf("头插法:");
HeadInsert(L,1);
HeadInsert(L,2);
HeadInsert(L,3);
HeadInsert(L,4);
HeadInsert(L,5);
print(L);
printf("尾插法:");
TailInsert(L,0);
TailInsert(L,-1);
print(L);
printf("该链表总共有:%d 个结点\n",L->data);
printf("----------------------------\n");
printf("在第3个位置插入元素100:\n");
InsertData(L,3,100);
print(L);
printf("该链表总共有:%d 个结点\n",L->data);
printf("----------------------------\n");
printf("在第3个位置删除元素100:\n");
DeleteData(L,3);
print(L);
printf("该链表总共有:%d 个结点\n",L->data);
return 0;
}
运行结果截图: