数据结构——单向循环链表、双链表、双向循环链表

news2025/5/17 22:11:39

目录

一、单向循环链表

  1.1 单向循环链表的概念

  1.2 单向循环链表的操作

    1.2.1 单向循环链表的创建

    1.2.2 单向循环链表的头插

    1.2.3 单向循环链表的遍历

    1.2.4 单向循环链表的头删

    1.2.5 单向循环链表的尾插

    1.2.6 单向循环链表的尾删

    1.2.7 约瑟夫环

  1.3 单向循环列表所有程序

    1.3.1 head.h

    1.3.2 main.c

    1.3.3 fun.c

  1.4 输出效果

二、双链表

  2.1 双链表的概念

  2.2 双链表的操作

    2.2.1 双链表的创建

    2.2.2 双链表的头插

    2.2.3 双链表的遍历

    2.2.4 双链表的头删

    2.2.5 双链表的尾插

    2.2.6 双链表的尾删

  2.3 双链表所有程序

    2.3.1 head.h

    2.3.2 main.c

    2.3.3 fun.c

  2.4 输出效果

三、双向循环列表

  3.1 双向循环链表的概念

  3.2 双向循环链表的操作

    3.2.1 双向循环链表的创建

    3.2.2 双向循环链表的头插

    3.2.3 双向循环链表的遍历

    3.2.4 双向循环链表的头删

    3.2.5 双向循环链表的尾插

    3.2.6 双向循环链表的尾删

  3.3 双向循环链表所有程序

    3.3.1 head.h

    3.3.2 main.c

    3.3.3 fun.c

  3.4 输出效果


一、单向循环链表

  1.1 单向循环链表的概念

        1.单向循环链表:尾节点的指针域指向头结点的地址

        2.单向循环链表的节点:数据域和指针域

        数据域:存储的数据元素

        指针域:存储节点之间的关系,下一个节点的地址,单向循环链表的指针域指向该节点的地址

  1.2 单向循环链表的操作

    1.2.1 单向循环链表的创建

// 创建单向循环链表---------------
Linklist creat_node()
{
	// 1.创建一个新的节点
	Linklist s=(Linklist)malloc(sizeof(struct Node));
	// 2. 
	if(NULL==s)
		return NULL;
	// 初始化新节点的数据域
	s->data=0;
	// 初始化新节点的指针域
	s->next=s;
	return s;
}

    1.2.2 单向循环链表的头插

// 头插---------------
Linklist insert_head(Linklist head,datatype element)
{
	Linklist s=creat_node();
	s->data=element;
	// 1.判断链表是否为空
	if(NULL==head)
		head=s;
	else  // 链表中存在多个节点 >=1
	{
		// 找到尾节点
		Linklist del=head;
		while(del->next!=head)
		{
			del=del->next;
		}
		s->next=head;
		head=s;
		del->next=head;
	}
	return head;
}

    1.2.3 单向循环链表的遍历

// 循环遍历---------------
void output(Linklist head)
{
	//  1.判断链表是否为空
	if(NULL==head)
		return;
	// 2.循环遍历
	Linklist p=head;
	printf("Linklist=");
	do
	{
		printf("%d ",p->data);
		p=p->next;
	}while(p!=head);
	putchar(10);
	return;
}

    1.2.4 单向循环链表的头删

// 单向循环链表的头删---------------
Linklist delete_head(Linklist head)
{
	// 1.判断链表是否为空
	if(NULL==head)
		return head;
	// 2.判断链表是否为1
	if(head->next==head)
	{
		free(head);
		head=NULL;
		return head;
	}
	// 3.头删
	//  找到尾节点
	Linklist real=head;
	while(real->next!=head)
	{
		real=real->next;
	}
	Linklist del;
	del=head;
	head=head->next;
	free(del);
	del=NULL;
	real->next=head;
	return head;
}

    1.2.5 单向循环链表的尾插

// 单向循环链表的尾插---------------
Linklist insert_tail(Linklist head,datatype element)
{
	// 1.创建新节点,并输入值
	Linklist s=creat_node();
	s->data=element;
	s->next=head;
	// 2.判断链表是否为空
	if(NULL==head)
	{
		head=s;
	}
	else
	{
		// 3.尾插
		Linklist p=head;
		while(p->next!=head)  // 找到最后一个节点
		{
			p=p->next;
		}
		p->next=s;  // 链接:p指针域指向新节点的地址
	}
	return head;
}

    1.2.6 单向循环链表的尾删

