双向链表、循环链表以及循环双向链表的整理

双向链表

引言

页面的前进与回退

在这里插入图片描述

已知某指针q的位置,求指针p。
在这里插入图片描述

由于单链表节点中只有一个指向其直接后继结点的指针域,因此若已知某结点的指针域为p,找其前驱结点只能从该链表的头结点开始,顺链查找,直到找到某个结点q的直接后继为p为止。q所指的结点即为p的直接前驱,其时间复杂度为O(n)。

所以要想让其时间复杂度达到O(1),可以选择增加一个指向直接前驱的指针域。
在这里插入图片描述

双向链表的基本操作

//定义结构体变量
typedef struct node{
	int data;			//data域存放数据
	struct node *prev;	//prev指针域存放上一个结点的地址
	struct node *next;	//next指针域存放下一个结点的地址
}LNode,*Linklist;

//创建头结点
Linklist CreatList(); 

//双向链表判空操作
int isEmpty(Linklist head); 

//链表的创建 [头插法]
void ListByInsertHead(Linklist head );

//链表的创建[尾插法]
void ListByInsertTail(Linklist head );
 
//将p结点插入q结点之前 
void Insert(LNode *p, LNode *q);

//删除给定结点
void delete_Node(LNode *p); 

//删除链表
void delete_List(LNode *p); 

链表创建头结点[初始化]

在这里插入图片描述

/**
*创建链表,返回头指针。
**/
Linklist CreatList(){
	Linklist head = (Linklist)malloc(sizeof(LNode));//创建头结点
	head->next=NULL;	//将next指针域置空
	head->prev=NULL;	//将prev指针域置空
	return head;		//返回头结点
}
/**
*双向链表判空操作
**/
int isEmpty(Linklist head){
	if(head->next==NULL && head->prev==NULL){
		//为空 
		return 1;
	}
	//不为空 
	return 0; 	
}

如何连接我相信大家画图都是知道的,但是有时候代码跑不起来的原因就是因为插入的次序没有。

链表头插法创建

在这里插入图片描述

断开两个连接

在这里插入图片描述

我们先看一下原图

在这里插入图片描述

断的次序(head->next后断):

在这里插入图片描述

特殊情况:

当只存在head结点时,head的next指针域为空。

在这里插入图片描述

已知Head结点和p结点,上面共四步:

Head->next=p;(1)
p->prev=Head;(2)
Head->next->prev=p;(3)
p->next=Head->next;(4)

3 4 1

/**
*头插法创建链表
**/
void ListByInsertHead(Linklist head ){
	
	LNode *p;	//指针p用来接受数据,进行插入
	int x;
	while(scanf("%d",&x)&&x!=-1){
		p=(Linklist)malloc(sizeof(LNode)) ;
		p->data=x;
		
        //头插法
        //只有头结点时,头结点的next域为NULL,不存在prev指针域,第一步要放在第三步之前,此时未更新head->next
		if(head->next!=NULL){	
			head->next->prev=p;
		}
		p->next=head->next;	//单链表头插操作
		head->next=p;		//单链表头插操作
		p->prev=head;		//将p的prev指针域指向head(这一步放哪都行)
        
	}
	return ;
}

链表尾插法创建

在这里插入图片描述

在这里插入图片描述

此时有四步改动

p->next=s;(1)
p=s;(2)
s->prev=p;(3)
s->next=p->next;(4)
//还有一种写法是s->next=NULL;

3 2

