链表的基本操作

头文件

#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
typedef int ElemType;
typedef struct LNode   //建立链表结构体
{
	ElemType data;
	struct LNode* next;   //还未定义别名只能用struct LNode定义next指针
}LinkList;

//functions.cpp里面函数声明
void InitList(LinkList*& L);   //初始化链表
void CreateList(LinkList*& L, ElemType a[], int n);//尾插法建表
int GetLength(LinkList*& L);   //求链表长度
void PrintList(LinkList*& L);   //输出链表中的元素
void ListEmpty(LinkList*& L);  //判断链表是否为空
void PrintElem(LinkList*& L, int i, ElemType& e); //输出链表中的第i个元素
void LocateElem(LinkList*& L, ElemType e);  //获取元素a的逻辑位置
bool InsertElem(LinkList*& L, int i, ElemType e);//在单链表的第i个位置插入值为e的结点
bool DeleteElem(LinkList*& L, int i, ElemType& e);//删除单链表中的第i个结点并用e返回其数据域值
void DestroyList(LinkList*& L);//销毁单链表
void Circulate(LinkList*& L);//使单链表变为循环单链表
void PrintCirList(LinkList*& L);//打印循环链表
void DestroyCirList(LinkList*& L);//销毁循环单链表
LinkList* createNode(ElemType data);//第一步:创建新结点
//在链表末尾添加结点
void appendNode(LinkList*&head, ElemType data); //调用的时候写的是appendNode(&head,data)


//#####test2-1里的函数声明
void InitCirList(LinkList*& L); //初始化循环单链表
LinkList* createCirNode(ElemType data);//第一步:创建新结点
//在循环单链表末尾添加结点
void appendCirNode(LinkList*&head, ElemType data);  //调用的时候写的是appendNode(&head,data)
int GetCirLength(LinkList*& L);   //求循环单链表长度
void PrintCirList(LinkList*& L);//打印循环链表
int CirIsEmpty(LinkList*& L);//判断循环单链表是否为空
void PrintCirElem(LinkList*& L, int i, ElemType& e); //输出循环单链表中的第i个元素
void LocateCirElem(LinkList*& L, ElemType e);  //获取循环单链表中元素a的逻辑位置
bool InsertCirElem(LinkList*& L, int i, ElemType e);//在循环单链表的第i个位置插入值为e的结点
bool DeleteCirElem(LinkList*& L, int i, ElemType& e);//删除循环单链表中的第i个结点并用e返回其数据域值
void DestroyCirList(LinkList*& L);//销毁循环单链表

//test2-2里的函数声明
// 获取链表中间结点(对于偶数长度链表,返回第二个中间结点)
LinkList* findMiddleNode(LinkList* head);

//test2-3里的函数声明

functions.cpp

#include"Sq.h"

void InitList(LinkList*& L)   //初始化链表
{
	L = (LinkList*)malloc(sizeof(LinkList));  //创建头结点
	L->next = NULL;     //其next域置为空
}
void CreateList(LinkList*& L, ElemType a[], int n)//尾插法建表
{
	LinkList* s, * r;  //s是用于暂存的中间结点,而r是用于进行链接的结点,始终指向尾结点,但一开始时指向头结点
	L = (LinkList*)malloc(sizeof(LinkList));  //创建头结点
	r = L;
	int i = 0;
	for (i = 0; i < n; i++)
	{
		s = (LinkList*)malloc(sizeof(LinkList));  //创建新结点
		s->data = a[i];
		r->next = s;   //将*s插入*r之后,即L之后
		r = s;  //移动尾指针
	}
	r->next = NULL;   //尾结点next域置为NULL
}
LinkList* createNode(ElemType data)//第一步:创建新结点
{
	LinkList* newNode = (LinkList*)malloc(sizeof(LinkList));
	if (newNode == NULL)
	{
		printf("内存分配失败\n");
		exit(1);
	}
	newNode->data = data;  //赋值
	newNode->next = NULL;  //next域置空
	return newNode;
}
//在链表末尾添加结点
void appendNode(LinkList*& head, ElemType data)  //调用的时候写的是appendNode(&head,data)
{
	LinkList* newNode = createNode(data);
	if (head->next==NULL)
	{
		head->next = newNode;
		return;
	}
	LinkList* temp = head;
	while (temp->next != NULL)
	{
		temp = temp->next;
	}
	temp->next = newNode;

}