// 单向循环链表的尾删---------------
Linklist delete_tail(Linklist head)
{
	// 1.判断链表是否为空
	if(NULL==head)
		return head;
	// 2.只有一个节点时
	else if(head->next==head)
	{
		free(head);
		head=NULL;
	}
	// 3.有多个节点时
	else
	{
		Linklist del=head;
		// 找到倒数第二个节点
		while(del->next->next!=head)
		{
			del=del->next;
		}
		// 删除p的后继
		free(del->next);
		del->next=head;
	}
		return head;
}

    1.2.7 约瑟夫环

        约瑟夫环:用循环链表编程实现约瑟夫问题

        n个人围成一圈,从某人开始报数1, 2, …, m,数到m的人出圈,然后从出圈的下一个人(m+1)开始重复此过程,直到 全部人出圈,于是得到一个出圈人员的新序列

        如当n=8,m=4时,若从第一个位置数起,则所得到的新的序列 为4, 8, 5, 2, 1, 3, 7, 6。

// 约瑟夫环
Linklist joseph(Linklist head,int n, int m)
{
	Linklist p=head;
	Linklist del=NULL;
	Linklist s=NULL;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<m-1;j++)
		{
			p=p->next;
		}
		del=p->next;
		p->next=p->next->next;
		p=p->next;
		if(i==1)
		{
			head=del;
			head->next=head;
		}
		else
		{
			s=head;
			while(s->next!=head)
			{
				s=s->next;
			}
			del->next=head;
			s->next=del;
		}
	}
	return head;
}

  1.3 单向循环列表所有程序

    1.3.1 head.h

#ifndef __HEAD_H__
#define __HEAD_H__

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef int datatype;
enum passble{SUCCSSES,FALSE=-1};
// 节点结构定义
// 节点:数据、指针域
// Node不可省略
typedef struct Node
{
	datatype data;  // 数据域:数据元素
	struct Node *next;  // 指针域:节点之间的关系,下一个节点的地址
}*Linklist;

Linklist creat_node();
Linklist insert_head(Linklist head,datatype element);
void output(Linklist head);
Linklist delete_head(Linklist);
Linklist insert_tail(Linklist head,datatype element);
Linklist delete_tail(Linklist head);
int get_len(Linklist head);
Linklist joseph(Linklist head,int n, int m);

#endif

    1.3.2 main.c

#include "head.h"
int main(int argc, const char *argv[])
{
// 单向循环链表的创建
// 单向循环链表的头插
	Linklist head=NULL;
	int n;
	printf("请输入插入个数:");
	scanf("%d",&n);
	datatype element;
	for(int i=0;i<n;i++)
	{
		printf("请输入插入的第 %d 个元素:",i+1);
		scanf("%d",&element);
		head=insert_head(head,element);
	}
	output(head);

// 单向循环链表的头删
	head=delete_head(head);
	printf("头删后");
	output(head);
// 单向循环链表的尾插
	printf("请输入尾插个数:");
	scanf("%d",&n);
	for(int i=0;i<n;i++)
	{
		printf("请输入尾插元素:");
		scanf("%d",&element);
		head=insert_tail(head,element);
	}
	output(head);

// 单向循环链表的尾删
	head=delete_tail(head);
	printf("尾删后");
	output(head);
	
// 约瑟夫环
	int m=4;
	head=joseph(head,n,m);
	output(head);

	return 0;
}

    1.3.3 fun.c

#include "head.h"
// 创建单向循环链表---------------
Linklist creat_node()
{
	// 1.创建一个新的节点
	Linklist s=(Linklist)malloc(sizeof(struct Node));
	// 2. 
	if(NULL==s)
		return NULL;
	// 初始化新节点的数据域
	s->data=0;
	// 初始化新节点的指针域
	s->next=s;
	return s;
}
// 头插---------------
Linklist insert_head(Linklist head,datatype element)
{
	Linklist s=creat_node();
	s->data=element;
	// 1.判断链表是否为空
	if(NULL==head)
		head=s;
	else  // 链表中存在多个节点 >=1
	{
		// 找到尾节点
		Linklist del=head;
		while(del->next!=head)
		{
			del=del->next;
		}
		s->next=head;
		head=s;
		del->next=head;
	}
	return head;
}

