循环双链表(C语言)

今天也要努力学习,争取考上杭电。

链表的知识

链表即线性表的链式存储。
通过一组任意的存储单元来存储线性表中的数据元素。为了建立数据间的线性关系,对每个链表结点,除了存放数据元素的信息外,还需要存放一个指向其后继的指针(双链表则还需要一个指向前驱的指针)。
链表解决了顺序表需要大量连续存储单元的缺点,但链表要附加指针域,也浪费了存储空间。存储密度不如顺序表大。
相比顺序表的随机访问链表则是非随机存取,需要从头挨个遍历,复杂度为O(n),但删除增加结点方便,每个结点的插入时间复杂度为O(1),但查找到操作位置需要的时间开销为O(n)。
单链表在找到某一结点时,访问后继结点复杂度为O(1),但访问前驱结点为O(n),所以为克服这个缺点引入了双链表,即多了一个前驱指针。
其中循环双链表,即头结点的前驱为表的最后一个结点,最后一个结点的后继为头结点。
本篇是用C语言实现的带头结点的循环双链表。

C代码实现

结构体定义:

//双链表结点结构定义 
typedef struct DNode{
	struct DNode *prior;			//前驱 
	ElemType data;					//数据 
	struct DNode *next;				//后继 
}DNode,*DNP;