int GetLength(LinkList*& L)   //求链表长度
{
	LinkList* p = L;  //设置指针p向后扫描整个链表
	int n = 0;  //设置n为计数器,起始值为0
	while (p->next != NULL)
	{
		n++;
		p = p->next;
	}
	printf("链表的长度为%d", n);
	return n;
}
void PrintList(LinkList*& L)   //输出链表中的元素
{
	LinkList* p = L;
	printf("链表中的元素有:");
	while (p->next != NULL)
	{
		printf("%d\t", p->next->data);
		p = p->next;
	}
	printf("\n");
}
void ListEmpty(LinkList*& L)  //判断链表是否为空
{
	if (L->next == NULL)
	{
		printf("该链表为空\n");
	}
	else
	{
		printf("该链表不为空\n");
	}
}
void PrintElem(LinkList*& L, int i, ElemType& e) //输出链表中的第i个元素
{
	int j = 0;
	LinkList* p = L;
	while (p != NULL && j < i)
	{
		j++;
		p = p->next;
	}
	if (p == NULL)
	{
		printf("sorry not found");
		exit(0);
	}
	else
	{
		e = p->data;
	}
	printf("第%d个元素是%d\n", i, e);
}
void LocateElem(LinkList*& L, ElemType e)  //获取元素a的逻辑位置
{
	int i = 1, flag = 0;;
	LinkList* p = L->next;
	while (p != NULL)
	{
		if (p->data == e)
		{
			printf("%d的逻辑位置在第%d位\n", e, i);
			flag = 1;
		}
		i++;
		p = p->next;
	}
	if (flag == 0)
	{
		printf("sorry not found");
		exit(0);
	}

}
bool InsertElem(LinkList*& L, int i, ElemType e)//在单链表的第i个位置插入值为e的结点
{
	LinkList* p = L, * s;
	if (i < 1)
	{
		return false;
	}
	int j = 0;
	while (p != NULL && j < i - 1)
	{
		j++;
		p = p->next;
	}
	if (p == NULL)
	{
		printf("sorry not found");
	}
	else
	{
		s = (LinkList*)malloc(sizeof(LinkList));
		s->data = e;
		s->next = p->next;
		p->next = s;
		return true;
	}
}
bool DeleteElem(LinkList*& L, int i, ElemType& e)//删除单链表中的第i个结点并用e返回其数据域值
{
	LinkList* p = L, * q;
	if (i < 1)
	{
		return false;
	}
	int j = 0;
	while (p != NULL && j < i - 1)
	{
		j++;
		p = p->next;
	}
	if (p == NULL)
	{
		printf("sorry not found");
		return false;
	}
	else
	{
		q = p->next;
		if (q == NULL)
		{
			printf("sorry not found");
			return false;
		}
		e = q->data;
		p->next = q->next;
		free(q);
		return true;
	}
}
void DestroyList(LinkList*& L)//销毁单链表
{
	LinkList* pre = L, * p = L->next;
	while (p != NULL)
	{
		free(pre);
		pre = p;
		p = p->next;
	}
	free(pre);//循环结束时,p为NULL,pre指向尾结点,释放它
}
void Circulate(LinkList*& L)//使单链表变为循环单链表
{
	LinkList* p = L;
	while (p->next != NULL)
	{
		p = p->next;
	}
	p->next = L;
}


编写一个程序,实现循环单链表的各种基本运算,本实验的单链表元素的类型为int,在主函数中调用循环单链表的基本操作完成如下操作:
(1)初始化循环单链表h
(2)依次插入1、3、1、4
(3)输出循环单链表L的长度
(4)输出循环单链表L
(5)判断循环单链表L是否为空
(6)输出循环单链表的第3个元素
(7)输出元素3的逻辑位置
(8)在第4个元素位置上插入元素5
(9)输出循环单链表L
(10)删除L的第3个元素
(11)输出单链表L
(12)释放单链表L

test2-1.cpp