// 循环遍历---------------
void output(Linklist head)
{
	//  1.判断链表是否为空
	if(NULL==head)
		return;
	// 2.循环遍历
	Linklist p=head;
	printf("Linklist=");
	do
	{
		printf("%d ",p->data);
		p=p->next;
	}while(p!=head);
	putchar(10);
	return;
}
// 单向循环链表的头删---------------
Linklist delete_head(Linklist head)
{
	// 1.判断链表是否为空
	if(NULL==head)
		return head;
	// 2.判断链表是否为1
	if(head->next==head)
	{
		free(head);
		head=NULL;
		return head;
	}
	// 3.头删
	//  找到尾节点
	Linklist real=head;
	while(real->next!=head)
	{
		real=real->next;
	}
	Linklist del;
	del=head;
	head=head->next;
	free(del);
	del=NULL;
	real->next=head;
	return head;
}
// 单向循环链表的尾插---------------
Linklist insert_tail(Linklist head,datatype element)
{
	// 1.创建新节点,并输入值
	Linklist s=creat_node();
	s->data=element;
	s->next=head;
	// 2.判断链表是否为空
	if(NULL==head)
	{
		head=s;
	}
	else
	{
		// 3.尾插
		Linklist p=head;
		while(p->next!=head)  // 找到最后一个节点
		{
			p=p->next;
		}
		p->next=s;  // 链接:p指针域指向新节点的地址
	}
	return head;
}
// 单向循环链表的尾删---------------
Linklist delete_tail(Linklist head)
{
	// 1.判断链表是否为空
	if(NULL==head)
		return head;
	// 2.只有一个节点时
	else if(head->next==head)
	{
		free(head);
		head=NULL;
	}
	// 3.有多个节点时
	else
	{
		Linklist del=head;
		// 找到倒数第二个节点
		while(del->next->next!=head)
		{
			del=del->next;
		}
		// 删除p的后继
		free(del->next);
		del->next=head;
	}
		return head;
}

// 约瑟夫环
Linklist joseph(Linklist head,int n, int m)
{
	Linklist p=head;
	Linklist del=NULL;
	Linklist s=NULL;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<m-1;j++)
		{
			p=p->next;
		}
		del=p->next;
		p->next=p->next->next;
		p=p->next;
		if(i==1)
		{
			head=del;
			head->next=head;
		}
		else
		{
			s=head;
			while(s->next!=head)
			{
				s=s->next;
			}
			del->next=head;
			s->next=del;
		}
	}
	return head;
}

  1.4 输出效果

二、双链表

  2.1 双链表的概念

        引入目的:无论是单向循环或单向链表,只能向后遍历吗,不可以向前访问,引出双链表

        1.双向链表:链表既可以向前访问也可以向后访问

        2.双向链表的节点

        3.双向链表节点的结构体定义

        结构体:数据域,两个指针域

        4.双向链表插入和删除的思想

  2.2 双链表的操作

    2.2.1 双链表的创建

// 创建节点
Doublelink creat_link()
{
	Doublelink s=(Doublelink)malloc(sizeof(struct Node));
	if(NULL==s)
		return NULL;
	s->data=0;  // 新节点的数据域初始化
	s->next=NULL;  // 新节点的指针域初始化
	s->last=NULL;
	return s;
}

    2.2.2 双链表的头插

// 双向列表的头插
Doublelink insert_head(Doublelink head,datatype element)
{
	Doublelink s=creat_link();
	s->data=element;
	// 1.判断链表是否为空
	if(NULL==head)
	{
		head=s;
		return head;
	}
	// 2.链表不为空,存在多个节点
	s->next=head;  // 新节点的next指向头节点
	head->last=s;  // 头结点的last指向新节点
	head=s; // 使新节点成为头结点
	return head;
}

    2.2.3 双链表的遍历

// 双向列表的遍历
void outlink(Doublelink head)
{
	// 1.判断链表是否为空
	if(NULL==head)
		return;
	// 2.链表不为空
	else
	{
		Doublelink p=head;
		printf("正向遍历 Doublelink= ");
		while(p->next!=NULL)
		{
			printf("%c ",p->data);
			p=p->next;
		}
		printf("%c ",p->data);
		putchar(10);
		printf("逆向遍历 Doublelink= ");
		while(p)
		{
			printf("%c ",p->data);
			p=p->last;
		}
		putchar(10);
	}
		return;
}

    2.2.4 双链表的头删

// 双向链表的头删
Doublelink delete_head(Doublelink head)
{
	// 1.判断链表是否为空
	if(NULL==head)
		return head;
	// 2.判断链表节点是否为1
	else if(head->next==NULL)
	{
		free(head);
		head=NULL;
		return head;
	}
	// 2.链表有多个节点
	head=head->next;  // 让第二个节点成为头结点
	free(head->last);  // 释放原头结点
	head->last=NULL;  // 让新头结点的last指向空
}

    2.2.5 双链表的尾插

// 双向列表的尾插
Doublelink insert_tail(Doublelink head,datatype element)
{
	Doublelink s=creat_link();
	s->data=element;
	// 1.判断链表是否为空
	if(NULL==head)
	{
		head=s; // 直接让head指向新节点,即让新节点成为头结点
		return head;
	}
	// 2.链接有一个或多个节点
	Doublelink p=head;
	while(p->next!=NULL)  // 找到最后一个节点
	{
		p=p->next;
	}
	p->next=s;  // 让位后一个节点指向新节点,即新节点成为尾节点
	s->last=p;  // 让新节点的last指向原尾节点
	return head;

}

    2.2.6 双链表的尾删

