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

目录

一、单向循环链表

  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 输出效果

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

dify安装

官网教程 https://github.com/langgenius/dify/blob/main/README_CN.md 1、下载源码 git clone https://github.com/langgenius/dify.git 2、进入docker目录 cd dify cd docker cp .env.example .env修改nginx对外端口配置 修改为9000 最后执行&#xff1a;docker compo…

使用Termux将安卓手机变成随身AI服务器(page assist连接)

通过以下方法在安卓手机上运行 Ollama 及大模型&#xff0c;无需 Root 权限&#xff0c;具体方案如下&#xff1a; 通过 Termux 模拟 Linux 环境运行 核心工具&#xff1a; 安装 &#xff08;安卓终端模拟器&#xff09;()]。借助 proot-distro 工具安装 Linux 发行版&#xf…

C++ STL中的reverse/unique/sort/lower_bound/upper_bound函数使用

本文主要涉及以下几个函数&#xff1a; reverse&#xff1a;反转序列。unique&#xff1a;移除相邻重复元素。sort&#xff1a;对序列进行排序。lower_bound 和 upper_bound&#xff1a;查找目标值的边界位置。头文件均为<algorithm> 1. reverse 功能&#xff1a;反转指…

QT QLabel加载图片等比全屏自适应屏幕大小显示

最近在工作项目中,遇到一个需求: 1.使用QLabel显示一张图片; 2.当点击这个QLabel时,需要全屏显示;但不能改变原来的尺寸; 3.当点击放大后的QLabel时,恢复原有大小. 于是乎,就有了本篇博客,介绍如何实现这样的功能. 一、演示效果 在一个水平布局中&#xff0c;添加两个Lable用…

C# 背景 透明 抗锯齿 (效果完美)

主要是通过 P/Invoke 技术调用 Windows API 函数 gdi32.dll/user32.dll&#xff0c;同时定义了一些结构体来配合这些 API 函数的使用&#xff0c;常用于处理图形绘制、窗口显示等操作。 运行查看效果 局部放大&#xff0c;抗锯齿效果很不错,尾巴毛毛清晰可见。 using System; u…

Windows10 将Docker虚拟磁盘文件ext4.vhdx迁移至D盘

今天打开电脑发现之前迁移到D盘的ext4.vdx居然占有80多个G不得不重新清理一下了 于是先删除了d盘的ext4.vdx文件 注销了原来的 wsl --unregister docker-desktopwsl --unregister docker-desktop-data 确认 WSL 发行版状态&#xff1a; 运行以下命令以确认当前的 WSL 发行版…

OpenCV二值化处理

1.1. 为什么需要二值化操作 二值化操作将灰度图像转换为黑白图像&#xff0c;即将图像中的像素值分为两类&#xff1a;前景&#xff08;通常为白色&#xff0c;值为 255&#xff09;和背景&#xff08;通常为黑色&#xff0c;值为 0&#xff09;。二值化的主要目的是简化图像&…

深入了解 DevOps 基础架构:可追溯性的关键作用

在当今竞争激烈的软件环境中&#xff0c;快速交付强大的应用程序至关重要。尽管如此&#xff0c;在不影响质量的情况下保持速度可能是一项艰巨的任务&#xff0c;这就是 DevOps 中的可追溯性发挥作用的地方。通过提供软件开发生命周期 &#xff08;SDLC&#xff09; 的透明视图…

由浅入深学习大语言模型RLHF(PPO强化学习- v1浅浅的)

最近&#xff0c;随着DeepSeek的爆火&#xff0c;GRPO也走进了视野中。为了更好的学习GRPO&#xff0c;需要对PPO的强化学习有一个深入的理解&#xff0c;那么写一篇文章加深理解吧。纵观网上的文章&#xff0c;要么说PPO原理&#xff0c;各种复杂的公式看了就晕&#xff0c;要…

【Java八股文】08-计算机网络面试篇

【Java八股文】08-计算机网络面试篇 计算机网络面试篇网络模型网络OSI模型和TCP/IP模型分别介绍一下键入网址到网页显示&#xff0c;期间发生了什么&#xff1f; 应用层- HTTP应用层有哪些协议&#xff1f;HTTP是什么及HTTP报文有哪些部分&#xff1f;HTTP是怎么传输数据的HTTP…