/**
*尾插法创建链表
**/
void ListByInsertTail(Linklist head ){

	LNode *p,*s;	//指针p用来保存尾指针位置,指针s用来存数据进行插入
	p=head;//只有头结点时,头结点即为最后一个结点
	int x;
	while(scanf("%d",&x)&&x!=-1){
		s=(Linklist)malloc(sizeof(LNode)) ;
		s->data=x;
    
        //尾插法
        s->next=p->next;//s的结点指向尾结点p的下一个,即NULL
		p->next=s;//将尾结点的next指针指向要插入的结点
        //第三步和第四步不能交换
		s->prev=p;//要插入的结点的前一个结点更新为尾结点
		p=s;//将尾结点更新为新插入的结点
        
       

    }
	
	return ;	
}
/**
*尾插法创建链表第二种写法
**/
void ListByInsertTail(Linklist head ){

	LNode *p,*s;	//指针p用来保存尾指针位置,指针s用来存数据进行插入
	p=head;//只有头结点时,头结点即为最后一个结点
	int x;
	while(scanf("%d",&x)&&x!=-1){
		s=(Linklist)malloc(sizeof(LNode)) ;
		s->data=x;
    
        //尾插法
		p->next=s;//将尾结点的next指针指向要插入的结点
        //第二步和第三步不能交换
		s->prev=p;//要插入的结点的前一个结点更新为尾结点
		p=s;//将尾结点更新为新插入的结点
    }
	s->next=NULL;//尾结点指向为空
    
	return ;	
}

指定结点前插入

在q结点前插入p结点

在这里插入图片描述

在这里插入图片描述

此处共有四处改动

p->prev=q->prev;(1)
q->prev->next=p;(2)
p->next=q;(3)
q->prev=p;(4)
/**
*指定结点前插入(在q结点前插入p结点)
**/

void Insert(LNode *p, LNode *q){
    //只有等q指针前面那个结点的相关操作处理完之后才能修改q->prev
	p->prev=q->prev;
	q->prev->next=p;
    
	p->next=q;
	q->prev=p;
}

链表删除

在这里插入图片描述

在这里插入图片描述

特殊:p为尾指针在这里插入图片描述

上述有三处改动

p->prev->next=p->next;(1)
p->next=p->prev(2)
free(p)(3)
/**
*删除指定结点
**/

void delete_Node(LNode *p){
	p->prev->next=p->next;
	if(p->next!=NULL){
		p->next->prev=p->prev;
	}                                                    
	free(p);
}

双向链表完整代码

#include<stdio.h>
#include<stdlib.h>
#include<windows.h>
typedef struct node{
	int data;
	struct node *prev;
	struct node *next;
}LNode,*Linklist;

//创建链表
Linklist CreatList(); 

//双向链表判空操作
int isEmpty(Linklist head); 

//链表的创建 [头插法]
void ListByInsertHead(Linklist head );

//链表的创建[尾插法]
void ListByInsertTail(Linklist head );
 

//将p结点插入q结点之前 
void Insert(LNode *p, LNode *q);

//删除p结点
void delete_Node(LNode *p); 

//删除链表
void delete_List(LNode *p); 

//打印链表
void print_List(Linklist head);


int main(){
	
	Linklist head = CreatList();
	printf("%d\n",isEmpty(head)); 
	ListByInsertTail(head);
	print_List(head);
	printf("\n");
	Linklist head1 = CreatList();
	ListByInsertHead(head1);
	print_List(head1);
//	LNode *t = (Linklist)malloc(sizeof(LNode));
//	t->data=10;
//	Insert(t,head->next);
//	print_List(head);
//	printf("\n");
//	delete_Node(t);
//	print_List(head);
//	delete_List(head);
	return 0;
	
} 

Linklist CreatList(){
	Linklist head = (Linklist)malloc(sizeof(LNode));
	head->next=NULL;
	head->prev=NULL;
	return head;
}

int isEmpty(Linklist head){
	if(head->next==NULL && head->prev==NULL){
		//为空 
		return 1;
	}
	//不为空 
	return 0; 	
}

void ListByInsertHead(Linklist head ){
	
	LNode *p;
	int x;
	while(scanf("%d",&x)&&x!=-1){
		p=(Linklist)malloc(sizeof(LNode)) ;
		p->data=x;
		
		if(head->next!=NULL){
			head->next->prev=p;
		}
		p->next=head->next;
		head->next=p;
		p->prev=head;
	}
	return ;
}

void ListByInsertTail(Linklist head ){

	LNode *p,*s;
	p=head;
	int x;
	while(scanf("%d",&x)&&x!=-1){
		s=(Linklist)malloc(sizeof(LNode)) ;
		s->data=x;
		s->next=p->next; 
		p->next=s;
		s->prev=p;
		p=s;
	}
	//s->next=NULL;
	return ;
	
	
}
				