// 双向列表的尾删
Doublelink delete_tail(Doublelink head)
{
	// 1.判断链表是否为空
	if(NULL==head)
		return head;
	// 2.判断链表节点是否为1
	else if(head->next==NULL)
	{
		free(head);  // 释放头结点
		head=NULL;  // head指向空
		return head;
	}
	// 3.链表有多个节点
	else
	{
		Doublelink p=head;
		while(p->next->next!=NULL)  // 找到倒数第二个节点
		{
			p=p->next;
		}
		free(p->next);  // 释放最后一个节点
		p->next=NULL;  // 让原倒数第二个节点指向空,即成为最后一个节点
		return head;
	}
	
}

  2.3 双链表所有程序

    2.3.1 head.h

#ifndef __HEAD_H__
#define __HEAD_H__


#include <stdio.h>
#include <string.h>
#include <stdlib.h>
enum passble{SUCCESS,FALSE=-1};
typedef char datatype;
// 双向链表节点结构体
typedef struct Node
{
	//数据域:数据元素
	datatype data;
	//指针域:下一个节点的地址
	struct Node *next;
	//指针域:上一个节点的地址
	struct Node *last;
}*Doublelink;
Doublelink creat_link();
Doublelink insert_head(Doublelink head,datatype element);
void outlink(Doublelink head);
Doublelink delete_head(Doublelink head);
Doublelink insert_tail(Doublelink head,datatype element);
Doublelink delete_tail(Doublelink head);

#endif

    2.3.2 main.c

#include "head.h"
int main(int argc, const char *argv[])
{
	Doublelink head=NULL;  // 定义一个链表节点指针
	int n;
	printf("请输入存入数据数量n:");
	scanf("%d",&n);
	datatype element;
	for(int i=1;i<=n;i++)
	{
		printf("请输入第%d个元素:",i);
		// scanf("%c",&element);
		getchar();
		element=getchar();
		head=insert_head(head,element);
	}
// 双向列表的遍历
	outlink(head);
	
// 双向链表的头删
	head=delete_head(head);
	printf("头删后\n");
	outlink(head);

// 双向列表的尾插
	printf("请输入尾插元素个数:");
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		printf("请输入尾插的第%d个元素:",i);
		getchar();
		element=getchar();
		head=insert_tail(head,element);
	}
	outlink(head);

// 双向列表的尾删
	head=delete_tail(head);
	printf("尾删后\n");
	outlink(head);

	return 0;
}

    2.3.3 fun.c

#include "head.h"
// 创建节点
Doublelink creat_link()
{
	Doublelink s=(Doublelink)malloc(sizeof(struct Node));
	if(NULL==s)
		return NULL;
	s->data=0;  // 新节点的数据域初始化
	s->next=NULL;  // 新节点的指针域初始化
	s->last=NULL;
	return s;
}

// 双向列表的头插
Doublelink insert_head(Doublelink head,datatype element)
{
	Doublelink s=creat_link();
	s->data=element;
	// 1.判断链表是否为空
	if(NULL==head)
	{
		head=s;
		return head;
	}
	// 2.链表不为空,存在多个节点
	s->next=head;  // 新节点的next指向头节点
	head->last=s;  // 头结点的last指向新节点
	head=s; // 使新节点成为头结点
	return head;
}

// 双向列表的遍历
void outlink(Doublelink head)
{
	// 1.判断链表是否为空
	if(NULL==head)
		return;
	// 2.链表不为空
	else
	{
		Doublelink p=head;
		printf("正向遍历 Doublelink= ");
		while(p->next!=NULL)
		{
			printf("%c ",p->data);
			p=p->next;
		}
		printf("%c ",p->data);
		putchar(10);
		printf("逆向遍历 Doublelink= ");
		while(p)
		{
			printf("%c ",p->data);
			p=p->last;
		}
		putchar(10);
	}
		return;
}

// 双向链表的头删
Doublelink delete_head(Doublelink head)
{
	// 1.判断链表是否为空
	if(NULL==head)
		return head;
	// 2.判断链表节点是否为1
	else if(head->next==NULL)
	{
		free(head);
		head=NULL;
		return head;
	}
	// 2.链表有多个节点
	head=head->next;  // 让第二个节点成为头结点
	free(head->last);  // 释放原头结点
	head->last=NULL;  // 让新头结点的last指向空
}

