c语言单链表

引言:

下面代码是否有错?

#include <stdio.h>

int main(void)
{
	struct Test
	{
		int x;
		int y;
		struct Test test;
	};
	return 0;
}

这是错误的,这个结构体是无限循环的,没有终止条件的,所以是不完整的结构体,会陷入无线递归。

只需改为

#include <stdio.h>

int main(void)
{
	struct Test
	{
		int x;
		int y;
		struct Test *test;
	};
	return 0;
}

 单链表

 信息域之间依靠指针连接,当最后一个指针指向NULL时,就代表着这个链表的结束。

把先前的Book改成链表的信息域 

#include <stdio.h>

struct Book
{
	char title[128];
	char author[128];
	struct Book *next;
};

int main(void)
{
	
	return 0;
}

如何给单链表增加一个新的信息域

头插法

#include <stdio.h>
#include <stdlib.h>

struct Book
{
	char title[128];
	char author[40];
	struct Book* next;
};//单链表

void getInput(struct Book* book)
{
	printf("书名:");
	scanf_s("%s", book->title);
	printf("作者:");
	scanf_s("%s", book->author);
}

//目的是给单链表添加信息域,所以要修改单链表
//单链表又是指针(指向Book的指针),要进行修改(指向其地址)就需要两次解引用**
void addBook(struct Book** library)
{
	struct Book* book,*temp;

	book = (struct Book*)malloc(sizeof(struct Book));
	//分配一个动态内存
	if (book == NULL)
	{
		printf("分配失败");
		exit(1);
	}

	getInput(book);

	//判断原链表是否有信息域
	if (*library == NULL)//空的单链表
	{
		*library = book;
		book->next = NULL;
	}
	else//有信息域的单链表
	{
		temp = *library;
		*library = book;
		book->next = temp;
	}
}

void printflibrary(struct Book* library)
{
	struct Book* book;
	int count = 1;

	book = library;
	while (book != NULL)
	{
		printf("Book%d:\n", count);
		printf("Title: %s\n", book->title);
		printf("Author: %s\n", book->author);
		book = book->next;
		count++;
	}
}

void freelibrary(struct Book** library)
{//要free library则需要输入其地址
	struct Book* temp;

	while (*library != NULL)
	{
		temp = *library;
		*library = (*library)->next;
		free(temp);
	}
}

int main(void)
{
	struct Book* library = NULL;
	//单链表的头指针为NULL,即一开始为空的单链表
	int ch;
	

	while (1)
	{
		printf("是否录入书籍(Y/N):\n");
		do
		{
			ch = getchar();
		} while (ch != 'Y' && ch != 'N');//防止输入格式不正确

		if (ch == 'Y')
		{
			addBook(&library);
			//在使用addBook时,注意是修改library的值,所以是输入其地址
		}
		else
		{
			break;
		}
	}

	printf("是否输出书籍(Y/N):\n");
	do
	{
		ch = getchar();
	} while (ch != 'Y' && ch != 'N');//防止输入格式不正确
	if (ch == 'Y')
	{
		printflibrary(library);
	}

	
	
	freelibrary(&library);
	//释放
	return 0;
}

VS有BUG,DEV可以

尾插法

只需要把头插法的add部分改为

void addBook(struct Book** library)
{
	struct Book* book, * temp;

	book = (struct Book*)malloc(sizeof(struct Book));
	//分配一个动态内存
	if (book == NULL)
	{
		printf("分配失败");
		exit(1);
	}

	getInput(book);

	//判断原链表是否有信息域
	if (*library == NULL)//空的单链表
	{
		*library = book;
		book->next = NULL;
	}
//*****************************************************************
	else//有信息域的单链表
	{
		temp = *library;
		while (temp->next != NULL)
		{
			temp = temp->next;
		}
		temp->next = book;
		book->next = NULL;
	}

//******************************************************************
}

但是在调用add函数时,每次调用都需要遍历一边book结构体,这极大的降低了代码的执行效率,所以我们需要提升效率,方法如下,在add函数中定义一个静态变量(直到整个程序结束后才清除)(通俗的说就是不会导致每次调用add函数时改变该变量的值)

void addBook(struct Book** library)
{
	struct Book* book;
	static struct Book* tail;
	
	book = (struct Book*)malloc(sizeof(struct Book));
	//分配一个动态内存
	if (book == NULL)
	{
		printf("分配失败");
		exit(1);
	}

	getInput(book);

	//判断原链表是否有信息域
	if (*library == NULL)//空的单链表
	{
		*library = book;
		book->next = NULL;
	}
	else//有信息域的单链表
	{
		tail->next = book;
		book->next = NULL;
	}
    tail = book;//重要
}