void Insert(LNode *p, LNode *q){
	p->prev=q->prev;
	q->prev->next=p;
	p->next=q;
	q->prev=p;
}


void delete_Node(LNode *p){
	p->prev->next=p->next;
	if(p->next!=NULL){
		p->next->prev=p->prev;
	}                                                    
	free(p);
}

void delete_List(LNode *p){
	LNode *t,*temp;
	t=p->next;
	while(t){
		temp=t->next;
		delete_Node(t);
		t=temp;
		print_List(p);
	}
	free(p);
}
void print_List(Linklist head){
	Linklist p;
	p=head;
	while(p->next){
		p=p->next;
		printf("%d ",p->data);	
	} 
	
	system("pause"); 
	printf("\n");
	while(p->prev){	
		printf("%d ",p->data);
		p=p->prev;
	}
}
 

循环双向链表的实现

小思考:自己实现循环双向链表

循环双向链表和双向链表的操作基本一致, 注意由于head指针的prev域和尾指针的next域不为空,因此部分操作需要增加对这两个指针的连接

双向循环链表完整代码:

#include<stdio.h>
#include<stdlib.h>
#include<windows.h>
typedef struct node{
	int data;
	struct node *prev;
	struct node *next;
}LNode,*Linklist;

//创建链表
Linklist CreatList(); 

//双向循环链表判空操作
int isEmpty(Linklist head); 

//链表的创建 [头插法]
void ListByInsertHead(Linklist head );

//链表的创建[尾插法]
void ListByInsertTail(Linklist head );
 

//将p结点插入q结点之前 
void Insert(LNode *p, LNode *q);

//删除p结点
void delete_Node(LNode *p); 

//删除链表
void delete_List(LNode *p); 

//打印链表
void print_List(Linklist head);


int main(){
	
		Linklist head = CreatList();
		printf("%d\n",isEmpty(head));
	ListByInsertTail(head);
	print_List(head);
	printf("\n");
	//Linklist head1 = CreatList();
	//ListByInsertHead(head1);
	//print_List(head1);
	LNode *t = (Linklist)malloc(sizeof(LNode));
	t->data=10;
	Insert(t,head->next);
	print_List(head);
	printf("\n");
	delete_Node(t);
	print_List(head);
	delete_List(head);
	return 0;
	
} 

Linklist CreatList(){
	Linklist head = (Linklist)malloc(sizeof(LNode));
	head->next=head;
	head->prev=head;
	return head;
}

int isEmpty(Linklist head){
	if(head->next==head && head->prev==head){
		//为空 
		return 1;
	}
	//不为空 
	return 0; 	
}

void ListByInsertHead(Linklist head ){
	
	LNode *p;
	int x;
	while(scanf("%d",&x)&&x!=-1){
		p=(Linklist)malloc(sizeof(LNode)) ;
		p->data=x;
		
		head->next->prev=p;
		p->next=head->next;
		head->next=p;
		p->prev=head;
	}
	return ;
}

void ListByInsertTail(Linklist head ){

	LNode *p,*s;
	p=head;
	int x;
	while(scanf("%d",&x)&&x!=-1){
		s=(Linklist)malloc(sizeof(LNode)) ;
		s->data=x;
		s->next=p->next;
		//增加代码:尾指针所指的下一个结点即头指针的prev修改为要插入的结点,即将头指针与新要插入的结点连接起来
		p->next->prev=s; 
		p->next=s;
		s->prev=p;
		p=s;
	}
	return ;
	
	
}
				
void Insert(LNode *p, LNode *q){
	p->prev=q->prev;
	q->prev->next=p;
	p->next=q;
	q->prev=p;
}


void delete_Node(LNode *p){
	p->prev->next=p->next;
	//if(p->next!=NULL){ //p->next不会为空,可删去 
	p->next->prev=p->prev;
//	}                                                    
	free(p);
}