// 双向列表的尾插
Doublelink insert_tail(Doublelink head,datatype element)
{
	Doublelink s=creat_link();
	s->data=element;
	// 1.判断链表是否为空
	if(NULL==head)
	{
		head=s; // 直接让head指向新节点,即让新节点成为头结点
		return head;
	}
	// 2.链接有一个或多个节点
	Doublelink p=head;
	while(p->next!=NULL)  // 找到最后一个节点
	{
		p=p->next;
	}
	p->next=s;  // 让位后一个节点指向新节点,即新节点成为尾节点
	s->last=p;  // 让新节点的last指向原尾节点
	return head;

}

// 双向列表的尾删
Doublelink delete_tail(Doublelink head)
{
	// 1.判断链表是否为空
	if(NULL==head)
		return head;
	// 2.判断链表节点是否为1
	else if(head->next==NULL)
	{
		free(head);  // 释放头结点
		head=NULL;  // head指向空
		return head;
	}
	// 3.链表有多个节点
	else
	{
		Doublelink p=head;
		while(p->next->next!=NULL)  // 找到倒数第二个节点
		{
			p=p->next;
		}
		free(p->next);  // 释放最后一个节点
		p->next=NULL;  // 让原倒数第二个节点指向空,即成为最后一个节点
		return head;
	}
	
}

  2.4 输出效果

三、双向循环列表

  3.1 双向循环链表的概念

        双向循环链表:尾节点的下一个节点是头节点,头节点的上一个节点是尾节点

  3.2 双向循环链表的操作

    3.2.1 双向循环链表的创建

#include "head.h"
// 创建节点
Doublelink creat_link()
{
	Doublelink s=(Doublelink)malloc(sizeof(struct Node));
	if(NULL==s)
		return NULL;
	s->data=0;  // 新节点的数据域初始化
	s->next=s;  // 新节点的指针域初始化
	s->last=s;
	return s;
}

    3.2.2 双向循环链表的头插

// 双向循环列表的头插
Doublelink insert_head(Doublelink head,datatype element)
{
	Doublelink s=creat_link();
	s->data=element;
	// 1.判断链表是否为空
	if(NULL==head)
	{
		head=s;
		return head;
	}
	// 2.链表不为空,存在多个节点
	s->next=head;  // 新节点的next指向头节点
	s->last=head->last;  // 新节点的last指向尾节点
	head->last->next=s;  // 尾节点的next指向新节点
	head->last=s;  // 头结点的last指向新节点
	head=s; // 使新节点成为头结点
	return head;
}

    3.2.3 双向循环链表的遍历

// 双向循环列表的遍历
void outlink(Doublelink head)
{
	// 1.判断链表是否为空
	if(NULL==head)
		return;
	// 2.链表不为空
	else
	{
		Doublelink p=head;
		printf("正向遍历 Doublelink= ");
		while(p->next!=head)
		{
			printf("%c ",p->data);
			p=p->next;
		}
		printf("%c ",p->data);
		putchar(10);
		printf("逆向遍历 Doublelink= ");
		while(p!=head)
		{
			printf("%c ",p->data);
			p=p->last;
		}
		printf("%c",p->data);
		putchar(10);
	}
		return;
}

    3.2.4 双向循环链表的头删

// 双向循环链表的头删
Doublelink delete_head(Doublelink head)
{
	// 1.判断链表是否为空
	if(NULL==head)
		return head;
	// 2.判断链表节点是否为1
	else if(head->next==head)
	{
		free(head);
		head=NULL;
		return head;
	}
	// 2.链表有多个节点
	Doublelink p=head;
	head->last->next=head->next;
	head=head->next;  // 让第二个节点成为头结点
	head->last=head->last->last;  // 让新头结点指向尾节点
	free(p);  // 释放原头结点
	p=NULL;  // 让指针p指向空
	return head;
}

    3.2.5 双向循环链表的尾插

// 双向循环列表的尾插
Doublelink insert_tail(Doublelink head,datatype element)
{
	Doublelink s=creat_link();
	s->data=element;
	// 1.判断链表是否为空
	if(NULL==head)
	{
		head=s; // 直接让head指向新节点,即让新节点成为头结点
		return head;
	}
	// 2.链接有一个或多个节点
	
	head->last->next=s;  // 让最后一个节点指向新节点,即新节点成为尾节点
	s->next=head;
	s->last=head->last;  // 让新节点的last指向原尾节点
	head->last=s;
	return head;

}

    3.2.6 双向循环链表的尾删