#include"Sq.h"
void InitCirList(LinkList*& L)   //初始化循环单链表
{
	L = (LinkList*)malloc(sizeof(LinkList));  //创建头结点
	L->next = L;     //其next域置为头结点
}
LinkList* createCirNode(ElemType data)//第一步:创建新结点
{
	LinkList* newNode = (LinkList*)malloc(sizeof(LinkList));
	if (newNode == NULL)
	{
		printf("内存分配失败\n");
		exit(1);
	}
	newNode->data = data;  //赋值
	newNode->next = NULL;  //next域置空
	return newNode;
}
//在循环单链表末尾添加结点
void appendCirNode(LinkList*&head, ElemType data)  //调用的时候写的是appendNode(&head,data)
{
	LinkList* newNode = createNode(data);
	if (head->next==head)
	{
		head->next = newNode;
		newNode->next = head;
	}
	else
	{
		LinkList* temp = new LinkList;
		temp = head;
		while (temp->next != head)
		{
			temp = temp->next;
		}
		temp->next = newNode;
		newNode->next = head;
	}
}
int GetCirLength(LinkList*& L)   //求循环单链表长度
{
	LinkList* p = L;  //设置指针p向后扫描整个链表
	int n = 0;  //设置n为计数器,起始值为0
	if ( L->next == L)//空链表或只有头结点的链表
	{
		return 0;
	}
	while (p->next != L)
	{
		n++;
		p = p->next;
	}
	printf("链表的长度为%d\n", n);
	return n;
}
void PrintCirList(LinkList*& L)//打印循环链表
{
	LinkList* p = L;
	printf("循环链表中的元素有:");
	while (p->next != L)
	{
		printf("%d\t", p->next->data);
		p = p->next;
	}
	printf("\n");
}
int CirIsEmpty(LinkList*& L)//判断循环单链表是否为空
{
	if (L == NULL || L->next == L)
	{
		printf("链表为空\n");
		return 1;
	}
	else
	{
		return 0;
	}
}
void PrintCirElem(LinkList*& L, int i, ElemType& e) //输出循环单链表中的第i个元素
{
	int j = 0;
	LinkList* p = L->next;
	while (p != L && j < i-1)//j小于i-1是因为一开始p就指向了第一个结点而不是头结点
	{
		j++;
		p = p->next;
	}
	if (p == L)
	{
		printf("sorry not found");
		exit(0);
	}
	else
	{
		e = p->data;
	}
	printf("第%d个元素是%d\n", i, e);
}
void LocateCirElem(LinkList*& L, ElemType e)  //获取循环单链表中元素a的逻辑位置
{
	int i = 1, flag = 0;;
	LinkList* p = L->next;
	while (p != L)
	{
		if (p->data == e)
		{
			printf("%d的逻辑位置在第%d位\n", e, i);
			flag = 1;
		}
		i++;
		p = p->next;
	}
	if (flag == 0)
	{
		printf("sorry not found\n");
		exit(0);
	}

}
bool InsertCirElem(LinkList*& L, int i, ElemType e)//在循环单链表的第i个位置插入值为e的结点
{
	LinkList* p = L->next, * s;
	if (i < 1||i>GetCirLength(L)+1)
	{
		return false;
	}
	int j = 1;
	while (p != L && j < i - 1)
	{
		j++;
		p = p->next;
	}
	if (p == L)
	{
		appendCirNode(L, e);
	}
	else
	{
		s = (LinkList*)malloc(sizeof(LinkList));
		s->data = e;
		s->next = p->next;
		p->next = s;
		return true;
	}
}
bool DeleteCirElem(LinkList*& L, int i, ElemType& e)//删除循环单链表中的第i个结点并用e返回其数据域值
{
	LinkList* p = L->next, * q;
	if (i < 1)
	{
		return false;
	}
	int j = 1;
	while (p != L && j < i - 1)//p是要删除的结点的上一结点
	{
		j++;
		p = p->next;
	}
	if (p == L)
	{
		printf("sorry not found\n");
		return false;
	}
	else
	{
		q = p->next;
		if (q == L)
		{
			printf("sorry not found\n");
			return false;
		}
		e = q->data;
		p->next = q->next;
		free(q);
		return true;
	}
}
void DestroyCirList(LinkList*& L)//销毁循环单链表
{
	LinkList* pre = L, * p = L->next;
	while (p != L)
	{
		free(pre);
		pre = p;
		p = p->next;
	}
	free(pre);//循环结束时,p为NULL,pre指向尾结点,释放它
}




