C语言 关于单链表的所有操作

目前实现的所有操作如下图所示:

学数据结构半个多月了,下面是整合的对单链表组的操作,测试了一下数据,应该没问题。

以下是代码:

#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#include<string.h>
#define MAXNUM 10
int cnt = 0;
typedef struct node {
	int data;
	struct node* next;
}Node, *LinkList;
//链表的初始化操作
void Init(LinkList *L) {
	*L = (LinkList)malloc(sizeof(Node));
	(*L)->data = 0;
	(*L)->next = 0;
}
void InitClear(LinkList *L) {
	(*L)->next = NULL;
}
//进行当前链表,链表组的打印
void PrintCurrList(LinkList*L) {
	LinkList p = (*L)->next;
	printf("Head->");
	if (!p)
		printf("NULL");
	while (p) {
		printf("%d", p->data);
		if (p->next)
			printf("->");
		p = p->next;
	}
	printf("\n");
}
int PrintAllList(LinkList*L) {
	int Index = 0;
	LinkList*p = L;
	int tag, IsDisplay = 0;
	while (Index < 10) {
		if (p[Index]) {
			printf("L%d:", Index);
			PrintCurrList(p + Index);
			IsDisplay = 1;
		}
		Index++;
	}
	if (IsDisplay) {
		printf("请输入当前想操控的链表:\n");
		scanf("%d", &tag);
		while (!*(L + tag)) {
			printf("输入有误,请再次输入:   ");
			scanf("%d", &tag);
		}
		return tag;
	}
	else {
		printf("当前列表空荡荡,请创建链表\n");
		return 0;
	}
}
void CreateList(LinkList*L) {
	*L = (LinkList)malloc(sizeof(Node));
	(*L)->data = 0;
	LinkList tail = *L, p;
	int j = 0, temp, n;
	printf("请输入结点数目:");
	scanf("%d", &n);
	while (j++ < n) {
		p = (LinkList)malloc(sizeof(Node));
		scanf("%d", &temp);
		p->data = temp;
		tail->next = p;
		tail = p;
	}
	tail->next = 0;
}
void CreateHead(LinkList*L, int*a, int len) {
	*L = (LinkList)malloc(sizeof(Node));
	int i = 0;
	LinkList p;
	(*L)->data = 0;
	(*L)->next = NULL;
	while (i < len) {
		p = (LinkList)malloc(sizeof(Node));
		p->data = a[i++];
		p->next = (*L)->next;
		(*L)->next = p;
	}
}
int getLen(LinkList *L) {
	LinkList p = (*L)->next;
	int cnt = 0;
	while (p) {
		p = p->next;
		cnt++;
	}
	return cnt;
}
int defaultL(LinkList*L) {
	cnt = 0;
	while (*L) {
		L++;
		cnt++;
	}
	if (cnt > 9) cnt = 9;
	return cnt;
}
LinkList getNode(LinkList*L, int i) {
	LinkList p = (*L);
	int j;
	for (j = 0; p&&j < i; j++) {
		p = p->next;
	}
	if (!p || j > i)
		return 0;//相当于return为空
	return p;
}
void swapNode(LinkList*L, int a, int b) {
	int len = getLen(L);
	//此时在赋值后,L可以当做一个地址在之后的函数中操作
	if (b > len || b<1 || a>len || a < 1||a==b) {
		printf("输入的结点有误!\n");
	}
	else {
		if (abs(a - b) == 1) {
			LinkList p;
			if (a<b)
				p = getNode(L, a - 1);
			if (b < a)  p = getNode(L, b - 1);
			LinkList anode, bnode;
			anode = p->next;
			bnode = anode->next;
			anode->next = bnode->next;
			bnode->next = anode;
			p->next = bnode;
		}
		else {
			LinkList apre, anode, anext, bpre, bnode, bnext;
			apre = getNode(L, a - 1);
			anode = apre->next;
			anext = apre->next->next;
			bpre = getNode(L, b - 1);
			bnode = bpre->next;
			bnext = bpre->next->next;
			bnode->next = anext;

			apre->next = bnode;
			bpre->next = anode;
			anode->next = bnext;
			bnode->next = anext;
		}
	}
}
void Insertnode(LinkList *L, int i, int data) {
	int j = 1;
	int len = getLen(L) + 1;
	LinkList p = *L, q;
	if (!p || i>len || i<1)  return;
	for (j; j < i&&p; j++) {
		p = p->next;
	}
	q = (LinkList)malloc(sizeof(Node));
	q->data = data;
	q->next = p->next;
	p->next = q;
}
void deleteNode(LinkList*L, int data) {
	LinkList p = (*L)->next, q;
	while (p->next) {
		if (p->next->data == data) break;
		p = p->next;
	}
	if (!(p->next)) {
		printf("你所查找的数据不存在\n");
		return;
	}
	else {
		q = p->next;
		p->next = q->next;
		free(q);
	}
}
void ClearList(LinkList*L) {
	LinkList p = *L, q;
	while (p) {
		q = p->next;//先进行预存,因为删除后就无法进行访问了
		free(p);
		p = q;
	}
	*L = 0;
}
LinkList getmidnode(LinkList*L) {
	LinkList s, p = (*L)->next;
	s = p;
	while (p) {
		//在p改变之前先进性改变
		if (!(p->next)) break;
		p = p->next->next;
		s = s->next;
	}
	return s;
}
//在排序中要保留结点的前驱部分,前驱部分不会随着交换而改变
void BubbleSort(LinkList*L) {
	LinkList end, p, temp, p1;
	p = *L;
	for (end = 0; end != *L; end = temp) {
		//for循环中先执行条件语句,然后执行之后的代码,最后再执行赋值语句
		for (temp = p = *L; p->next->next != end; p = p->next) {
			//最开始给了end空值,所以开始要循环到末尾,要从被比较结点前面的一个结点开始操作,所操作的最大限值在p->next->next
			if (p->next->data > p->next->next->data) {
				//p1 = p->next;
				//p1->next = p1->next->next;//这一步就相当于删除了p1后面的结点,不能在进行交换操作了
				//p1->next->next = p1;
				//p->next = p1->next;
				p1 = p->next->next;//要先保存住排在后面的结点
				p->next->next = p1->next;
				p1->next = p->next;
				p->next = p1;
				temp = p->next->next;
			}
		}
	}
}