// 双向循环列表的尾删
Doublelink delete_tail(Doublelink head)
{
	// 1.判断链表是否为空
	if(NULL==head)
		return head;
	// 2.判断链表节点是否为1
	else if(head->next==head)
	{
		free(head);  // 释放头结点
		head=NULL;  // head指向空
		return head;
	}
	// 3.链表有多个节点
	else
	{
		head->last=head->last->last;  // 让头结点的last指向倒数第二个节点
		free(head->last->next);  // 释放最后一个节点
		head->last->next=head;  // 让原倒数第二个节点指向头结点,即成为最后一个节点
		return head;
	}
	
}

  3.3 双向循环链表所有程序

    3.3.1 head.h

#ifndef __HEAD_H__
#define __HEAD_H__


#include <stdio.h>
#include <string.h>
#include <stdlib.h>
enum passble{SUCCESS,FALSE=-1};
typedef char datatype;
// 双向链表节点结构体
typedef struct Node
{
	//数据域:数据元素
	datatype data;
	//指针域:下一个节点的地址
	struct Node *next;
	//指针域:上一个节点的地址
	struct Node *last;
}*Doublelink;
Doublelink creat_link();
Doublelink insert_head(Doublelink head,datatype element);
void outlink(Doublelink head);
Doublelink delete_head(Doublelink head);
Doublelink insert_tail(Doublelink head,datatype element);
Doublelink delete_tail(Doublelink head);

#endif

    3.3.2 main.c

#include "head.h"
int main(int argc, const char *argv[])
{
	Doublelink head=NULL;  // 定义一个链表节点指针
	int n;
	printf("请输入存入数据数量n:");
	scanf("%d",&n);
	datatype element;
	for(int i=1;i<=n;i++)
	{
		printf("请输入第%d个元素:",i);
		// scanf("%c",&element);
		getchar();
		element=getchar();
		head=insert_head(head,element);
	}
// 双向循环列表的遍历
	outlink(head);

// 双向循环链表的头删
	head=delete_head(head);
	printf("头删后\n");
	outlink(head);

// 双向循环列表的尾插
	printf("请输入尾插元素个数:");
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		printf("请输入尾插的第%d个元素:",i);
		getchar();
		element=getchar();
		head=insert_tail(head,element);
	}
	outlink(head);

// 双向循环列表的尾删
	head=delete_tail(head);
	printf("尾删后\n");
	outlink(head);
	return 0;
}

    3.3.3 fun.c

#include "head.h"
// 创建节点
Doublelink creat_link()
{
	Doublelink s=(Doublelink)malloc(sizeof(struct Node));
	if(NULL==s)
		return NULL;
	s->data=0;  // 新节点的数据域初始化
	s->next=s;  // 新节点的指针域初始化
	s->last=s;
	return s;
}

// 双向循环列表的头插
Doublelink insert_head(Doublelink head,datatype element)
{
	Doublelink s=creat_link();
	s->data=element;
	// 1.判断链表是否为空
	if(NULL==head)
	{
		head=s;
		return head;
	}
	// 2.链表不为空,存在多个节点
	s->next=head;  // 新节点的next指向头节点
	s->last=head->last;  // 新节点的last指向尾节点
	head->last->next=s;  // 尾节点的next指向新节点
	head->last=s;  // 头结点的last指向新节点
	head=s; // 使新节点成为头结点
	return head;
}

// 双向循环列表的遍历
void outlink(Doublelink head)
{
	// 1.判断链表是否为空
	if(NULL==head)
		return;
	// 2.链表不为空
	else
	{
		Doublelink p=head;
		printf("正向遍历 Doublelink= ");
		while(p->next!=head)
		{
			printf("%c ",p->data);
			p=p->next;
		}
		printf("%c ",p->data);
		putchar(10);
		printf("逆向遍历 Doublelink= ");
		while(p!=head)
		{
			printf("%c ",p->data);
			p=p->last;
		}
		printf("%c",p->data);
		putchar(10);
	}
		return;
}

// 双向循环链表的头删
Doublelink delete_head(Doublelink head)
{
	// 1.判断链表是否为空
	if(NULL==head)
		return head;
	// 2.判断链表节点是否为1
	else if(head->next==head)
	{
		free(head);
		head=NULL;
		return head;
	}
	// 2.链表有多个节点
	Doublelink p=head;
	head->last->next=head->next;
	head=head->next;  // 让第二个节点成为头结点
	head->last=head->last->last;  // 让新头结点指向尾节点
	free(p);  // 释放原头结点
	p=NULL;  // 让指针p指向空
	return head;
}