void delete_List(LNode *p){
	LNode *t,*temp;
	t=p->next;
	while(t!=p&&t!=p){//判断条件改为当t又绕回一圈时即链表只剩一个结点 
		temp=t->next;
		delete_Node(t);
		t=temp;
		print_List(p);
		printf("\n");
	}
	free(p);
}
void print_List(Linklist head){
	Linklist p;
	p=head->next;
	while(1){
	
		printf("%d ",p->data);	
		if(p->next==head){
			break;
		}	
		p=p->next;
	} 
	
	system("pause"); 
	printf("\n");
	while(1){	
		printf("%d ",p->data);
		
		if(p->prev==head){
			break;
		}
		p=p->prev;
	}
}
 

模拟链表

引言

前面介绍的链表都是由指针实现的,链表中结点的分配和回收都是动态的,称为"动态链表“。与之对应的,还有一种"静态链表",又称模拟链表,静态链表由数组实现,每个数据元素除了存储数据信息外,还要存储逻辑相邻的下一个数据元素在数组中的位置。可见,静态链表虽然是用数组实现的,但是逻辑相邻的数据元素不一定在物理上是相邻的。

下表就是一个模拟链表,他的存储顺序是:a1->a2->a3->a4->a5->……,但是他的物理顺序是a4->a2-a3->a1->a5->……

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aCqcRdob-1615363269774)(.\双向链表.assets\image-20201208221730863.png)]

模拟链表通过两个数组实现链式存储,data数组用来存储当前结点的数据,next数组用来存储下一个结点的位置。(是不是感觉和链表很像),通过相同点的数组下标,标识每一个元素存储的数据及下一个结点的位置信息,一样的,第一个结点并没有存放任何元素。

模拟链表的基本操作

//定义next=-1为空 
typedef struct{
	int data;
	int next;
}imitate;

//定义静态链表 
imitate im[Maxsize];

//链表表头和新要插入的位置 
int head,pos;

//初始化链表
void init_list(); 

//链表判空操作
int isEmpty() ;

//链表增加结点
void add_toList(); 

//指定位置之后插入结点
int add_postoList(int point,int x); 

//删除指定位置元素
int delete_pos_list(int point); 

模拟链表的初始化

在这里插入图片描述

/**
*链表初始化
**/
void init_list(){
	head=0;//头结点放在0位置
	pos=1; 
	im[0].next=-1; 
}

链表判空

int isEmpty(){
	if(im[head].next==-1){
		return 1;
	}
	return 0;
}

链表增加节点

在这里插入图片描述

在这里插入图片描述

/**
*链表尾部增加结点
**/
void add_toList(int x){
    //从头开始遍历,找到尾结点
	int i=head;
	
	while(im[i].next!=-1){
		i=im[i].next;
	}
	
    im[pos].data=x;//读入新要插入的元素
    
	im[i].next = pos;//将尾结点的next指针指向新要插入的元素
	im[pos].next = -1;//将新插入的元素的尾指针置空
	++pos;
}

指定位置后增加结点

在这里插入图片描述

在这里插入图片描述

/**
*链表指定位置后增加结点 成功返回1 失败返回0
*point变量表示链表中第几个数据
**/
int add_postoList(int point,int x){
	
	for(int i= im[head].next;i!=-1;i=im[i].next){
		if((--point)==0){//找到那个要增加结点的位置
			im[pos].data=x;//读入值yuanxie
			im[pos].next=im[i].next;//新插入的值的下一个结点为第point个位置的结点的下一个结点
			im[i].next=pos;//第point个结点的下一个结点更新为新插入的结点
			++pos;//插入点后移
			return 1;
		}
	}
	return 0; 
}

删除指定位置的结点

在这里插入图片描述

在这里插入图片描述

/**
*链表指定位置删除结点 成功返回1 失败返回0
*point变量表示链表中第几个数据
**/
int delete_pos_list(int point){
	point=point-1;//找要删除的结点的前一个结点
	for(int i= im[head].next;i!=-1;i=im[i].next){
		if((--point)==0){		//循环找到那个结点
			im[i].next=im[im[i].next].next; //该结点等于要删除的结点的下一个结点。
			return 1;
		}
	}
	return 0;
}
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值