搜索单链表

实际上就是遍历单链表然后对比

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct Book
{
	char title[128];
	char author[40];
	struct Book* next;
};//单链表

void getInput(struct Book* book);
void addBook(struct Book** library);
void printflibrary(struct Book* library);
void freelibrary(struct Book** library);
struct Book *SearchBook(struct Book *library, char *target);
void printfsearch(struct Book* book);



void getInput(struct Book* book)
{
	printf("书名:");
	scanf_s("%s", book->title);
	printf("作者:");
	scanf_s("%s", book->author);
}

//目的是给单链表添加信息域,所以要修改单链表
//单链表又是指针(指向Book的指针),要进行修改(指向其地址)就需要两次解引用**
void addBook(struct Book** library)
{
	struct Book* book;
	static struct Book* tail;
	
	book = (struct Book*)malloc(sizeof(struct Book));
	//分配一个动态内存
	if (book == NULL)
	{
		printf("分配失败");
		exit(1);
	}

	getInput(book);

	//判断原链表是否有信息域
	if (*library == NULL)//空的单链表
	{
		*library = book;
		book->next = NULL;
	}
	else//有信息域的单链表
	{
		tail->next = book;
		book->next = NULL;
	}
	tail = book;
}

void printflibrary(struct Book* library)
{
	struct Book* book;
	int count = 1;

	book = library;
	while (book != NULL)
	{
		printf("Book%d:\n", count);
		printf("Title: %s\n", book->title);
		printf("Author: %s\n", book->author);
		book = book->next;
		count++;
	}
}

void freelibrary(struct Book** library)
{//要free library则需要输入其地址
	struct Book* temp;

	while (*library != NULL)
	{
		temp = *library;
		*library = (*library)->next;
		free(temp);
	}
}

struct Book* SearchBook(struct Book* library, char* target)
{
	struct Book* book;

	book = library;
	while (1)
	{
		if (!strcmp(book->title, target) || !strcmp(book->author, target))
		{
			//strcmp()比较字符串,如果相等->返回0;不相等->返回1
			break;
		}
		book = book->next;//迭代
	}
	return book;
}

void printfsearch(struct Book *book)
{
	printf("书名: %s", book->title);
	printf("作者: %s", book->author);
}

int main(void)
{
	struct Book* library = NULL;
	//单链表的头指针为NULL,即一开始为空的单链表
	

	int ch;
	while (1)
	{
		printf("是否录入书籍(Y/N):\n");
		do
		{
			ch = getchar();
		} while (ch != 'Y' && ch != 'N');//防止输入格式不正确

		if (ch == 'Y')
		{
			addBook(&library);
			//在使用addBook时,注意是修改library的值,所以是输入其地址
		}
		else
		{
			break;
		}
	}

	printf("是否输出书籍(Y/N):\n");
	do
	{
		ch = getchar();
	} while (ch != 'Y' && ch != 'N');//防止输入格式不正确
	if (ch == 'Y')
	{
		printflibrary(library);
	}

	char target[128];
	struct Book* book;
	printf("\n请输入书名或者作者:");

	scanf_s("%s", target);

	book = SearchBook(library, target);

	if (book != NULL)
	{
		printfsearch(book);
	}
	else
	{
		printf("对不起,没找到!");
	}

	freelibrary(&library);
	//释放
	return 0;
}

搜索函数和打印函数

struct Book* SearchBook(struct Book* library, char* target)
{
	struct Book* book;

	book = library;
	while (1)
	{
		if (!strcmp(book->title, target) || !strcmp(book->author, target))
		{
			//strcmp()比较字符串,如果相等->返回0;不相等->返回1
			break;
		}
		book = book->next;//迭代
	}
	return book;
}

void printfsearch(struct Book *book)
{
	printf("书名: %s", book->title);
	printf("作者: %s", book->author);
}

main函数中

    char target[128];
	struct Book* book;
	printf("\n请输入书名或者作者:");

	scanf_s("%s", target);

	book = SearchBook(library, target);

	if (book != NULL)
	{
		printfsearch(book);
	}
	else
	{
		printf("对不起,没找到!");
	}

这样过于简单,因为当搜索内容为多本身书的话,这样只能打印第一本符合条件的书,所以要想打印所有符合条件的书,应该这样写:

char target[128];
	struct Book* book;
	printf("\n请输入书名或者作者:");

	scanf_s("%s", target);

	book = SearchBook(library, target);

	if (book != NULL)
	{
		do
		{
			printf("已找到下列书籍:\n");
			printfsearch(book);
		} while ((SearchBook(book->next, target) != NULL));
	}
	else
	{
		printf("对不起,没找到!");
	}