// 双向循环列表的尾插
Doublelink insert_tail(Doublelink head,datatype element)
{
	Doublelink s=creat_link();
	s->data=element;
	// 1.判断链表是否为空
	if(NULL==head)
	{
		head=s; // 直接让head指向新节点,即让新节点成为头结点
		return head;
	}
	// 2.链接有一个或多个节点
	
	head->last->next=s;  // 让最后一个节点指向新节点,即新节点成为尾节点
	s->next=head;
	s->last=head->last;  // 让新节点的last指向原尾节点
	head->last=s;
	return head;

}

// 双向循环列表的尾删
Doublelink delete_tail(Doublelink head)
{
	// 1.判断链表是否为空
	if(NULL==head)
		return head;
	// 2.判断链表节点是否为1
	else if(head->next==head)
	{
		free(head);  // 释放头结点
		head=NULL;  // head指向空
		return head;
	}
	// 3.链表有多个节点
	else
	{
		head->last=head->last->last;  // 让头结点的last指向倒数第二个节点
		free(head->last->next);  // 释放最后一个节点
		head->last->next=head;  // 让原倒数第二个节点指向头结点,即成为最后一个节点
		return head;
	}
	
}

  3.4 输出效果

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2300237.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

冒险岛079 V8 整合版源码搭建教程+IDEA启动

今天教大家来部署下一款超级怀旧游戏冒险岛&#xff0c;冒险岛源码是开源的&#xff0c;但是开源的代码会有各种&#xff0c;本人进行了加工整合&#xff0c;并且用idea进行了启动测试&#xff0c;经过修改后没有任何问题。 启动截图 后端控制台 前端游戏界面 声明 冒险岛源码…

Qwen2-VL 的重大省级,Qwen 发布新旗舰视觉语言模型 Qwen2.5-VL

Qwen2.5-VL 是 Qwen 的新旗舰视觉语言模型&#xff0c;也是上一代 Qwen2-VL 的重大飞跃。 Qwen2.5-VL主要特点 视觉理解事物&#xff1a;Qwen2.5-VL不仅能够熟练识别花、鸟、鱼、昆虫等常见物体&#xff0c;而且还能够分析图像中的文本、图表、图标、图形和布局。 代理性&…

STM32外设SPI FLASH应用实例

STM32外设SPI FLASH应用实例 1. 前言1.1 硬件准备1.2 软件准备 2. 硬件连接3. 软件实现3.1 SPI 初始化3.2 QW128 SPI FLASH 驱动3.3 乒乓存储实现 4. 测试与验证4.1 数据备份测试4.2 数据恢复测试 5 实例5.1 参数结构体定义5.2 存储参数到 SPI FLASH5.3 从 SPI FLASH 读取参数5…

Java零基础入门笔记:(1-2)入门(简介、基础知识)

前言 本笔记是学习狂神的java教程&#xff0c;建议配合视频&#xff0c;学习体验更佳。 【狂神说Java】Java零基础学习视频通俗易懂_哔哩哔哩_bilibili - Java简介 Java是一种广泛使用的高级编程语言&#xff0c;具有简单、面向对象、分布式、多线程、动态性、健壮性和安全…

Java 基于 SpringBoot+Vue 的动漫平台(附源码,文档)

博主介绍&#xff1a;✌程序员徐师兄、8年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战*✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447…

Ubuntu 系统 cuda12.2 安装 MMDetection3D

DataBall 助力快速掌握数据集的信息和使用方式&#xff0c;会员享有 百种数据集&#xff0c;持续增加中。 需要更多数据资源和技术解决方案&#xff0c;知识星球&#xff1a; “DataBall - X 数据球(free)” 贵在坚持&#xff01; ---------------------------------------…

DDD该怎么去落地实现(3)通用的仓库和工厂

通用的仓库和工厂 我有一个梦&#xff0c;就是希望DDD能够成为今后软件研发的主流&#xff0c;越来越多研发团队都转型DDD&#xff0c;采用DDD的设计思想和方法&#xff0c;设计开发软件系统。这个梦想在不久的将来是有可能达成的&#xff0c;因为DDD是软件复杂性的解决之道&a…

【SpringBoot苍穹外卖】debugDay0 打开前端页面

在某一天学完后&#xff0c;电脑关机&#xff0c;再打开啥都忘了&#xff0c;记起来一点点&#xff0c;前端页面打不开&#xff0c;后端控制台一直循环出错。原来是下面这样哈哈。 查看端口是否被别的程序占用的操作步骤 winR输入cmd打开命令行 netstat -ano | findstr "8…

做谷歌SEO需要了解哪些基本概念?

做谷歌SEO时&#xff0c;必须掌握一些基本的概念。首先是关键词。关键词是用户在搜索框里输入的词汇&#xff0c;它们直接影响到你网站的排名。所以&#xff0c;了解用户的搜索习惯、挑选合适的关键词&#xff0c;是每一个SEO优化者必须做的工作。 内容是关键。谷歌非常看重网…

