Day 62 单向循环链表 双链表

1.单向循环链表:

概念:

如果把单 链表的 最后一个节点的指针指向链表头部,而不是指向 NULL ,那么就构成了一个单向循环链表,通俗讲就是把尾节点的下一跳指向头结点使其形成一个闭环。
使用原因:
单向链表 中,头指针是相当重要的,因为单向链表的操作都需要头指针,所以如果头指针丢失或者破坏,那么整个链表都会遗失,并且浪费链表内存空间,因此我们引入了单向循环链表这种数据结构。
使用时要注意的问题:
1. 在链表中我们使用的是虚拟头结点,但是在循环链表中我们在遍历的时候就会遇到麻烦,因此在单向循环链表中我们使用的是真实头结点。
2. 循环单链表没有明显的结束条件,当我们不注意时,很容易陷入死循环。此时,我们可以设置一个标记点作为循环的标记,当我们知道表长,我们也可以设置一个计数器来结束循环。
单向循环链表:中间插法

设计一个单向循环链表的过程:

1. 定义链表结点:数据域 + 指针域
2. 定义链表结构体:头结点指针
3. 初始化链表
4. 指定位置插入新数据 ( 头插法,中间插法 )
5. 删除指定位置数据
6. 获取链表长度
7. 依据数据查找所在链表位置
8. 返回第一个结点
9. 打印链表结点数据
10. 释放链表
下面是对一个单向循环链表的增删改查:
#include<stdio.h>
#include<string.h>
#include<stdlib.h>

struct node{
	int data;
	struct node *next;
};

typedef struct node link_t;

link_t *link_init(void){
	link_t *p=(link_t *)malloc(sizeof(link_t));
	if(p==NULL){
		perror("malloc error");
		return NULL;
	}
	p->next=p;
	return p;
}

static void insert_behind(link_t *p,link_t *node){
	node->next=p->next;
	p->next=node;
}

void link_add_tail(link_t *p,int d){
	link_t *head=p;
	link_t *node=(link_t *)malloc(sizeof(link_t));
	if(node==NULL){
		return;
	}
	node->data=d;
	while(p->next!=head){
		p=p->next;
	}
	insert_behind(p,node);
}

void link_del(link_t *p,int d){
	link_t *head=p;
	link_t *delnode=NULL;
	while(p->next!=head){
		if(p->next->data==d){
			delnode=p->next;
			p->next=delnode->next;
			free(delnode);
			delnode=NULL;
			continue;
		}
		p=p->next;
	}
}

void link_update(link_t *p,int old,int new){
	link_t *head=p;
	link_t *node=NULL;
	link_t *temp=NULL;
	while(p->next!=head){
		if(p->next->data==old){
			p->next->data=new;
		}
		p=p->next;
	}
}

int link_find(link_t *p,int d){
	link_t *head=p;
	int count=0;
	while(p->next!=head){
		if(p->next->data==d){
			return count+1;
		}
		count++;
		p=p->next;
	}
	return -1;
}

void display(link_t *p){
	link_t *head=p;
	printf("遍历结果为:");
	while(p->next!=head){
		p=p->next;
		printf("%d->",p->data);
	}
	printf("\n");
}

int main(){
	int i;
	link_t *phead=link_init();
	for(i=0;i<6;i++){
		link_add_tail(phead,i);
	}
	display(phead);
	link_del(phead,3);
	display(phead);
	int ret=link_find(phead,4);
	printf("找到节点的位置是:%d\n",ret);
	display(phead);
	return 0;
}

2.双链表:

概念:就是在单链表的的每个结点中,再设置一个指针域,也就是每个结点都有两个指针域,一个指向它的前驱结点,一个指向它的后继结点。

使用原因:

虽然使用单链表能 100% 解决逻辑关系为 " 一对一 " 数据的存储问题,但在解决某些特殊问题时,单链表并不是效率最优的存储结构。比如说,如果算法中需要大量地找某指定结点的前趋结点,使用单链表无疑是灾难性的,因为单链表更适合 " 从前往后 " 找,而 " 从后往前 " 找并不是它的强项。所以为了实现“从后往前找”的功能,使用双链表。
结构:
1. 指针域:用于指向当前节点的直接前驱节点;
2. 数据域:用于存储数据元素。
3. 指针域:用于指向当前节点的直接后继节点;
以下是对一个双链表的增删改查:
#include<string.h>
#include<stdio.h>
#include<stdlib.h>
/* 双向链表的结构 */
struct List
{
int data; //数据域
struct List *next; //向后的指针
struct List *front; //向前的指针
};
typedef struct List NODE;
/*
* 双向链表的创建
*
*/
NODE *CreatList()
{
NODE *head = (NODE *)malloc(sizeof(NODE));
if(head == NULL)
{
printf("malloc error\n");
return NULL;
}
head->next = head->front = NULL; //初始化链表,指针置空
return head;
}
/*
* 双向链表插入节点,头插法
*/
void InsertNode(NODE *head , int data)
{
if( head == NULL )
{
printf("The list is empty.\n"); // 头结点为空,则无法插入
return;
}
NODE *tmpHead = head; // 创建一个临时的头结点指针
if( tmpHead->next == NULL )
{
/* 当双向链表只有一个头结点时 */
NODE *addition = (NODE *)malloc(sizeof(NODE));
if( addition == NULL )
{
printf("malloc error\n");
return;
}
addition->data = data; //数据域赋值
addition->next = tmpHead->next; //后向指针连接
tmpHead->next = addition;
addition->front = tmpHead; //将插入结点的front 指向头结点
}
else
{
/* 当双向链表不只一个头结点时 */
NODE *addition = (NODE *)malloc( sizeof(NODE));
if( addition == NULL )
{
printf("malloc error\n");
return;
}
addition->data = data; // 数据域赋值
tmpHead->next->front = addition; // 头结点的下一个结点的front 指针
addition->front = tmpHead; // 插入的结点的front 指针指向头结点
addition->next = tmpHead->next; // 插入结点的next 指针指向原本头指针的下
一结点
tmpHead->next = addition; // 将头结点的next 指针指向插入结点
}
}
/*
* 删除一个节点
*/
void DeleteNode(NODE *head , int data)
{
if(head == NULL)
{
printf("The list is empty.\n");
return;
}
NODE *tmpHead = head;
while( tmpHead->next != NULL )
{
tmpHead = tmpHead->next;
if( tmpHead->data == data )
{
tmpHead->front->next = tmpHead->next; // 将被删结点的上一个结
点的next 指针指向 被删结点的下一个结点
tmpHead->next->front = tmpHead->front; // 将被删结点的下一个结
点的 front 指针指向 被删结点的上一个结点
break;
}
}
free(tmpHead); //释放内存
tmpHead = NULL;
}
/*
* 修改一个节点的值
*/
void UpdateNode(NODE *head , int oldval,int newval)
{
if(head == NULL)
{
printf("The list is empty.\n");
return;
}
NODE *tmpHead = head;
while( tmpHead->next != NULL )
{
tmpHead = tmpHead->next;
if(tmpHead->data == oldval)
{
tmpHead->data == newval;
continue;
}
}
}
/*
* 查找值为val的节点位置
*/
int FindNode(NODE *head , int val)
{
int count = 0;
if(head == NULL)
{
printf("The list is empty.\n");
return -1;
}
NODE *tmpHead = head;
while( tmpHead->next != NULL )
{
tmpHead = tmpHead->next;
if(tmpHead->data == val)
{
return count;
}
count++;
}
return -1;
}
/*
* 双向链表的遍历
*/
void displayList(NODE *head)
{
printf("-------------------------------\n");
if( head == NULL )
{
printf("The list is empty.\n");
return;
}
NODE *tmpHead = head; //头节点没有数据
while(tmpHead->next != NULL )
{
tmpHead = tmpHead->next; //头结点数据域为空,因此直接从头结点的下一结点开
始遍历
printf("%d->",tmpHead->data);
}
// 此时tmpHead 的地址在链表的最后一个结点处
while( tmpHead->front->front != NULL )
{
printf("%d<-",tmpHead->data); // 最后一个结点要输出
tmpHead = tmpHead->front;
}
printf("%d\n",tmpHead->data);
return;
}
/*
* 销毁一个链表,从头结点开始
*/
void DestroyList(NODE *head)
{
NODE *tmp;
while(head->next != NULL)
{
tmp = head; // 指针不断后移
head = head->next;
free(tmp);
tmp = NULL;
}
free(head);
head = NULL;
}
int main()
{
//创建一个双向链表
NODE *phead = CreatList();
if(phead == NULL)
{
printf("create list error\n");
}
//双向链表的头部插入数据
int i;
for(i=0;i<6;i++)
{
InsertNode(phead , i);
}
//显示链表的数据
displayList(phead);
//删除链表中的一个节点
DeleteNode(phead , 4);
displayList(phead);
//修改链表中的一个节点的值
UpdateNode(phead , 2,222);
displayList(phead);
//查找链表中的一个值
int ret = FindNode(phead , 3);
printf("要找的节点是:%d\n",ret);
return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值