有关多种(单向,双向,循环)链表的操作----初始化,插入,输出,删除,头(尾)插法

代码以功能排布,相同的功能,不同种类的链表放在一块便于对比

声明:

#include"stdio.h"
#include"stdlib.h"

typedef struct Node {
	int data;
	struct Node* Next;
}*PList;
typedef struct DNode {
	int data;
	struct DNode* Next;
	struct DNode* Prior;
}*DList; //双向循环链表
typedef struct CNode {
	int data;
	struct CNode* Next;
}*CList; //单向循环链表

输出:

不同的链表注意结束的条件

/*输出组*/
void Print(struct Node* p) {
	printf("单向链输出: ");
	p = p->Next;
	while (p) {
		printf("%d ", p->data);
		p = p->Next;
	}
	printf("\n");
}

void Print_D(struct DNode* P) {
	printf("双向链输出: ");
	struct DNode* p = P->Next;
	while (p != P) {
		printf("%d ", p->data);
		p = p->Next;
	}
	printf("\n");
}

void Print_CH(struct CNode* P) {
	printf("循环链输出(头指针): ");
	struct CNode* p = P->Next;
	while (p != P) {
		printf("%d ", p->data);
		p = p->Next;
	}
	printf("\n");
}

void Print_CE(struct CNode* p) {  //p指向链表的尾部
	printf("循环链输出(尾指针): ");
	struct CNode* c = p->Next->Next;
	while (c != p->Next) { //c指向头结点的时候结束
		printf("%d ", c->data);
		c = c->Next;
	}
	printf("\n");
}

初始化

要对每个指针域赋初始值:

  1. 单项链表初始时需要对头结点的next赋值NULL
  2. 循环链表和双向链表的初始化需要next和prior指向头结点
/*初始化组*/
int Init(PList* P) {
	*P =  (PList)malloc(sizeof(struct Node));
	if (!*P) {
		printf("failed to initialize\n");
		return 1;
	}
	(*P)->Next = NULL; //一定要记住赋初值
	return 0;
}

int DInit(DList* P) {
	*P = (DList)malloc(sizeof(struct DNode));
	if (!*P) {
		printf("failed to initialize\n");
		return 1;
	}
	(*P)->Next = *P;
	(*P)->Prior = *P;
	return 0;
}

int CInit(CList* P) {
	*P = (CList)malloc(sizeof(struct CNode));
	if (!*P) {
		printf("failed to initialize\n");
		return 1;
	}
	(*P)->Next = *P;
	return 0;
}

插入:

注意插入时的p指向的问题:

  1. p的指向在单链表和循环链表中通常是插入位置的前一个结点
  2. 在双向链表中,我实现的是指向i的前一个结点,当然也有其他的实现,但其他实现的while的条件就会发生改变
/*插入 从1到n都可插   插到第i个前*/
int ListInsert(PList* P, int i, int e) {
	PList p = *P;	int j = 0;
	while (p && j < i - 1) { //p要指向i前一个结点
		p = p->Next;	j++;
	}
	if (i - 1 < j || !p) {
		printf("i is error\n");
		return 0;
	}
	PList s = (PList)malloc(sizeof(struct Node));
	s->data = e;
	s->Next = p->Next;
	p->Next = s;
	return 1;
}

int DListInsert(DList* P, int i, int e) {
	DList p = *P;	int j = 0;
	while (p->Next != *P && j < i - 1) { //p指向i前一个结点 注意第一次时p指向头结点,需要进入while循环,之后的条件不可以以p指向头结点为退出条件
									 //因为指向i这个结点时会有麻烦,当在最后一个插入时,需要再次指向头节点,所以while条件就会麻烦。
		p = p->Next;	j++;
	}
	if (i - 1 != j) { 
		printf("i is error\n");
		return 0;
	}
	DList s = (DList)malloc(sizeof(struct DNode));
	s->data = e;
	s->Next = p->Next;
	p->Next->Prior = s;
	p->Next = s;
	s->Prior = p;
	return 1;
}