/*
int main(int argc, char* argv[])
{
	LinkList* L=new LinkList;
	InitCirList(L);
	appendCirNode(L,1);
	appendCirNode(L, 3);
	appendCirNode(L, 1);
	appendCirNode(L, 4);
	GetCirLength(L);
	PrintCirList(L);
	int e=0;
	PrintCirElem(L,3,e);
	LocateCirElem(L,3);
	InsertCirElem(L,4,5);
	PrintCirList(L);
	DeleteCirElem(L,3,e);
	printf("被删除的元素是%d\n",e);
	PrintCirList(L);
	DestroyCirList(L);
	return 0;
}
*/

在这里插入图片描述

已知带头结点的单链表L,设计一个算法求链表的中间结点。如果链表长度为奇数,返回唯一的中间结点,如果链表长度为偶数,返回第二个中间结点。

test2-2

#include"Sq.h"
// 获取链表中间结点(对于偶数长度链表,返回第二个中间结点)
LinkList* findMiddleNode(LinkList* head) 
{
    LinkList* slow = head->next;
    LinkList* fast = head->next;
    while (fast != NULL && fast->next != NULL)
    //fast!=NULL这个条件是针对有奇数个结点时,比如5个结点,fast指针只能跳到了第5个结点的next域,即为NULL。
    //fast->next!=NULL这个条件是针对于有偶数个结点时,比如6,fast刚好跳到了第6个结点,第6个结点不为空但它的next域为空
    {
        slow = slow->next;
        fast = fast->next->next;
    }
    // 如果链表长度为偶数,移动慢指针一次
    if (fast != NULL) //因为链表长度为奇数时fast==NULL
    {
        slow = slow->next;
    }
    return slow;
}

/*int main(int argc, char* argv[])
{
	LinkList* L=new LinkList;
    InitList(L);
	appendNode(L,1);
	appendNode(L, 2);
	appendNode(L, 3);
	appendNode(L, 4);
	appendNode(L, 5);
	appendNode(L, 6);
	printf("原");
	PrintList(L);
    LinkList* middleNode = findMiddleNode(L);
    printf("Middle node: %d\n", middleNode->data);
	return 0;
}
*/



在这里插入图片描述

编写算法逆置一个带头结点的双链表

test2-3

#include"Sq.h"
typedef struct DNode//定义双链表的结构体
{
	int data;
	struct DNode* prev;
	struct DNode* next;
}DNode;
DNode* createDNode(int data)//创建新结点
{
	DNode* newNode = (DNode*)malloc(sizeof(DNode));
	newNode->data = data;
	newNode->prev = NULL;
	newNode->next = NULL;
	return newNode;
}
void insertDNode(DNode*& head, int data)//在双链表末尾插入新结点
{
	DNode* newNode = createDNode(data);
	if (head->next == NULL)//如果头结点的next域为空则将新结点变为头结点next域,即第一个数据结点
	{
		head->next = newNode;
		return;
	}
	else
	{
		DNode* temp = head;
		while (temp->next != NULL)
		{
			temp = temp->next;
		}
		temp->next = newNode;
		newNode->prev = temp;
	}
}
void PrintDList(DNode* head)//打印双链表
{
	DNode* temp = head;
	while (temp->next != NULL)//注意这里是temp而不是temp->next因为在目前这里的双链表定义中没有无数据的头结点
	{
		printf("%d\t", temp->next->data);
		temp = temp->next;
	}
	printf("\n");
}