【Linux】Linux 文件系统——有关 inode 不足的案例

ℹ️大家好&#xff0c;我是练小杰&#xff0c;今天周二了&#xff0c;明天星期三&#xff0c;还有三天就是星期五了&#xff0c;坚持住啊各位&#xff01;&#xff01;&#xff01;&#x1f606; 本文是对之前Linux文件权限中的inode号进行实例讨论&#xff0c;看到博客有错误…

SpringBoot整合Redis和Redision锁

参考文章 1.Redis 1.导入依赖 <!--Redis依赖--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><dependency><groupId>org.apache.c…

亲测可用,IDEA中使用满血版DeepSeek R1!支持深度思考!免费!免配置!

作者&#xff1a;程序员 Hollis 之前介绍过在IDEA中使用DeepSeek的方案&#xff0c;但是很多人表示还是用的不够爽&#xff0c;比如用CodeChat的方案&#xff0c;只支持V3版本&#xff0c;不支持带推理的R1。想要配置R1的话有特别的麻烦。 那么&#xff0c;今天&#xff0c;给…

一周学会Flask3 Python Web开发-Debug模式开启

锋哥原创的Flask3 Python Web开发 Flask3视频教程&#xff1a; 2025版 Flask3 Python web开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili 默认情况&#xff0c;项目开发是普通模式&#xff0c;也就是你修改了代码&#xff0c;必须重启项目&#xff0c;新代码才生效&…

某手sig3-ios算法 Chomper黑盒调用

Chomper-iOS界的Unidbg 最近在学习中发现一个Chomper框架&#xff0c;Chomper 是一个模拟执行iOS可执行文件的框架&#xff0c;类似于安卓端大名鼎鼎的Unidbg。 这篇文章使用Chomper模拟执行某手的sig3算法&#xff0c;初步熟悉该框架。这里只熟悉模拟执行步骤以及一些常见的…

PyTorch 深度学习框架中 torch.cuda.empty_cache() 的妙用与注意事项

&#x1f349; CSDN 叶庭云&#xff1a;https://yetingyun.blog.csdn.net/ 在使用 PyTorch 进行深度学习模型训练与调优过程中&#xff0c;torch.cuda.empty_cache() 方法作为一种高效工具被广泛采用&#xff1b;但其正确应用要求充分理解该方法的功能及最佳实践。下文将对该方…

巧用GitHub的CICD功能免费打包部署前端项目

近年来&#xff0c;随着前端技术的发展&#xff0c;前端项目的构建和打包过程变得越来越复杂&#xff0c;占用的资源也越来越多。我有一台云服务器&#xff0c;原本打算使用Docker进行部署&#xff0c;以简化操作流程。然而&#xff0c;只要执行sudo docker-compose -f deploy/…

配置Api自动生成

我的飞书:https://rvg7rs2jk1g.feishu.cn/docx/TVlJdMgYLoDJrsxAwMgcCE14nxt 使用Springfox Swagger生成API&#xff0c;并导入Postman&#xff0c;完成API单元测试 Swagger: 是一套API定义的规范&#xff0c;按照这套规范的要求去定义接口及接口相关信息&#xff0c;再通过可…

【JMeter使用-2】JMeter中Java Request采样器的使用指南

Apache JMeter 是一款功能强大的性能测试工具&#xff0c;支持多种协议和测试场景。除了内置的采样器&#xff08;如HTTP请求、FTP请求等&#xff09;&#xff0c;JMeter还允许通过 Java Request采样器 调用自定义的Java代码&#xff0c;从而实现更复杂的测试逻辑。本文将详细介…

将Google文档导入WordPress:简单实用的几种方法

Google文档是内容创作者非常实用的写作工具。它支持在线编辑、多人协作&#xff0c;并能够自动保存内容。但当我们想把Google文档中的内容导入WordPress网站时&#xff0c;可能会遇到一些小麻烦&#xff0c;比如格式错乱、图片丢失等问题。本文将为大家介绍几种简单实用的方法&…