int CListInsert(CList* P, int i, int e) {
	CList p = *P;	int j = 0;
	while (p->Next != *P && j < i - 1) { //p指向i前一个结点 理由同上
		p = p->Next;	j++;
	}
	if (i - 1 != j) {
		printf("i is error\n");
		return 0;
	}
	CList s = (CList)malloc(sizeof(struct CNode));
	s->data = e;
	s->Next = p->Next;
	p->Next = s;
	return 1;
}

头插法与尾插法:

//头插法
int CHListInsert(CList* P, int e) {
	CList H = (CList)malloc(sizeof(struct CNode));
	if (!H) {
		return 0;
	}
	H->data = e;
	H->Next = (*P)->Next;
	(*P)->Next = H;
	return 1;
}


//尾插法
int CEListInsert(CList* P, int e) {
	CList E = (CList)malloc(sizeof(struct CNode));
	if (!E) {
		return 0;
	}
	E->data = e;
	E->Next = (*P)->Next;
	(*P)->Next = E;
	*P = E;
	return 1;
}

删除:

删除时p的指向也是个问题:

  1. 在删除时,单向和循环我实现的都是让p指向 i 前一个结点,双向实现的是指向第i个,你也可以有其他实现
  2. 删除操作与插入操作的while条件不同,因为删除操作必须要保证删除的结点存在
/*删除组*/
int DeleList(PList* P, int i, int* e) {
	PList p = *P;	int j = 0; //p指向i前一个结点且要保证第i个结点存在
	while (p->Next != NULL && j < i - 1) {
		p = p->Next;	j++;
	}
	if (i < j || p->Next == NULL) {
		return 0;
	}
	PList d = p->Next;
	*e = d->data;
	p->Next = d->Next;
	free(d);
	return 1;
}

int DeleDList(DList* P, int i, int* e) {
	DList p = (*P)->Next;	int j = 1; //让p指向第i个结点
	while (p != *P && j < i) {
		p = p->Next;	j++;
	}
	if (i != j || p == *P) {
		return 0;
	}
	*e = p->data;
	p->Next->Prior = p->Prior;
	p->Prior->Next = p->Next;
	free(p);
	return 0;
}

int DeleCList(CList* P, int i, int* e) {
	CList p = *P;	int j = 0; //p指向i前一个 要保证i个结点存在
	while (p->Next != *P && j < i - 1) {
		p = p->Next;	j++;
	}
	if (p->Next == *P || i < j) {
		return 0;
	}
	CList d = p->Next;
	*e = d->data;
	p->Next = d->Next;
}

测试:

你也可以设置一些更刁钻的测试,如果发现输出结果不符合预期,可以在评论区留言

/*测试*/
int main() {
	PList plist; DList dlist;	CList clist;
	Init(&plist);	DInit(&dlist);	CInit(&clist);
	Print(plist);
	Print_D(dlist);
	Print_CH(clist);
	printf("\n");
	int elem[] = {1,2,3,4,5};
	for (int i = 0; i < 5; i++) { //有效插入
		ListInsert(&plist, i+1, elem[i]);
		DListInsert(&dlist, i+1, elem[i]);
		CListInsert(&clist, i+1, elem[i]);
	}
	Print(plist);
	Print_D(dlist);
	Print_CH(clist);
	printf("\n");
	int err = 9;
	//错误插入
	ListInsert(&plist, 7, err);
	DListInsert(&dlist, 7, err);
	CListInsert(&clist, 7, err);
	ListInsert(&plist, 0, err);
	DListInsert(&dlist, 0, err);
	CListInsert(&clist, 0, err);
	Print(plist);
	Print_D(dlist);
	Print_CH(clist);
	printf("\n 尾插法: \n");
	//循环链表的尾插法
	CList c;
	CInit(&c);
	for (int i = 0; i < 7; i++) {
		CEListInsert(&c, i);
	}
	Print_CE(c);
	printf("\n 头插法: \n");
	//循环链表的头插法
	CList h;
	CInit(&h);
	for (int i = 0; i < 7; i++) {
		CHListInsert(&h, i);
	}
	Print_CH(h);
	printf("\n");
}

运行结果
这段代码实现了一些链表的基础操作
如果还需要什么功能,可以在评论区内留言

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值