void reverseDList(DNode* head)
/**
 * 采用的思想是先将第一个数据结点跟头结点断开,
 * 把下一结点拼接到前部分结点的前面形成一个新的链表,
 * 再依次将后面的结点拼接到这个链表的前面实现逆置
 * eg.原链表是1、2、3、4,逐渐变为
 * 2、1
 * 3、2、1
 * 4、3、2、1
 * 再将头结点接回新链表的的前面
 */
{
	if (head->next == NULL || head->next->next == NULL)
	{
		return; // 链表为空或只有一个节点,无需逆置
	}
	DNode* current = head->next;//令current为第一个数据结点
	DNode* temp = NULL;
	current->prev = NULL;//将第一个结点的prev域置为空,要将其与头结点先断开方便后面的逆置操作
	temp = current->next;//令temp为第二个数据结点
	//将第一个结点的next域置为空,因为逆置的话第一个结点要变为最后一个结点,next域要变为NULL才方便实现打印之类的操作
	current->next = NULL;
	DNode* temp2;
	while (temp != NULL)
	{
		temp2 = temp->next;//temp2保存将要处理的结点的下一结点
		//将temp接到current的前面
		current->prev = temp;
		temp->next = current;
		//将current变为temp,即被接到前面的结点变为第一个
		current = temp;
		//将要处理的结点变为下一个
		temp = temp2;
	}
	//将头结点接回到新链表的前面
	current->prev = head;
	head->next = current;
}
void DestroyDList(DNode* &head)//销毁双链表
{
	DNode* current = head;
	DNode* temp;
	while (current != NULL)
	{
		temp = current;
		current = current->next;
		free(temp);
	}
	head = NULL;//将头结点指针置为空
}
/*int main(int argc, char* argv[])
{
	DNode* head = new DNode;
	head->prev = NULL;
	head->next = NULL;
	insertDNode(head, 1);
	insertDNode(head, 2);
	insertDNode(head, 3);
	insertDNode(head, 4);
	printf("原双链表为:");
	PrintDList(head);
	reverseDList(head);
	printf("逆置后双链表为:");
	PrintDList(head);
	DestroyDList(head);
	return 0;
}
*/


在这里插入图片描述

已知带头结点的单链表L,设计一个算法,以单链表的首结点值x为基准,将该单链表分割为两个部分,使所有小于x的结点排在大于或等于x的结点之前。例如,原链表为(3,5,4,1,2),完成该算法后链表为(2,1,3,5,4)。要求空间复杂度为O(1)。(可以用头插法或者交换)

test2-4

#include"Sq.h"
/*****************************************************************//**
 * 已知带头结点的单链表L,设计一个算法,以单链表的首结点值x为基准,
 * 将该单链表分割为两个部分,使所有小于x的结点排在大于或等于x的结点之前。
 * 例如,原链表为(3,5,4,1,2),完成该算法后链表为(2,1,3,5,4)。
 * 要求空间复杂度为O(1)。(可以用头插法或者交换)
 *********************************************************************/
void DevideL(LinkList*& L, int x)//
{
	if (L->next == NULL || L->next->next == NULL)
	{  //如果为空链表或只有一个数据结点的话则结束
		return;
	}
	//一开始用front来保存第一个数据结点,若有后面的数据插上来则front指向这个新插上来的数据结点
	LinkList* front = L->next;
	//设置一个rear指针指向开始往下轮着处理完的链表的尾部,比如3,5,4,1,2
	// 处理完3和5,则rear指向5这个结点
	LinkList* rear = L;//一定要注意一开始时rear是指向头结点的!
	//temp1指针用来指向要处理的结点,一开始是第一个数据结点,一般在rear指针的后面
	LinkList* temp1 = L->next;
	LinkList* temp2;//temp2用于暂存temp1的下一个结点
	while (temp1 != NULL)
	{

		if (temp1->data < x)
		{
			temp2 = temp1->next;
			rear->next = temp1->next;
			temp1->next = front;
			front = temp1;
			temp1 = temp2;
		}
		else
		{
			temp1 = temp1->next;
			rear = rear->next;//尾指针往后移动一位
		}
	}
	L->next = front;//将分割好的链表接回到头结点的后面
}





int main(int argc, char* argv[])
{
	LinkList* L;
	InitList(L);
	appendNode(L,3);
	appendNode(L, 5);
	appendNode(L, 4);
	appendNode(L, 1);
	appendNode(L, 2);
	printf("分割前");
	PrintList(L);
	DevideL(L,3);
	printf("分割后");
	PrintList(L);
	DestroyList(L);
	return 0;
}



在这里插入图片描述

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值