void SelectSort(LinkList*L) {
	LinkList start, p, temp, p2, temp1;
	start = (*L)->next;
	int cnt = 1, cnt1;
	for (p = start; p; p = temp->next) {
		cnt1 = cnt;
		for (p2 = temp = p; p2; p2 = p2->next, cnt1++) {
			//这些指针只是位置的指标,实际操作的链表是一体的
			if (p->data > p2->data) {
				temp1 = p;//提前预留交换后的状态
				swapNode(L, cnt, cnt1);
				p = temp = getNode(L, cnt);
				p2 = temp1;
			}
		}
		cnt++;
	}
}
void selectSort2(LinkList *L) {
	//相当于新建了一个链表,把原链表的结点按最小值依次拆分放到新链表中
	LinkList start, tail, min, minpre, p;
	start = 0;
	while (*L) {
		for (p = *L, min = *L; p->next; p = p->next) {
			//头结点和min要默认定义在最开始的节点上
			if (p->next->data < min->data) { //找到一个比当前结点更小的结点
				minpre = p;//保存上最小结点前面的结点,不会随着链表的变动而变动,以便于以后的操作
				min = p->next;
			}
		}
		if (!start) {
			start = min;
			tail = min;
		}
		/*
		//教训:此时要加else,如果是if语句的话就承接了上面的if语句,最终结果为整个链表都为头结点的值
		if(start) {
		tail->next = min;
		tail = min;
		}
		*/
		else {
			tail->next = min;
			tail = min;
		}
		//将最小的那个结点从原链表中移除掉
		if (min == *L) {
			*L = (*L)->next;
		}
		else {
			minpre->next = min->next;
		}
		//free(min);  //不要把原结点的空间释放掉
	}
	tail->next = 0;
	*L = start;//为原链表重新赋值
}
void Insertsort(LinkList*L) {
	LinkList p, start, min, minpre, q;
	start = (*L)->next;//插入要从后面的数开始
	(*L)->next = 0;//把原链表换为空链表,然后再插入
				   //整个插入过程是把start插入到*L,然后根据start指针把start内的成员逐个插入到*L中
	while (start) {
		for (p = *L, q = start; p&&start->data > p->data; minpre = p, p = p->next);
		//在此获得start应插入到*L的前结点
		//q是预留的start空间
		start = start->next;//注意:这里start先进行,因为下面q的变化会联动start,先在此预留空间
		if (p == *L) {//插在头结点前面时
			*L = q;
			q->next = p;
		}
		else {
			//其余情况,*L中有小于start的结点,则插到*L中
			minpre->next = q;
			q->next = p;
		}
	}
}
void InsertOrdered(LinkList*L, int data) {
	LinkList q = *L, pre;
	LinkList p = (LinkList)malloc(sizeof(Node));
	p->data = data;
	if (!(*L)) {//若*L是空链表,则为其创建头结点
		*L = p;
		p->next = 0;
	}
	else {
		//while (q->data < p->data&&q) {
		//教训:一定要把q放到前面,当q为空时,q->data已经不能访问,先判断q==NULL,可免去后续的判断
		while (q&&q->data < p->data) {
			pre = q;
			q = q->next;
		}
		if (q == *L) {
			*L = p;
			p->next = q;
		}
		else {
			p->next = pre->next;
			pre->next = p;
		}
	}
}
void PrintList(LinkList*L) {
	int Index = 0;
	LinkList*p = L;
	int IsDisplay = 0;
	while (Index < 10) {
		if (p[Index]) {
			printf("L%d:", Index);
			PrintCurrList(p + Index);
			IsDisplay = 1;
		}
		Index++;
	}
}
void Combined(LinkList*L, LinkList*b) {
	if (!(*b)) return;
	else {
		LinkList p = *L, pre;
		while (p) {
			pre = p;
			p = p->next;
		}
		LinkList q = (*b)->next;
		pre->next = q;
		free(*b);
		*b = 0;
		//ClearList(b);//不要清除整个链表,只清楚头结点即可
	}
}
void ReverseList(LinkList*L) {
	LinkList p = (*L)->next, q = 0, temp, p1 = *L;
	while (p) {
		temp = p->next;
		p->next = q;
		q = p;
		p = temp;
	}
	p1->next = q;
	//把预留的头结点接到q上,这样实现了出来头结点以外其他结点的倒置
	*L = p1;
}
void ClearAllList(LinkList *L) {
	for (int i = 0; i < MAXNUM; i++)
		ClearList(L + i);
}
void Menu() {
	printf("1:创建链表(空链表)\n2:创建链表(头插法,数组创建)\n3:创建链表(尾插法,自己赋值)\n4:显示与改变当前链表\n");
	printf("5:当前链表长度\n6:获取第i个结点的数据\n7:交换两结点\n8:插入结点\n9:删除结点\n10:获取中间结点数据\n");
	printf("11:排序链表(冒泡排序)\n12:排序链表(插入排序)\n13:排序链表(选择排序)\n14:插入有序结点\n");
	printf("15:合并链表\n16:倒置链表\n17:初始化为空链表\n18:删除该链表\n19:清空数据并重新显示菜单\n20:退出该程序\n");
}
int main() {
	static int LinkTag, temp;
	//static,每次调用时为上次调用的值,其存储空间一旦声明就不会释放
	int a[] = { 3,23,34,43,151,325,32 };
	int len = sizeof(a) / sizeof(int);
	LinkList L[MAXNUM] = { 0 };
	LinkList tempList;
	int choice, num1, num2;
	Menu();
	while (1) {
		printf("--------------------------------------\n");
		printf("请输入您的操作:");
		scanf("%d", &choice);
		switch (choice)
		{
		case 1:
			LinkTag = defaultL(L);
			Init(L + LinkTag);
			PrintCurrList(L + LinkTag);
			break;
		case 2:
			LinkTag = defaultL(L);
			CreateHead(L + LinkTag, a, len);
			PrintCurrList(L + LinkTag);
			break;
		case 3:
			LinkTag = defaultL(L);
			CreateList(L + LinkTag);
			PrintCurrList(L + LinkTag);
			break;
		case 4:
			LinkTag = PrintAllList(L);
			break;
		case 5:
			printf("当前链表长度为%02d\n", getLen(L + LinkTag));
			break;
		case 6:
			printf("输入第i个结点:");
			scanf("%d", &temp);
			tempList = getNode(L + LinkTag, temp);
			if (!tempList)
				printf("该节点不存在\n");
			else
				printf("L%d的第%d个值为:%d\n", LinkTag, temp, tempList->data);
			break;
		case 7:
			printf("请输入要交换的两个结点:");
			scanf("%d %d", &num1, &num2);
			swapNode(&L[LinkTag], num1, num2);
			PrintCurrList(L + LinkTag);
			break;
		case 8:
			printf("输入要插入的结点 和 数字:");
			scanf("%d %d", &num1, &num2);
			Insertnode(L + LinkTag, num1, num2);
			PrintCurrList(L + LinkTag);
			break;
		case 9:
			printf("输入你要删除的数据:");
			scanf("%d", &num1);
			deleteNode(L + LinkTag, num1);
			PrintCurrList(L + LinkTag);
			break;
		case 10:
			tempList = 0;
			tempList = getmidnode(L + LinkTag);
			printf("当前链表的中间结点数据为%d\n", tempList->data);
			break;
		case 11:
			BubbleSort(L + LinkTag);
			PrintCurrList(L + LinkTag);
			break;
		case 12:
			Insertsort(L + LinkTag);
			PrintCurrList(L + LinkTag);
			break;
		case 13:
			selectSort2(L + LinkTag);
			PrintCurrList(L + LinkTag);
			break;
		case 14:
			printf("输入你要插入的数据:");
			scanf("%d", &num1);
			InsertOrdered(L + LinkTag, num1);
			PrintCurrList(L + LinkTag);
			break;
		case 15:
			PrintList(L);
			printf("当前链表为L%d,请输入想要合并的链表:", LinkTag);
			scanf("%d", &num1);
			Combined(L + LinkTag, L + num1);
			PrintCurrList(L + LinkTag);
			break;
		case 16:
			ReverseList(L + LinkTag);
			PrintCurrList(L + LinkTag);
			break;
		case 17:
			InitClear(L + LinkTag);
			PrintCurrList(L + LinkTag);
			break;
		case 18:
			ClearList(L + LinkTag);
			printf("删除该链表之后,还剩下:\n");
			PrintAllList(L);
			break;
		case 19:
			system("cls");
			ClearAllList(L);
			Menu();
			break;
		case 20:
			exit(0);
			break;
		default:
			break;
		}
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值