引言:
下面代码是否有错?
#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;
}