通过BingAPI爬取Bing半个月内壁纸

通过BingAPI爬取Bing半个月内壁纸 一、前言二、爬虫代码三、代码说明 一、前言 爬取Bing搜索网站首页壁纸的方式主要有两种&#xff0c;第一种为间接爬取&#xff0c;即并不直接对Bing网站发起请求&#xff0c;而是对那些收集汇总了Bing壁纸的网站发起请求&#xff0c;爬取图片…

springboot021-基于协同过滤算法的个性化音乐推荐系统

&#x1f495;&#x1f495;作者&#xff1a; 小九学姐 &#x1f495;&#x1f495;个人简介&#xff1a;十年Java&#xff0c;Python美女程序员一枚&#xff0c;精通计算机专业前后端各类框架。 &#x1f495;&#x1f495;各类成品Java毕设 。javaweb&#xff0c;ssm&#xf…

开关电源实战(一)宽范围DC降压模块MP4560

系列文章目录 文章目录 系列文章目录MP4560MP4560 3.8V 至 55V 的宽输入范围可满足各种降压应用 MOSFET只有250mΩ 输出可调0.8V-52V SW:需要低VF肖特基二极管接地,而且要靠近引脚,高压侧开关的输出。 EN:输入使能,拉低到阈值以下关闭芯片,拉高或浮空启动 COMP:Compens…

【MySQL】我在广州学Mysql 系列——Mysql 日志管理详解

ℹ️大家好&#xff0c;我是练小杰&#xff0c;今天又是新的一周了&#xff0c;又该摆好心态迎接美好的明天了&#xff01;&#xff01;&#xff01;&#x1f606; 本文主要对Mysql数据库中的日志种类以及基本命令进行讨论&#xff01;&#xff01; 回顾&#xff1a;&#x1f4…

《Zookeeper 分布式过程协同技术详解》读书笔记-2

目录 zk的一些内部原理和应用请求&#xff0c;事务和标识读写操作事务标识&#xff08;zxid&#xff09; 群首选举Zab协议&#xff08;ZooKeeper Atomic Broadcast protocol&#xff09;文件系统和监听通知机制分布式配置中心, 简单Demojava code 集群管理code 分布式锁 zk的一…

HTML5+CSS多层级ol标签序号样式问题

在CSS中&#xff0c;ol标签用于创建有序列表&#xff0c;而多层级的ol标签可以通过CSS实现不同的序号样式。以下是一些常见的问题和解决方案&#xff1a; 1. 多层级ol的序号格式问题 默认情况下&#xff0c;多层级的ol标签会自动继承父级的序号格式&#xff0c;但有时我们可能…

网络初始2:网络编程--基于UDP和TCP实现回显器

基础概念 1.发送端与接受端 在通过网络传输信息时&#xff0c;会有两个进程&#xff0c;接收端和发送端。 发送端&#xff1a;数据的发送方进程&#xff0c;即网络通信中的源主机。 接收端&#xff1a;数据的接收方进程&#xff0c;即网路通信中的目的主机。 2.Socet套接字…

vtkCamera类的Dolly函数作用及相机拉近拉远

录 1. 预备知识 1.1.相机焦点 2. vtkCamera类的Dolly函数作用 3. 附加说明 1. 预备知识 要理解vtkCamera类的Dolly函数作用,就必须先了解vtkCamera类表示的相机的各种属性。  VTK是用vtkCamera类来表示三维渲染场景中的相机。vtkCamera负责把三维场景投影到二维平面,如…

车载音频架构图详解(精简)

目录 上图是车载音频架构图,对这个图我们进行详细的分析 左边第一层 是 app 常用的类有MediaPlayer和MediaRecorder, AudioTrack和AudioRecorder 第二层 是framework提供给应用的多媒体功能的AP

使用神经网络对驾驶数据进行道路类型分类

摘要 道路分类&#xff0c;了解我们是在城市、农村地区还是在高速公路上驾驶&#xff0c;可以提高现代驾驶员辅助系统的性能&#xff0c;并有助于了解驾驶习惯。本研究的重点是仅使用车速数据来普遍解决这个问题。已经开发了一种数据记录方法&#xff0c;用于为 On-board Diagn…

S4D480 S4HANA 基于PDF的表单打印

2022年元旦的笔记草稿 SAP的表单打印从最早的SAPScripts 到后来的SMARTFORM&#xff0c;步入S4时代后由于Fiori的逐渐普及&#xff0c;更适应Web的Adobe Form成了SAP主流output文件格式。 目录 一、 基于PDF表单打印系统架构Interface 接口Form 表单ContextLayout 二、表单接…