相关函数代码:`
头文件、用到的宏定义:

#include<stdio.h>
#include<malloc.h>
#include<string.h>

//_bool类型 
#define _bool int
#define true 1
#define false 0

//循环双链表元素数据类型 ElemType
#define ElemType int

初始化头结点、头插、尾插、按位插:
注:很多书上是头(尾)插法建立链表,就是说初始化表头和一开始建立链表插入数据放在一个功能实现了,但是我把它分开写的,把初始化单独写了一个函数,这样后面可以随时头插尾插。

//循环双链表初始化头结点
_bool IniHeadNode(DNP h){
	h->prior=h;						//初始前驱是尾结点,即头结点 
	h->next=h;						//初始后继为头结点 
	h->data=0;						//头结点data用来存放长度  
	return true;
}  

//头插入链表    inilen:要插入数据的数量 
_bool HeadInsert(DNP h,int inilen){
	ElemType data;
	DNP p=h;
	while(inilen--){
		DNP n=NULL;
		//void* malloc(unsigned int size);
		n=(DNP)malloc(sizeof(DNode));
		if(n==NULL){ 
			printf("Insufficient space\n");
			return false; 
		}
		scanf("%d",&data);
		n->data=data;
		n->next=p->next;
		p->next->prior=n;
		p->next=n;
		n->prior=p;
		h->data++;	
	}
	return true;
} 

//尾插入链表    inilen:要插入数据的数量  
_bool TailInsert(DNP h,int inilen){
	ElemType data;
	DNP p=h;
	while(inilen--){
		DNP n=NULL;
		//void* malloc(unsigned int size);
		n=(DNP)malloc(sizeof(DNode));
		if(n==NULL){
			printf("Insufficient space\n");
			return false; 
		}
		scanf("%d",&data);
		n->data=data;
		n->next=p;
		p->prior->next=n;
		n->prior=p->prior;
		p->prior=n;
		h->data++;	
	}
	return true;	
} 

//某一位置后插入一个新结点
_bool Insert(DNP h,int destin){
	ElemType data;
	DNP n=NULL,p=h;
	n=(DNP)malloc(sizeof(DNode));
	if(n==NULL){ 
		printf("Insufficient space\n");
		return false; 
	}
	scanf("%d",&data);
	while(destin--)		p=p->next;
	p->next->prior=n;
	n->next=p->next;
	n->prior=p;
	p->next=n;
	n->data=data;
	return true; 
}

遍历:

//正向遍历双链表   返回表长度 
//反向同理,将后继改为前驱即可 
int TraversalList(DNP h){
	DNP p=h->next;
	if(p==h){
		printf("NULL\n");
		return false;
	}
	while(p!=h){					//循环链表,最后一个结点的后继为头结点,不循环则为NULL 
		printf("%5d",p->data);
		p=p->next;
	}printf("\n");
	return h->data;
}

删除、查询:
这里也偷懒了…因为感觉有很多好像都是一样的,像按值删除,我要先找出那个结点然后删除。所以直接写了查询和删除函数,后面另一个函数调用了。出现标记耦合,不太好,但是就这样吧…

//删除结点p
_bool DeleteNode(DNP p){
	if(p==NULL)	return false;
	p->next->prior=p->prior;
	p->prior->next=p->next;
	free(p);
	return true;
}

//按位查询结点      成功则返回查询到的结点地址 
DNP SelectByLocation(DNP h,int destin){
	if(destin>h->data){
		printf("Location(%d) not present!\n",destin);
		return NULL;
	}
	DNP p=h;
	int flag=destin;
	while(flag--){
		p=p->next; 
	}
	printf("Position %d is %d\n",destin,p->data); 
	return p;
}

//按值查询结点     成功则返回查询到的结点地址 
DNP SelectByValue(DNP h,ElemType value){
	DNP p=h->next;
	int i=1;
	while(p!=h){
		if(p->data==value){
			printf("%d in position %d\n",value,i);
			return p;
		}
		i++;
		p=p->next;
	}printf("Value(%d) not present!\n",value);
	return NULL;
}

//按位查询并删除
//偷懒所以分开写了,并且也没写按值删除
//注:出现了标记耦合 
_bool DeleteByLocation(DNP h,int destin){
	DeleteNode(SelectByLocation(h,destin));
//	DeleteNode(SelectByValue(h,value));
} 

今天的我是菜鸡吗?
是!
今天整理完数据结构书上的数据结构和算法了吗?
没有!

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
循环双向链表是一种链表结构,它可以在单链表的基础上,增加一个指向前一个结点的指针,从而实现双向遍历。循环双向链表是指尾结点的 next 指针指向头结点,头结点的 prev 指针指向尾结点,形成一个循环。 以下是循环双向链表的 C 语言实现: ```c #include <stdio.h> #include <stdlib.h> typedef struct Node { int data; struct Node *prev; struct Node *next; } Node; Node *create_node(int data) { Node *new_node = (Node *)malloc(sizeof(Node)); new_node->data = data; new_node->prev = NULL; new_node->next = NULL; return new_node; } void insert_node(Node **head, Node *new_node) { if (*head == NULL) { *head = new_node; (*head)->prev = new_node; (*head)->next = new_node; } else { Node *tail = (*head)->prev; tail->next = new_node; new_node->prev = tail; new_node->next = *head; (*head)->prev = new_node; } } void delete_node(Node **head, Node *del_node) { if (*head == NULL || del_node == NULL) { return; } if (*head == del_node) { *head = del_node->next; } if (del_node->next != NULL) { del_node->next->prev = del_node->prev; } if (del_node->prev != NULL) { del_node->prev->next = del_node->next; } free(del_node); } void print_list(Node *head) { if (head == NULL) { printf("List is empty\n"); } else { Node *current = head; do { printf("%d ", current->data); current = current->next; } while (current != head); printf("\n"); } } int main() { Node *head = NULL; Node *node1 = create_node(1); Node *node2 = create_node(2); Node *node3 = create_node(3); insert_node(&head, node1); insert_node(&head, node2); insert_node(&head, node3); print_list(head); delete_node(&head, node2); print_list(head); return 0; } ``` 在上面的代码中,我们定义了一个 Node 结构体,表示循环双向链表的结点。create_node 函数用于创建一个新结点,insert_node 函数用于将新结点插入链表,delete_node 函数用于删除指定结点,print_list 函数用于打印链表。 在 main 函数中,我们创建了三个结点,然后插入链表,打印链表,删除第二个结点,再次打印链表

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值