单链表和数组的优缺点

数组的插入难题

当要把一个元素插入一个有序的数组中时,需要把后面的元素全部后移,才能插入

 而链表不一样,只需将如图插入位置的前方的指针改为指向需要插入的元素,然后指向之后的元素

 实现按照数字大小插入一段有顺序的单链表中

#include <stdio.h>
#include <stdlib.h>

struct Number
{
	int num;
	struct Number *next;
};

void addNum(struct Number **nums, int n);

void addNum(struct Number **nums, int n)
{
	struct Number* previous;
	struct Number* current;
	struct Number* new;
	//因为单链表的特性,访问到后面的信息域之后就不能再返回到先前的信息域了
	//所以使用new来储存新的信息域,用while循环来找new应该存放的位置
	//用current来记录这个位置,用previous来记录current的前一个位置

	current = *nums;
	previous = NULL;
	//current初始位置是第一个,所以previous=NULL

	while (current != NULL && current->num < n)
	{
		previous = current;
		current = current->next;
	}

	new = (struct Number*)malloc(sizeof(struct Number));

	if (new == NULL)
	{
		printf("分配失败");
		exit(1);
	}

	new->num = n;
	new->next = current;

	//此时previous还分两种情况

	if (previous == NULL)//第一:要插入的信息域在链表中还是最小的
	{
		*nums = new;
	}
	else//要插入的信息域在链表之间
	{
		previous->next = new;
	}
}

void printfNum(struct Number *nums)
{
	struct Number* current;

	current = nums;
	while (current != NULL)
	{
		printf("%d ", current->num);
		current = current->next;
	}
	putchar('\n');
}

int main(void)
{
	int input;
	struct Number* nums = NULL;

	while (1)
	{
		printf("请输入数字(-1为结束):\n");
		scanf_s("%d", &input);
		addNum(&nums, input);
		if (input == -1)
		{
			break;
		}
		printfNum(nums);
	}

	return 0;
}

删除

#include <stdio.h>
#include <stdlib.h>

struct Number
{
	int num;
	struct Number *next;
};

void addNum(struct Number **nums, int n);
void printfNum(struct Number* nums);

void addNum(struct Number **nums, int n)
{
	struct Number* previous;
	struct Number* current;
	struct Number* new;
	//因为单链表的特性,访问到后面的信息域之后就不能再返回到先前的信息域了
	//所以使用new来储存新的信息域,用while循环来找new应该存放的位置
	//用current来记录这个位置,用previous来记录current的前一个位置

	current = *nums;
	previous = NULL;
	//current初始位置是第一个,所以previous=NULL

	while (current != NULL && current->num < n)
	{
		previous = current;
		current = current->next;
	}

	new = (struct Number*)malloc(sizeof(struct Number));

	if (new == NULL)
	{
		printf("分配失败");
		exit(1);
	}

	new->num = n;
	new->next = current;

	//此时previous还分两种情况

	if (previous == NULL)//第一:要插入的信息域在链表中还是最小的
	{
		*nums = new;
	}
	else//要插入的信息域在链表之间
	{
		previous->next = new;
	}
}

void printfNum(struct Number *nums)
{
	struct Number* current;

	current = nums;
	while (current != NULL)
	{
		printf("%d ", current->num);
		current = current->next;
	}
	putchar('\n');
}

void deleteNum(struct Number** nums, int n)
{
	struct Number *previous, *current;

	current = *nums;
	previous = NULL;

	while (current != NULL && current->num != n)
	{
		previous = current;
		current = current->next;
	}

	if (current == NULL)
	{
		printf("找不到匹配的节点\n");
		return ;//在void函数中可以表示直接退出函数
	}
	else
	{
		if (previous == NULL)
		{
			*nums = current->next;
		}
		else
		{
			previous->next = current->next;
		}
		free(current);
	}
}

int main(void)
{
	int input;
	struct Number* nums = NULL;

	printf("插入整数中.......\n");
	while (1)
	{
		printf("请输入数字(-1为结束):\n");
		scanf_s("%d", &input);
		addNum(&nums, input);
		if (input == -1)
		{
			break;
		}
		printfNum(nums);
	}

	int input1;
	printf("删除整数中.......\n");
	while (1)
	{
		printf("请输入数字(-1为结束):\n");
		scanf_s("%d", &input1);
		if (input1 == -1)
		{
			break;
		}
		deleteNum(&nums, input1);
		printfNum(nums);
	}

	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值