说在前面:链表章节(22题之前)多处用到了二级指针,有些地方可以选择使用一级指针也可
题目目录
1. 顺序表01(2023/08/19)
(1)题目描述
已知线性表(a1,a2,……,an)按顺序结构存储且每个元素为不相等的整数。
设计把所有奇数移动到所有偶数前边的算法。
例如,A=(1,2,3,4,5,6,7),移动后变为 A=(1,7,3,5,4,6,2)
(要求时间最少,辅助空间最少)
(2)算法思想
对于顺序表,从前往后找偶数,与从后往前找到的奇数进行对调(在原线性表上操作,减少 辅助空间)
(3)代码示例
#include <stdio.h>
/*
* 已知线性表(a1,a2,……,an)按顺序结构存储且每个元素为不相等的整数。
* 设计把所有奇数移动到所有偶数前边的算法。
* 例如,A=(1,2,3,4,5,6,7),移动后变为 A=(1,7,3,5,4,6,2)
* (要求时间最少,辅助空间最少)
*/
void move(int *array, int length);
int main() {
int array[] = { 1, 2, 3, 4, 5, 6, 7 };
int length = sizeof(array) / sizeof(array[0]); //求出线性表的长度
printf("length=%d\n", length);
move(array,length);
for (int i = 0; i < length; i++) {
printf("%5d", array[i]);
}
return 0;
}
void move(int *array, int length) {
int i = 0; //指向线性表的第一个元素
//求出线性表的长度(这个地方错误是因为数组名作为函数参数传递后会退化为指针,所以sizeof()计算实质上是一个指针的大小
//int j = sizeof(array) / sizeof(array[0]);
int j = length - 1;
int temp; //临时中间变量
while (i <= j) {
while (array[i] % 2 == 1) { //从前往后找偶数,与后面的奇数进行交换
i++;
}
while (array[j] % 2 == 0) { //从后往前找奇数,与前面的偶数进行交换
j--;
}
if (i < j) {
temp = array[i];
array[i] = array[j];
array[j] = temp;
}
}
}
(4)总结
本着考研监督自己每天写 >=1道C代码题,也在此做一个记录!
2. 顺序表02(2023/08/19)
(1)题目描述
设计一个高效算法,将顺序表L中所有元素逆置,要求算法的空间复杂度为O(1)
(2)算法思想
利用两个“指针”分别从前往后、从后往前对顺序表进行扫描,一一对应对调即可(在原顺序表上操作,未增加辅助空间),空间复杂度为O(1)
(3)代码示例
#include <stdio.h>
void reverse(int* array, int length); //函数声明
int main() {
int array[] = { 10,65,5,6,3,9,8,12,26,31,30,23 };
int length = sizeof(array) / sizeof(array[0]);
printf("reverse之前顺序表L中的元素:\n");
for (int i = 0; i < length; i++) {
printf("%5d", array[i]);
}
reverse(array, length);
printf("\n==================\n");
printf("reverse之后顺序表L中的元素:\n");
for (int i = 0; i < length; i++) {
printf("%5d", array[i]);
}
return 0;
}
/*
* 设计一个高效算法,将顺序表L中所有元素逆置,要求算法的空间复杂度为O(1)
*/
void reverse(int *array, int length)
{
int temp;
for (int i = 0, j = length - 1; i < length / 2; i++, j--) { //将顺序表中的元素前后对调,只需对半对调即可
temp = array[i];
array[i] = array[j];
array[j] = temp;
}
}
(4)总结
so easy!
3. 顺序表03(2023/08/20)
(1)题目描述
将两个有序表合并为一个新的有序顺序表,并由函数返回结果顺序表。
(2)算法思想
首先安顺序分别从两个顺序表中去除较小的元素存入新表,再看看哪个表还有剩余,将剩余的再加到新的顺序表后面
(3)代码示例
#include <stdio.h>
//这里的顺序表采用数组
bool Merge(int* array01, int length01, int* array02, int length02, int* MergeArray, int length03); //函数声明
int main()
{
int array01[] = { 1,2,3,5,7,12 };
int array02[] = { 4,7,10,13,16,38 };
int MergeArray[20] = { 0 }; //将合并后的元素存入该数组中,的初始化否则会报错
int length01 = sizeof(array01) / sizeof(array01[0]);
int length02 = sizeof(array02) / sizeof(array02[0]); //分别求出两个有序顺序表的长度
int length03 = sizeof(MergeArray) / sizeof(MergeArray[0]);
printf("======合并前两个有序顺序表的元素分别为===============\n");
printf("array01: ");
for (int i = 0; i < length01; i++) {
printf("%5d", array01[i]);
}
printf("\narray02: ");
for (int i = 0; i < length02; i++) {
printf("%5d", array02[i]);
}
bool result = Merge(array01, length01, array02, length02, MergeArray,length03);
if (result) {
printf("\n\n======合并后两个有序顺序表的元素分别为===============\n");
for (int j = 0; j < (length01 + length02); j++) {
printf("%5d", MergeArray[j]);
}
}
else {
printf("\n\n合并失败!超出表长!!!\n");
}
return 0;
}
/*
* 题目描述:
* 将两个有序表合并为一个新的有序顺序表,并由函数返回结果顺序表。
*
* 算法思想:
* 首先安顺序分别从两个顺序表中去除较小的元素存入新表,再看看哪个表还有剩余,将剩余的再加到新的顺序表后面
*/
//在外部先用sizeof(arr)/sizeof(arr[0])求出数组长度,然后将他的长度作为一个参数传递进去
bool Merge(int* array01, int length01, int* array02, int length02,int *MergeArray,int length03) {
if ((length01 + length02) > length03) {
return false;
}
int i = 0;
int j = 0;
int k = 0;
while (i < length01 && j < length02) {
if (array01[i] < array02[j]) {
MergeArray[k++] = array01[i++];
}
else {
MergeArray[k++] = array02[j++];
}
}
while (i < length01) {
MergeArray[k++] = array01[i++];
}
while (j < length02) {
MergeArray[k++] = array02[j++];
}
length03 = k;
return true;
}
(4)总结
没难度!
4. 顺序表04(2023/08/21)
(1)题目描述
从顺序表中删除具有最小值的元素(假设唯一)并由函数返回被删除元素的值。空出的位置由最后一个元素填补
(2)算法思想
搜索整个顺序表,查找最小值元素并记录其位置和value,搜索结束后用最后一个元素填补空出的原最小值元素的位置
(3)代码示例
#include <stdio.h>
int Delete_Min(int* array, int length);//函数声明
int main()
{
int array[] = { 5,6,9,15,32,64,8,9,5,7,21,53,69,1,2,8,67 };
int length = sizeof(array) / sizeof(array[0]); //计算出顺序表的长度
printf("顺序表array的元素如下:\n");
for (int j = 0; j < length - 1; j++) {
printf("%5d", array[j]);
}
int MinValueofArray = Delete_Min(array, length);
printf("\n\n顺序表array中的具有最小值的元素为: %5d",MinValueofArray);
return 0;
}
/*
* 题目描述:
* 从顺序表中删除具有最小值的元素(假设唯一)并由函数返回被删除元素的值。空出的位置由最后一个元素填补
*
* 算法思想:搜索整个顺序表,查找最小值元素并记录其位置和value,搜索结束后用最后一个元素填补空出的原最小值元素的位置
*/
int Delete_Min(int* array, int length)
{
if (length == 0) {
return -1;
}
int position = 0;//用于记录最小值元素的位置
int MinValue = array[position];//假设第一个元素最小
for (int i = 0; i < length - 1; i++) {
if (array[i] < MinValue) {
MinValue = array[i]; //更新最小值
position = i;
}
}
array[position] = array[length - 1]; //用最后一个元素填补空出的原最小值元素的位置
length = length - 1;//更新顺序表的长度
return MinValue;
}
(4)总结
没难度!!
5. 顺序表05(2023/08/22)
(1)题目描述
给定一个有n个元素的整数数组b,其中的连续的想等元素构成的子序列称为平台,设计一个算法求出b中最长平台的长度
(2)算法思想
从头扫描数组,用mlen保存b中最长平台的长度(初始值为1),冷保存当前平台的长度(初始值为1),当mlen<len时更新mlen的值,最后返回mlen
(3)代码示例
#include <stdio.h>
int Maxlength(int array[], int length);//函数声明
int main()
{
int array[] = { 5,5,6,5,9,6,6,3,6,6,6,6,6,8,8,9,1,4,7,2,2,3 };
int length = sizeof(array) / sizeof(array[0]);//求出数组的长度
printf("array[]数组的元素为:\n");
for (int i = 0; i < length; i++) {
printf("%5d", array[i]);
}
int Maxlen = Maxlength(array, length);
printf("\n array[]数组中最长平台的长度为: %5d", Maxlen);
return 0;
}
/*
* 题目描述:
* 给定一个有n个元素的整数数组b,其中的连续的想等元素构成的子序列称为平台,设计一个算法求出b中最长平台的长度
* 算法思想:
* 从头扫描数组,用mlen保存b中最长平台的长度(初始值为1),冷保存当前平台的长度(初始值为1),当mlen<len时更新mlen的值,最后返回mlen
*/
int Maxlength(int array[], int length) //值传递
{
int mlen = 1; //记录最长平台的长度,初始化为1
int curlen = 1; //记录当前平台的长度
int start = 0; //记录平台的第一个元素的下标
for (int i = 0; i < length; i++) {
if (array[i] == array[start]) {
curlen++;
}
else {
if (curlen > mlen) {
mlen = curlen;
}
//无论curlen 和 mlen比较结果如何都要重设start,并重置curlen
start = i;
curlen = 1;//重置当前平台长度
}
}
return mlen;
}
(4)总结
其实题目实质就是让你找出数组中连续元素相等的最长长度!
6. 顺序表06(2023/08/23)
(1)题目描述
已知长度为n的线性表L才用顺序存储结构,编写一个时间复杂度为O(n),空间复杂度为O(1)的算法,该算法删除线性表中所有值为x的数据元素
(2) 算法思想
从头扫描顺序表,如果遇到非x元素则往前挪,相等则跳过,实质就是用非x元素来按序填补已删除的x元素。没有增加额外的辅助空间,空间复杂度为O(1)。
(3)代码示例
#include <stdio.h>
void del_x(int* array, int x, int* length);//函数声明
int main()
{
int array[] = { 32,61,15,54,9,54,35,63,78,96,54 };
int length = sizeof(array) / sizeof(array[0]);
printf("顺序表array中原有的元素为:\n");
for (int i = 0; i < length; i++) {
printf("%5d", array[i]);
}
printf("\n请输入要删除的元素:");
int x; //要删除的元素
scanf_s("%d", &x); //用户输入
del_x(array, x, &length);
printf("\n删除元素 %3d 后顺序表array中的元素为:\n", x);
for (int i = 0; i < length; i++) {
printf("%5d", array[i]);
}
return 0;
}
/*
* 题目描述:
* 已知长度为n的线性表L才用顺序存储结构,编写一个时间复杂度为O(n),空间复杂度为O(1)的算法,该算法删除线性表中所有值为x的数据元素
* 算法思想:
* 从头扫描顺序表,如果遇到非x元素则往前挪,相等则跳过,实质就是用非x元素来按序填补已删除的x元素。
*/
void del_x(int* array, int x, int* length)
{
int len = 0; //记录顺序表中元素值不等x的个数,也就是删除x后顺表的长度
for (int i = 0; i < *length; i++) {
if (array[i] != x) {
array[len] = array[i]; //如果当前元素不等于要删除的元素,则往前挪,即非x的元素将x元素覆盖,在原顺序表上操作,未增加辅助空间
len++;
}
}
*length = len; //更新顺序表的长度
}
(4)总结
要注意的是题目要求空间复杂度为O(1),所以不能在额外空间来辅助,只能在原顺序表中操作。其实只需从前往后扫描顺序表,将元素一个一个挪,如果遇到了要删除的元素就不挪,用后面的非要求删除的元素覆盖即可!
7. 顺序表07(2023/08/23)
(1)题目描述
设计一个算法,从一给定的顺序表L中删除元素值在x到y(x<=y)之间的所有元素,要求以较高的效率来实现,空间复杂度为O(1)
(2)算法思想
从头扫描顺序表,如果遇到不是在x-y之间的元素,则往前挪,是的话则跳过,实质就是用不在x-y之间的元素来按序填补已删除的x-y之间的元素。没有增加额外的辅助空间,空间复杂度为O(1)。
(3)代码示例
#include <stdio.h>
void del_xy(int* array, int x, int y, int* length);//函数声明
int main()
{
int array[] = { 32,61,15,54,9,54,35,63,78,96,54 };
int length = sizeof(array) / sizeof(array[0]);
printf("顺序表array中原有的元素为:\n");
for (int i = 0; i < length; i++) {
printf("%5d", array[i]);
}
printf("\n请输入要删除的元素:");
int x,y; //要删除的元素
scanf_s("%d %d", &x,&y); //用户输入
del_xy(array, x,y, &length);
printf("\n删除元素值在%3d -- %3d 之间的元素后顺序表array中的元素为:\n", x,y);
for (int i = 0; i < length; i++) {
printf("%5d", array[i]);
}
return 0;
}
/*
* 题目描述:
* 设计一个算法,从一给定的顺序表L中删除元素值在x到y(x<=y)之间的所有元素,要求以较高的效率来实现,空间复杂度为O(1)
* 算法思想:
* 从头扫描顺序表,如果遇到不是在x-y之间的元素,则往前挪,是的话则跳过,实质就是用不在x-y之间的元素来按序填补已删除的x-y之间的元素。没有增加额外的辅助空间,空间复杂度为O(1)。
*/
void del_xy(int* array, int x, int y, int* length) {
int len = 0;
for (int i = 0; i < *length; i++) {
if (array[i] <= x || array[i] >= y) {
array[len] = array[i];
len++;
}
}
*length = len;//更新数组的长度
}
(4)总结
这个题和上一题(顺序表06)的思路一样,只是此题是要求删除一个范围类的元素,但两个题目的实质没有区别!
8. 顺序表08(2023/08/24)
(1)题目描述
设将n(n>1)个整数存放在一维数组array中。试着设计一个在时间复杂度和空间复杂度都尽可能高效的算法,将array中保存的序列循环左移p(0<p<n)个位置,即将array中的数据由(x0,x1,……,x n-1)变换为(xp,xp+1,……,x0,x1,……,xp-1)。
(2)算法思想
【举栗:(3,6,7,8,5,2,1)】
1. 先将整个数组逆置 --> (1,2,5,8,7,6,3)
2. 将后p个数逆置 --> (1,2,5,8,3,6,7)
3. 将前n-p个数逆置 --> (8,5,2,1,3,6,7)
注:第二个步骤和第三个步骤先后顺序不影响最后结果
(3)代码示例
#include <stdio.h>
void LeftShift(int* array, int left, int right); //函数声明
void Reserve(int* array, int left, int right);
int main()
{
int array[] = { 13,26,69,85,98,27,31,45,63,29 };
int length = sizeof(array) / sizeof(array[0]);//求出数组的长度
printf("循环左移前数组中的元素位置为:\n");
for (int i = 0; i < length; i++) {
printf("%5d", array[i]);
}
LeftShift(array, 0, length - 1);
printf("\n循环左移p位后数组中的元素位置为:\n");
for (int i = 0; i < length; i++) {
printf("%5d", array[i]);
}
return 0;
}
/*
* (2010年统考真题)
* 题目描述:
* 设将n(n>1)个整数存放在一维数组array中。试着设计一个在时间复杂度和空间复杂度都尽可能高效的算法,将array中保存的序列循环左移p(0<p<n)个位置,
* 即将array中的数据由(x0,x1,……,x n-1)变换为(xp,xp+1,……,x0,x1,……,xp-1)。
*
* 举栗:(3,6,7,8,5,2,1) 循环左移3位
*
* 算法思想:
* 1. 先将整个数组逆置 --> (1,2,5,8,7,6,3)
* 2. 将后p个数逆置 --> (1,2,5,8,3,6,7)
* 3. 将前n-p个数逆置 --> (8,5,2,1,3,6,7)
* 注:第二个步骤和第三个步骤先后顺序不影响最后结果
*
* 实质:循环左移p位,也就是将数组中的前p个数按原有顺序挪到数组的后p个位置去,所以我们可以先全部逆置,然后再分别逆置回来即可
*/
//循环左移
void LeftShift(int* array, int left, int right) {
int p;
printf("\n请输入循环左移多少p位:");
scanf_s("%d", &p);
if(p > 0 && p < right + 1) {
Reserve(array, left, right); //逆置整个数组 (对应步骤 1)
Reserve(array, left, right - p); //逆置数组后p个元素 (对应步骤 2)
Reserve(array, right - p + 1, right); //逆置数组前n-p个元素 (对应步骤 3)
}
}
//逆置数组
void Reserve(int* array, int left, int right){
int tmp;
while (left < right) {
tmp = array[left];
array[left] = array[right];
array[right] = tmp;
left++;
right--;
}
}
(4)总结
1. 算法时间复杂度为O(n) ( O(p)+O(n-p)+O(n) );
2. 算法空间复杂度为O(1) (在原数组上操作未增加辅助空间,LeftShift()定义几个变量不影响);
3. 算法思路简单明了挺好理解的。
(呜呜呜!😭今天出六级成绩,俺没过!!!很亏贼!)
9. 链表09(2023/08/25)
(1)题目描述
将两个递增的有序链表合并为一个递增的有序链表。要求结果链表仍使用原来链表的存储空间,不另外占用其他的存储空间。表中不允许有重复元素数据出现。
注: 1. 题目要求不允许有重复元素;
2. 题目要求不可另外占用其他存储空间;
3. 题目给的是两个递增有序链表,所以单个链表中不会出现重复的元素。
(2)算法思想
一个链表的头结点所占空间),操作指针比较两个链表中结点数据域的值的大小,将小的先链接到LC中,如果相等则选取其中一个,释放另一个
(3)代码示例
#include <stdio.h>
#include <stdlib.h>
//malloc包含在<stdlib.h>头文件中
/*
* 在定义结构体类型时,
* 1. 不加typedef:相当于定义了结构体LNode和LNode的两个变量LNode,*LinkList;
* 2. 加上typedef:除了定义结构体LNode,又给该结构体起了别名LNode和*LinkList;LNode和*LinkList位置的标识符就都是别名而不是变量名,
* LNode和*LinkList也可以像类型一样用来定义结构体变量
*/
//定义单链表结点类型
typedef struct LNode {
int data; //数据域
struct LNode* next; //指针域
}LNode, * LinkList;
LinkList List_TailInsert(LinkList L);//函数声明
void MergeList(LinkList LA, LinkList LB, LinkList* LC); //地址传递
//LinkList MergeList(LinkList LA, LinkList LB, LinkList LC); //值传递
int main()
{
LinkList LA = (LinkList)malloc(sizeof(LNode));//创建链表LA头结点
LA->next = NULL;
LinkList LB = (LinkList)malloc(sizeof(LNode)); //创建链表LB头结点
LB->next = NULL;
LinkList LC = (LinkList)malloc(sizeof(LNode));//创建链表LC,作为结果链表的头结点
LC->next = NULL;
printf("===============请输入LA链表的数据值(以-1为结束标志):\n");
LinkList pa = List_TailInsert(LA);
/*while ((pa->next) != NULL) { //循环输出LA
printf("%5d", *(pa->next));
pa = pa->next;
}*/
printf("\n==============请输入LB链表的数据值(以-1为结束标志):\n");
LinkList pb = List_TailInsert(LB);
/*while ((pb->next) != NULL) { //循环输出LB
printf("%5d", *(pb->next));
pb = pb->next;
}*/
MergeList(LA, LB, &LC); //地址传递
LinkList pc = LC;
//LinkList pc = MergeList(LA, LB, LC); //值传递
if (pc->next != NULL) {
printf("\n\n===============LA和LB合并后的结果为==============\n");
while ((pc->next) != NULL) { //循环输出结果链表LC
printf("%5d", *(pc->next));
pc = pc->next;
}
}
return 0;
}
/*
* 题目描述:
* 将两个递增的有序链表合并为一个递增的有序链表。要求结果链表仍使用原来链表的存储空间,不另外占用其他的存储空间。表中不允许
* 有重复元素数据出现。
* 算法思想:
* 定义三个结点指针pa,pb,pc分别指向LA,LB,LC(结果链表),在两个链表随意选取一个链表的头结点作为结果链表的头结点(合并完之后释放另
* 一个链表的头结点所占空间),操作指针比较两个链表中结点数据域的值的大小,将小的先链接到LC中,如果相等则选取其中一个,释放另一个
*/
/*
* 此处没有以返回值的形式进行LC的传递,采用了地址传递的方式,即调用函数改变值就是实际上改变内存中的值
* 由于实参是一个一级指针的地址,要传入这样的地址给形参,这需要一个对应类型的二级指针来接受一级指针的地址
* 当然也可以采用传值的方式来传递,不过就需要返回地址,否则无法得知修改后的结果
*/
//合并链表
void MergeList(LinkList LA, LinkList LB, LinkList* LC) {
if (LA->next == NULL && LB->next != NULL) {
*LC = LB;
}
else if (LA->next != NULL && LB->next == NULL) {
*LC = LA;
}
else if (LA->next == NULL && LB->next == NULL) {
printf("LA和LB都为空!!!");
//return LC;
}
else {
LinkList pa = LA->next; //pa指针指向LA的第一个结点
LinkList pb = LB->next; //pb指针指向LB的第一个节点
LinkList pc = *LC = LA;//LA的头结点作为结果链表的头结点
LinkList tmp;
while (pa && pb) { //当前pa和pb都未指向空时,比较pa,pb的data
if (pa->data < pb->data) { //LA的元素值小于LB的元素
pc->next = pa; //将pa链接到LC
pc = pa; //pc++; //pc指针后移一位,指向当前LC末尾节点
pa = pa->next; //pa指针后移
}
else if (pa->data > pb->data) { //LA的元素值大于LB的元素
pc->next = pb; //将pb链接到LC
pc = pb; //pc++ //pc指针后移一位,指向当前LC末尾节点
pb = pb->next; //pb指针后移
}
else { //此时pa->data = pb->data
pc->next = pa; //我们将pa链接到LC(当然也可以选择将pb,逻辑同理)
pc = pa; //pc++ //pc指针后移一位,指向当前LC末尾节点
pa = pa->next; //pa后移
tmp = pb->next; //将重复元素赋值给临时指针一遍释放内存(malloc申请的是堆区内存空间,需要程序员手动释放)
pb = tmp->next; //pb后移两位
free(tmp); //删除重复元素(题目要求表中不能有重复数据)
}
}
pc->next = pa ? pa : pb; //如果pc指针的下一个结点是pa指针指向的结点,说明循环是因为pc指针为空才跳出,否则,就是因为pa为空跳出
free(LB);//释放LB头结点
//return LC;
}
}
//创建链表(这里采用尾插法)
LinkList List_TailInsert(LinkList L) {
int x;
//L = (LinkList)malloc(sizeof(LNode)); //创建链表头结点
LinkList s, r = L;//s为头指针,r为尾指针
scanf_s("%d", &x); //从键盘输入结点的value
while (x != -1) { //输入-1代表结束当前链表的创建
s = (LinkList)malloc(sizeof(LNode));
s->data = x;
r->next = s;
r = s;
scanf_s("%d", &x); //从键盘输入结点的value
}
r->next = NULL;
return L;
}
注: 1. 此代码的合并函数采用的是传地址的形式,因为传的是一级指针的地址,所以在形参接收实参传来的地址时需要用二级指针来接收(也可以采用值传递的方式,但是需要返回链表的头结点);
2. 在定义结构体时需要注意(见代码注释)
(4)总结
(1)从链表开始难度上了一个等级,一个是模拟数据的输入输出,二个是真正的指针的操作(稍有不注意,指针这家伙就会惹出大麻烦);
(2)对于结构体的定义和使用需要熟练掌握;(此代码此处需要注意的点见代码注释)
(3)一般来说,
(这张图引荐于某大佬的文章)
加油!🙉
10. 链表01(2023/08/26)
(1)题目描述
将两个非递减的有序表合并为一个非递增的有序表。要求结果链表仍然使用原来两个链表的存储空间,不占用另外的存储空间。表中允许有重复的数据。
(2)算法思想
与上一道题相差不大,主要两点区别:
1. 为保证新表与原表顺序相反,这里需要用到头插法来建立单链表;
2. 当一个表到达表尾结点时,另一个非空表的剩余元素要依次摘取,依次链接到LC表的表头结点之后,才能使得新表与原表顺序相反。
(3)代码示例
#include <stdio.h>
#include <stdlib.h>
//定义单链表结点类型
typedef struct LNode {
int data; //数据域
struct LNode* next; //指针域
}LNode, * LinkList;
LinkList List_TailInsert(LinkList L);//函数声明
void MergeList(LinkList LA, LinkList LB, LinkList* LC);
int main()
{
LinkList LA = (LinkList)malloc(sizeof(LNode));//创建链表LA头结点
LA->next = NULL;
LinkList LB = (LinkList)malloc(sizeof(LNode)); //创建链表LB头结点
LB->next = NULL;
LinkList LC = (LinkList)malloc(sizeof(LNode));//创建链表LC,作为结果链表的头结点
LC->next = NULL; //初始化LC为空
printf("===============请输入LA链表的数据值(以-1为结束标志):\n");
LinkList pa = List_TailInsert(LA);
//while ((pa->next) != NULL) { //循环输出LA
// printf("%5d", *(pa->next));
// pa = pa->next;
//}
printf("\n==============请输入LB链表的数据值(以-1为结束标志):\n");
LinkList pb = List_TailInsert(LB);
/*while ((pb->next) != NULL) { //循环输出LB
printf("%5d", *(pb->next));
pb = pb->next;
}*/
MergeList(LA, LB, &LC); //地址传递
LinkList pc = LC;
//LinkList pc = MergeList(LA, LB, LC); //值传递
if (pc->next != NULL) {
printf("\n\n===============LA和LB合并后的结果为==============\n");
while ((pc->next) != NULL) { //循环输出结果链表LC
printf("%5d", *(pc->next));
pc = pc->next;
}
}
return 0;
}
/*
* 题目描述:
* 将两个非递减的有序表合并为一个非递增的有序表。要求结果链表仍然使用原来两个链表的存储空间,不占用另外的存储空间。表中允许有重复的数据。
* 算法思想:
* 与上一道题相差不大,主要两点区别:
* 1. 为保证新表与原表顺序相反,这里需要用到头插法来建立单链表;
* 2. 当一个表到达表尾结点时,另一个非空表的剩余元素要依次摘取,依次链接到LC表的表头结点之后,才能使得新表与原表顺序相反。
*/
//将两个非递减有序表合并成一个非递增有序表
void MergeList(LinkList LA, LinkList LB, LinkList* LC) {
LinkList pa = LA->next;
LinkList pb = LB->next;
LinkList q = *LC = LA; //把LA头结点作为结果链表头结点,同时定义q指针用于指向待摘取结点(相当于头插法中指向将要插入结点的s指针)
(*LC)->next = NULL; //LA->next = NULL; //要合并先断开(这里一定要记得断开,否则导致结果链表最后一个元素不会指向空,而是指向了自己,在打印时导致死循环)
while (pa || pb) { //只要pa或者pb有一个不为空则都依次摘取结点连接到LC
if (pa == NULL && pb != NULL) { //摘取LB的结点依次链接到LC
q = pb;
pb = pb->next;
}
else if (pb == NULL && pa != NULL) { //摘取LA中的结点依次连接到LC
q = pa;
pa = pa->next;
}
else if (pa->data <= pb->data) { //(此处说明LA,LB都不为空,则)摘取较小的链接到LC
q = pa;
pa = pa->next;
}
else {
q = pb;
pb = pb->next;
}
//采用头插法,这样便将小的放到了链表的末尾,实现了非递增
q->next = (*LC)->next; //这里必须给*L加括号,因为结构体运算符->的优先级高于*
(*LC)->next = q;
}
free(LB); //释放LB头结点占用的内存空间
}
//创建链表(这里采用尾插法)
LinkList List_TailInsert(LinkList L) {
int x;
LinkList s, r = L;//s为头指针,r为尾指针
scanf_s("%d", &x); //从键盘输入结点的value
while (x != -1) { //输入-1代表结束当前链表的创建
s = (LinkList)malloc(sizeof(LNode));
s->data = x;
r->next = s;
r = s;
scanf_s("%d", &x); //从键盘输入结点的value
}
r->next = NULL;
return L;
}
这里把头插法创建链表的代码也给出:
//头插法读入数据的顺序和生成的链表中的元素的顺序是相反的
//创建链表(头插法)
LinkList List_TailInsert(LinkList L) {
int x;
L->next = NULL; //初始为空列表
LNode* s;
scanf_s("%d", &x); //从键盘输入结点的value
while (x != -1) { //输入-1代表结束当前链表的创建
s = (LNode*)malloc(sizeof(LNode));
s->data = x;
s->next = L->next;
L->next = s;
scanf_s("%d", &x); //从键盘输入结点的value
}
return L;
}
注:
头插法读入数据的顺序和生成的链表中的元素的顺序是相反的(这个题恰好就用到了这一特点)
(4) 总结
1. 这里需要注意的地方就是新表顺序(非递增)要与原表顺序(非递减)相反,采用头插法;
2. 在合并的函数里,把LA链表的头结点作为目标链表头结点,不记得将头结点与原链表断开,导致我在主函数中循环打印时出现了死循环(打印完结果链表的元素还在一直打印最后一个元素),因为在合并之前没有断开的话就会导致最后一个元素自己指向自己)如下:
(真的一不注意结果就会很离谱!!!)
3. 正常结果如下:
11. 链表03 (2023/0827)
(1)题目描述
已知两个链表LA和LB分别为两个集合,其元素递增排列。请设计一个算法,用于求出LA与LB的交集,并存放在LA链表中
(2)算法思想
算法思想:
对两个链表的元素一次作比较,若相等,则链接到结果集中,若不等,分两种情况,
1. (当前作比较元素)LA中的小,则删除LA中此元素(LB中往后再找不出与此元素值相等的元素,两个链表都是有序且递增)
2. LB中的较小,删除LB中当前元素,LA中往后不可能找得出与该元素相等的元素
注:这里不仅仅只是把相等的元素找出来链接到结果集中就完事了,不符合要求的结点必须free。因为每一个结点都是由malloc函数在堆区分配的空间,系统不会自动释放,需要程序员手动分配手动释放,以免造成内存泄漏
(3)代码示例
#include <stdio.h>
#include <stdlib.h>
typedef struct LNode {
int data;
struct LNode* next;
}LNode, * LinkList;
LinkList List_TrailInsert(LinkList L);
void Intersection(LinkList LA, LinkList LB, LinkList* LC);
int main()
{
LinkList LA = (LinkList)malloc(sizeof(LNode));
LA->next = NULL;
LinkList LB = (LinkList)malloc(sizeof(LNode));
LB->next = NULL;
LinkList LC = (LinkList)malloc(sizeof(LNode));
LC->next = NULL;
printf("===============请输入LA链表的数据值(以-1为结束标志):\n");
LinkList pa = List_TrailInsert(LA);
/*while ((pa->next) != NULL) {
printf("%5d", *(pa->next));
pa = pa->next;
}*/
printf("\n===============请输入LB链表的数据值(以-1为结束标志):\n");
LinkList pb = List_TrailInsert(LB);
Intersection(LA, LB, &LC);
LNode* pc = LC->next;
if (pc) {
printf("\n\n==============LA和LB交集为======================\n");
while (pc) {
printf("%5d", pc->data);
pc = pc->next;
}
}
return 0;
}
/*
* 题目描述:
* 已知两个链表LA和LB分别为两个集合,其元素递增排列。请设计一个算法,用于求出LA与LB的交集,并存放在LA链表中
*
* 算法思想:
* 对两个链表的元素一次作比较,若相等,则链接到结果集中,若不等,分两种情况,
* 1. (当前作比较元素)LA中的小,则删除LA中此元素(LB中往后再找不出与此元素值相等的元素,两个链表都是有序且递增)
* 2. LB中的较小,删除LB中当前元素,LA中往后不可能找得出与该元素相等的元素
*/
void Intersection(LinkList LA, LinkList LB, LinkList* LC) {
LNode* pa = LA->next;
LNode* pb = LB->next;
LNode* pc = *LC = LA;
LNode* tmp;
//(*LC)->next = NULL; //LA->next = NULL; //其实这里不断开也没关系,如果采用头插法就必须断开,否则会出错(见题链表02)
if ( pa == NULL) {
*LC = LA;
}
else {
while (pa && pb) {
if (pa->data == pb->data) { //相等,交集,将该元素链接到结果集LC
pc->next = pa;
pc = pa; //pc指针后移
pa = pa->next; //pa指针后移
tmp = pb;
pb = pb->next; //pb指针后移
free(tmp);
}
else if (pa->data < pb->data) { //当前LA中作比较元素小于LB中作比较元素,因为两个链表都是有序且递增,所以LB中不可能再出现与LA相等元素,故删除LA中较小的元素,指针后移继续比较
tmp = pa;
pa = pa->next;
free(tmp);
}
else { //当前LB中作比较元素小于LA中当前作比较元素,删除LB中较小元素,指针后移,继续比较
tmp = pb;
pb = pb->next;
free(tmp);
}
}
while (pa) { //依次删除LA中剩下的所有元素
tmp = pa;
pa = pa->next;
free(tmp);
}
while (pb) { //依次删除LB中剩下的所有元素
tmp = pb;
pb = pb->next;
free(tmp);
}
pc->next = NULL;
free(LB);
}
}
LinkList List_TrailInsert(LinkList L) {
int data;
LNode* r = L; //尾指针
scanf_s("%d", &data);
while (data != -1) {
LNode* s = (LNode*)malloc(sizeof(LNode)); //结点
s->data = data;
r->next = s;
r = s;
scanf_s("%d", &data);
}
r->next = NULL;
return L;
}
(4)总结
1. 审题要仔细;
2. 在写代码前可以画一下图解,这样可以很大程度避免出错,比如指针不记得后移(致命的错误,反正指针这家伙!你懂的!)
3. 正常打印结果
12. 链表04(2023/08/27)
(1)题目描述
一直链表LA和LB分别表示两个集合,其元素递增排列。请设计两个算法求出两个集合LA和LB的差集,并且以同样的形式存储, 同时返回该集合元素的个数。
(2)算法思想
使用LA作为结果表的表头,对照集合LB操作集合LA,把LA中与LB中相等的元素删除即可,同时释放掉不符合要求的元素。
注:这里在最后不需要将LA表中的剩余元素free(剩余的元素应属于差集),所以最后不要把指向结果集的指针(pc)指向了NULL,否则就会错过LA中剩余的元素。其次,同样将非差集中的元素所占内存free。
(3)代码示例
#include <stdio.h>
#include <stdlib.h>
typedef struct LNode {
int data;
struct LNode* next;
}LNode, * LinkList;
//函数声明
LinkList List_TrailInsert(LinkList L);
void Difference(LinkList LA, LinkList LB, LinkList* LC, int* count);
int main()
{
int count = 0;
LinkList LA = (LinkList)malloc(sizeof(LNode));
LA->next = NULL;
LinkList LB = (LinkList)malloc(sizeof(LNode));
LB->next = NULL;
LinkList LC = (LinkList)malloc(sizeof(LNode));
LC->next = NULL;
printf("===============请输入LA链表的数据值(以-1为结束标志):\n");
LinkList pa = List_TrailInsert(LA);
/*while ((pa->next) != NULL) {
printf("%5d", *(pa->next));
pa = pa->next;
}*/
printf("\n===============请输入LB链表的数据值(以-1为结束标志):\n");
LinkList pb = List_TrailInsert(LB);
Difference(LA, LB, &LC,&count);
LNode* pc = LC->next;
if (pc) {
printf("\n\n==============LA和LB差集为======================\n");
while (pc) {
printf("%5d", pc->data);
pc = pc->next;
}
printf("\n\n=======差集的元素个数为:%5d\n", count);
}
return 0;
}
/*
* 题目描述:一直链表LA和LB分别表示两个集合,其元素递增排列。请设计两个算法求出两个集合LA和LB的差集,并且以同样的形式存储,
* 同时返回该集合元素的个数。
* 算法思想:使用LA作为结果表的表头,对照集合LB操作集合LA,把LA中与LB中相等的元素删除即可,同时释放掉不符合要求的元素。
*/
void Difference(LinkList LA, LinkList LB, LinkList* LC, int* count) {
LNode* pa = LA->next;
LNode* pb = LB->next;
LNode* pc = *LC = LA;
LNode* tmp;
if (!pb) {
*LC = LA;
}
else {
while (pa && pb) {
if (pa->data == pb->data) { //pc指针不动,pa、pb指针后移,删除两个集合中相等的元素
tmp = pa;
pc->next = pa->next;
pa = pa->next;
free(tmp);
tmp = pb;
pb = pb->next;
free(tmp);
}
else if (pa->data < pb->data) { //把pa中的元素链接到结果集,pa指针后移,pb指针不动
pc = pa;
*count++;
pa = pa->next;
}
else { // 删除当前pb指向的作比较元素,pb后移,pa不动
tmp = pb;
pb = pb->next;
free(tmp);
}
}
while (pb) { //删除 pb中所有的元素,释放空间
tmp = pb;
pb = pb->next;
free(tmp);
}
//pc->next = NULL;//这里千万不要多此一举
free(LB);
}
}
LinkList List_TrailInsert(LinkList L) {
int data;
LNode* r = L; //尾指针
scanf_s("%d", &data);
while (data != -1) {
LNode* s = (LNode*)malloc(sizeof(LNode)); //结点
s->data = data;
r->next = s;
r = s;
scanf_s("%d", &data);
}
r->next = NULL;
return L;
}
(4)总结
1. 理清逻辑;
2. 慎用指针。
3. 加油💪
4. 正常运算结果打印
13. 链表05(2023/08/28)
(1)题目描述
试编写在带头结点的单链表L中删除一个最小值结点的高效率算法(假设最小值结点唯一)。
(2)算法思想
1. 从指针从头至尾扫描单链表,需要记录最小值(待删除)结点的前驱结点;
2. 三个指针,pre记录当前已扫描结点中最小值的前驱结点,pa用于扫描单链表,minp用于记录扫描到的结点的前驱结点;
3. 待整个链表扫描完后,此时pre指向的就是整个单链表最小值结点的前驱结点,用minp最后记录最小值结点,以前删除后释放空间 。
注:在单链表中删除结点的关键是需要记录待删除结点的前驱结点
(3)代码示例
#include <stdio.h>
#include <stdlib.h>
typedef struct LNode {
int data;
struct LNode* next;
}LNode, * LinkList;
//函数声明
LinkList List_TrailInsert(LinkList L);
int Delete_Min(LinkList* L);
int main()
{
LinkList L = (LinkList)malloc(sizeof(LNode));
//L->next = NULL;
printf("===============请输入LA链表的数据值(以-1为结束标志):\n");
LinkList pa = List_TrailInsert(L);
int min = Delete_Min(&L);
LinkList p = L->next;
printf("\n\n==============删除最小值结点 \'%d \' 后链表的元素为======================\n",min);
while (p) {
printf("%5d", p->data);
p = p->next;
}
return 0;
}
/*
* 题目描述:
* 试编写在带头结点的单链表L中删除一个最小值结点的高效率算法(假设最小值结点唯一)
* 算法思想:
* 1. 从指针从头至尾扫描单链表,需要记录最小值(待删除)结点的前驱结点。
* 2. 三个指针,pre记录当前已扫描结点中最小值的前驱结点,pa用于扫描单链表,minp用于记录扫描到的结点的前驱结点
* 3. 待整个链表扫描完后,此时pre指向的就是整个单链表最小值结点的前驱结点,用minp最后记录最小值结点,以前删除后释放空间
*/
//删除最小值结点,同时返回最小值(return)以及删除最小值后的链表(函数传参)
int Delete_Min(LinkList* L) {
LNode* pre = *L; //记录最小结点的前驱节点,初始化为头结点(默认第一个结点最小)
LNode* minp = *L; //指向最小结点的前驱节点,跟随pa指针
LNode* pa = (*L)->next;
int min;
while (pa) {
if (pa->data < pre->next->data) { //找到更小值结点,更新各指针
pre = minp; //更新指向最小值结点前驱结点的指针
min = pa->data; //更新最小值
minp = pa; //更新扫描到的结点的前驱结点指针
pa = pa->next; //pa指针后移
}
else {
minp = pa;
pa = pa->next;
}
}
minp = pre->next; //删除最小值结点
pre->next = pre->next->next;
free(minp);
return min;
}
//尾插法构造链表
LinkList List_TrailInsert(LinkList L) {
int x;
LNode* r = L;
scanf_s("%d", &x);
while (x != -1) {
LNode* s = (LNode*)malloc(sizeof(LNode));
s->data = x;
r->next = s;
r = s;
scanf_s("%d", &x);
}
r->next = NULL;
return L;
}
(4)总结
1. 指针慎用;
2. 在单链表中删除节点一定要记录待删除结点的前驱结点;
3. 正常运算结果打印
14. 链表 06(2023/08/28)
(1)题目描述
在带头结点的单链表L中删除所有值为x的结点,并释放其空间,假设之值为x的结点不唯一,试编写算法实现上述操作。
(2) 算法思想
两种方法:
1. 方法一的思想与链表05的思想一样,记录住要删除结点的前驱结点是关键,只需要改一改条件即可(详见链表05)
2. 方法二相当于采用尾插法重新构建了一个单链表。用p指针扫描L的所有结点,当其值不为x时将其链接到L之后,否则将其释放。
(3)代码示例
#include <stdio.h>
#include <stdlib.h>
typedef struct LNode {
int data;
struct LNode* next;
}LNode, * LinkList;
//函数声明
LinkList List_TrailInsert(LinkList L);
void Delete_X(LinkList* L,int x);
int main()
{
LinkList L = (LinkList)malloc(sizeof(LNode));
//L->next = NULL;
printf("===============请输入LA链表的数据值(以-1为结束标志):\n");
LinkList pa = List_TrailInsert(L);
int value;
printf("========请输入要删除的结点的值:");
scanf_s("%d", &value);
Delete_X(&L,value);
LinkList p = L->next;
printf("\n\n==============删除最小值结点 \'%d \' 后链表的元素为======================\n", value);
while (p) {
printf("%5d", p->data);
p = p->next;
}
return 0;
}
/*
* 题目描述:
* 在带头结点的单链表L中删除所有值为x的结点,并释放其空间,假设之值为x的结点不唯一,试编写算法实现上述操作。
* 算法思想:
* 方法一的思想与链表05的思想一样,记录住要删除结点的前驱结点是关键,只需要改一改条件即可(详见链表05)
* 方法二相当于采用尾插法重新构建了一个单链表。用p指针扫描L的所有结点,当其值不为x时将其链接到L之后,否则将其释放。
*/
//方法一
//删除最小值结点,同时返回最小值(return)以及删除最小值后的链表(函数传参)
//void Delete_X(LinkList* L,int x) {
// LNode* pre = *L; //记录待删除结点的前驱节点,初始化为头结点
// LNode* pa = (*L)->next;
// LNode* tmp;
// while (pa) {
// if (pa->data == x) {
// tmp = pa;
// pa = pa->next;
// pre->next = pa;
// free(tmp);
// }
// else {
// pre = pa;
// pa = pa->next;
// }
// }
//}
/*
* 方法二
* 用p指针扫描单链表所有结点,结点值为x的free,否则链接到L后面,相当于重新构建了一个L单链表
*/
void Delete_X(LinkList* L, int x) {
LNode* r = *L;
LNode* p = (*L)->next;
LNode* tmp;
while (p) {
if ((p->data)!= x) {
r->next = p;
r = p; //这个地方千万要记得更新尾指针,很容易忘记
p = p->next;
}
else {
tmp = p;
p = p->next;
free(tmp);
}
}
r->next = NULL;
}
//尾插法构造链表
LinkList List_TrailInsert(LinkList L) {
int x;
LNode* r = L;
scanf_s("%d", &x);
while (x != -1) {
LNode* s = (LNode*)malloc(sizeof(LNode));
s->data = x;
r->next = s;
r = s;
scanf_s("%d", &x);
}
r->next = NULL;
return L;
}
(4)总结
1. 推荐使用方法二,逻辑清晰,代码简洁,更容易理解!
2. 正常运算结果打印
15. 链表07(2023/08/29)
(1)题目描述
试编写算法将带头结点的单链表就地逆置。所谓“就地”就是辅助空间复杂度为O(1)。
(2)算法思想
将单链表L的头结点与单链表断开,将后继节点利用头插法的方式依次链接到L链表,相当于利用头插法重建L单链表。
关键:利用头插法的特点:读入的元素的顺序与建成表的顺序是相反的。
(3) 代码示例
#include <stdio.h>
#include <stdlib.h>
typedef struct LNode {
int data;
struct LNode* next;
}LNode, * LinkList;
//函数声明
LinkList List_TrailInsert(LinkList L);
void Reverse(LinkList* L);
int main()
{
LinkList L = (LinkList)malloc(sizeof(LNode));
//L->next = NULL;
printf("===============请输入LA链表的数据值(以-1为结束标志):\n");
LinkList pa = List_TrailInsert(L);
while ((pa->next) != NULL) {
printf("%5d", *(pa->next));
pa = pa->next;
}
Reverse(&L);
LinkList p = L->next;
printf("\n\n==============原地逆置后L链表的元素为======================\n");
while (p) {
printf("%5d", p->data);
p = p->next;
}
return 0;
}
/*
* 题目描述:
* 试编写算法将带头结点的单链表就地逆置。所谓“就地”就是辅助空间复杂度为O(1)
*
* 算法思想:
* 将单链表L的头结点与单链表断开,将后继节点利用头插法的方式依次链接到L链表,相当于利用头插法重建L单链表。
*/
//逆置链表
void Reverse(LinkList* L) { //函数传参的形式返回逆置后的L链表
LNode* p = (*L)->next;
LNode* r;
(*L)->next = NULL; //断链
while (p) {
r = p->next; //先保存p的后继结点
p->next = (*L)->next;
(*L)->next = p; //修改头结点,让它指向第一个结点
p = r;
}
}
//尾插法构造链表
LinkList List_TrailInsert(LinkList L) {
int x;
LNode* r = L;
scanf_s("%d", &x);
while (x != -1) {
LNode* s = (LNode*)malloc(sizeof(LNode));
s->data = x;
r->next = s;
r = s;
scanf_s("%d", &x);
}
r->next = NULL;
return L;
}
(4)总结
1. 掌握头插法的特点,灵活运用;
2. 正确运算结果打印
16. 链表08 (2023/08/30)
(1)题目描述
将一个带头结点的单链表LA分解为连个带头结点的单链表LA和LB,使得LA中含有原表中序号为奇数的元素,而LB中含有原表中序号为偶数的元素,且保持其相对顺序不变 。
(2)算法思想
将LA表中的结点按照序号的奇偶性依次摘取,分别链接到LA和LB,保持其顺序不变,都采用尾插法构建两个单链表。
(3)代码示例
#include <stdio.h>
#include <stdlib.h>
typedef struct LNode {
int data;
struct LNode* next;
}LNode, * LinkList;
//函数声明
LinkList List_TrailInsert(LinkList L);
LinkList DisCreate(LinkList* LA);
int main()
{
LinkList L = (LinkList)malloc(sizeof(LNode));
//L->next = NULL;
printf("===============请输入LA链表的数据值(以-1为结束标志):\n");
List_TrailInsert(L);
//LinkList pa = List_TrailInsert(L);
//while ((pa->next) != NULL) {
// printf("%5d", *(pa->next));
// pa = pa->next;
//}
LinkList pb = DisCreate(&L); //函数返回的是LB链表
LinkList pa = L->next;
printf("\n=================将LA单链表按奇偶拆分为两个链表之后:\n");
printf("\n\n==============LA链表(下标奇)的元素为======================\n");
while (pa) {
printf("%5d", pa->data);
pa = pa->next;
}
printf("\n\n==============LB链表(下标偶)的元素为======================\n");
while ((pb->next) != NULL) {
printf("%5d", *(pb->next));
pb = pb->next;
}
return 0;
}
/*
* 题目描述:
* 将一个带头结点的单链表LA分解为连个带头结点的单链表LA和LB,使得LA中含有原表中序号为奇数的元素,而LB中含有原表中序号为偶数的元素,
* 且保持其相对顺序不变
* 算法思想:
* 将LA表中的结点按照序号的奇偶性依次摘取,分别链接到LA和LB,保持其顺序不变,都采用尾插法构建两个单链表。
*/
//return 返回LB,函数传参的方式返回LA
LinkList DisCreate(LinkList* LA) {
LinkList LB = (LinkList)malloc(sizeof(LNode));
LB->next = NULL;
LNode* pa = *LA;
LNode* pb = LB;
LNode* p = (*LA)->next;
(*LA)->next = NULL; //断开LA链表头结点(在完成pa,pb,p等指针的初始化之后才能断开)
int i = 1; //初始化为1
while (p) {
if (i % 2 == 0) { //下标为偶,连接到LB
pb->next = p;
pb = p;
}
else { //下标为奇,链接到LA
pa->next = p;
pa = p;
}
i++; //下标+1
p = p->next; //p指针后移
}
pa->next = NULL;
pb->next = NULL;
return LB;
}
//尾插法构造链表
LinkList List_TrailInsert(LinkList L) {
int x;
LNode* r = L;
scanf_s("%d", &x);
while (x != -1) {
LNode* s = (LNode*)malloc(sizeof(LNode));
s->data = x;
r->next = s;
r = s;
scanf_s("%d", &x);
}
r->next = NULL;
return L;
}
(4)总结
1. 要合并先断链,要分解也要断链(是在以原头结点作为结果链表头结点的基础上,如果另外申请空间重建另说),需要注意的是,断链之前先初始化该初始化的指针;
2. 正确运算结果打印
17. 链表09(2023/08/30)
(1)题目描述
设C={a1,b1,a2,b2,……,an,bn}为线性表,采用头结点LA单链表存放,设计一个就地算法,将其拆分为两个单链表,使得
LA = {a1,a2,a3,……,an}
LB = {bn,……,b3,b2,b1}
(2)算法思想
思想和线性表08(16题)一样,区别在,LA采用尾插法建立,LB采用头插法建立
注:这里我没有给结点编序号,而是依次摘取分别链接,所以,在一个循环中第二次摘取结点时需要再判断一下p是否已经指向了NULL,以免越界
(3)代码示例
#include <stdio.h>
#include <stdlib.h>
typedef struct LNode {
int data;
struct LNode* next;
}LNode, * LinkList;
//函数声明
LinkList List_TrailInsert(LinkList L);
LinkList DisCreate(LinkList* LA);
int main()
{
LinkList L = (LinkList)malloc(sizeof(LNode));
//L->next = NULL;
printf("===============请输入LA链表的数据值(以-1为结束标志):\n");
List_TrailInsert(L);
LinkList pb = DisCreate(&L); //函数返回的是LB链表
LinkList pa = L->next;
printf("\n=================将LA单链表拆分为两个链表之后:\n");
printf("\n\n==============LA链表的元素为======================\n");
while (pa) {
printf("%5d", pa->data);
pa = pa->next;
}
printf("\n\n==============LB链表的元素(与原表顺序相反)为======================\n");
while ((pb->next) != NULL) {
printf("%5d", *(pb->next));
pb = pb->next;
}
return 0;
}
/*
* 题目描述:
* 设C={a1,b1,a2,b2,……,an,bn}为线性表,采用头结点LA单链表存放,设计一个就地算法,将其拆分为两个单链表,使得
* LA = {a1,a2,a3,……,an}
* LB = {bn,……,b3,b2,b1}
* 算法思想:
* 思想和线性表08一样,区别在,LA采用尾插法建立,LB采用头插法建立
*/
//return 返回LB,函数传参的方式返回LA
LinkList DisCreate(LinkList* LA) {
LinkList LB = (LinkList)malloc(sizeof(LNode));
LB->next = NULL;
LNode* pa = *LA;
LNode* pb = LB;
LNode* p = (*LA)->next;
(*LA)->next = NULL; //断开LA链表头结点(在完成pa,pb,p等指针的初始化之后才能断开)
while (p) { //按照先链接到LA(尾插法),再链接到LB(头插法),pa,pb始终指向LA,LB链表的末尾结点
//先链接到LA
pa->next = p;
pa = p; //pa指针后移
p = p->next; //p指针后移
if (p) {
//再链接到LB
pb = p; //pb指针后移
p = p->next; //p指针后移
pb->next = LB->next;
LB->next = pb;
}
}
pa->next = NULL;
return LB;
}
//尾插法构造链表
LinkList List_TrailInsert(LinkList L) {
int x;
LNode* r = L;
scanf_s("%d", &x);
while (x != -1) {
LNode* s = (LNode*)malloc(sizeof(LNode));
s->data = x;
r->next = s;
r = s;
scanf_s("%d", &x);
}
r->next = NULL;
return L;
}
(4)总结
1. 指针在后移的时候也要注意思考是否能够后移,以免越界;
2. 正确运算结果打印
18. 链表10(2023/08/31)
(1)题目描述
设计算法将一个带头结点的单链表LA分解为两个具有相同结构的链表LB和LC,其中LB表的结点为LA表中小于0的结点,而LC表中的结点为LA表中大于0的结点(链表LA中的元素为非零整数,要求LB、LC表利用LA表的结点)
(2)算法思想
扫描LA表,依次摘取结点,按结点值大于小于0的规则依次分别链接到LB、LC表中
注:在构造链表时,这个时候就不能以-1为结束标志了,因为题目要求结点的值是非零整数,所以我们可以以0为结束标志
(3)代码示例
#include <stdio.h>
#include <stdlib.h>
typedef struct LNode {
int data;
struct LNode* next;
}LNode, * LinkList;
//函数声明
LinkList List_TrailInsert(LinkList L);
void Decompose(LinkList* LA, LinkList* LB, LinkList* LC);
int main()
{
LinkList LA = (LinkList)malloc(sizeof(LNode));
LA->next = NULL;
LinkList LB = (LinkList)malloc(sizeof(LNode));
LinkList LC = (LinkList)malloc(sizeof(LNode));
printf("===============请输入LA链表的数据值(以0为结束标志):\n");
List_TrailInsert(LA);
Decompose(&LA, &LB, &LC);
LNode* pb = LB->next;
LNode* pc = LC->next;
printf("\n=================将LA单链表按大于小于0的规则拆分为两个链表之后:\n");
printf("\n\n==============LB链表的元素(<0)为======================\n");
while (pb) {
printf("%5d", pb->data);
pb = pb->next;
}
printf("\n\n==============LC链表的元素(>0)为======================\n");
while (pc) {
printf("%5d", pc->data);
pc = pc->next;
}
return 0;
}
/*
* 题目描述:
* 设计算法将一个带头结点的单链表LA分解为两个具有相同结构的链表LB和LC,其中LB表的结点为LA表中小于0的结点,而LC表中的结点为LA表中大于0的结点
* (链表LA中的元素为非零整数,要求LB、LC表利用LA表的结点)
*
* 算法思想:
* 扫描LA表,依次摘取结点,按结点值大于小于0的规则依次分别链接到LB、LC表中
*/
//return 返回LB,函数传参的方式返回LC
void Decompose(LinkList* LA, LinkList* LB, LinkList* LC) {
LNode* p = (*LA)->next;
(*LA)->next = NULL;
(*LB) = (*LA);
LNode* pb = (*LB);
(*LC) = (LinkList)malloc(sizeof(LNode));
(*LC)->next = NULL;
LNode* pc = (*LC);
while (p) {
if ((p->data) < 0) {
pb->next = p;
pb = p;
p = p->next;
}
else {
pc->next = p;
pc = p;
p = p->next;
}
}
pb->next = NULL;
pc->next = NULL;
}
//尾插法构造链表
LinkList List_TrailInsert(LinkList L) {
int x;
LNode* r = L;
scanf_s("%d", &x);
while (x != 0) {
LNode* s = (LNode*)malloc(sizeof(LNode));
s->data = x;
r->next = s;
r = s;
scanf_s("%d", &x);
}
r->next = NULL;
return L;
}
(4)总结
(1)每次摘取完结点指针一定要后移,指向链表末尾节点的指针也要记得后移;
(2)正确运算结果打印
19. 链表11(2023/08/31)
(1)题目描述
设计一个算法,通过一趟遍历确定长度为n的单链表中值最大的结点,返回该节点的数据域。
(2)算法思想
扫描单链表L,并记录当前已扫描结点中的最大值结点。
(3)代码示例
#include <stdio.h>
#include <stdlib.h>
typedef struct LNode {
int data;
struct LNode* next;
}LNode, * LinkList;
//函数声明
LinkList List_TrailInsert(LinkList L);
int LinkList_MAX(LinkList L);
int main()
{
LinkList L = (LinkList)malloc(sizeof(LNode));
L->next = NULL;
printf("===============请输入L链表的数据值(以-1为结束标志):\n");
List_TrailInsert(L);
int Max = LinkList_MAX(L);
printf("===============L链表中最大值结点为:%5d", Max);
return 0;
}
/*
* 题目描述:
* 设计一个算法,通过一趟遍历确定长度为n的单链表中值最大的结点,返回该节点的数据域
*
* 算法思想:
* 扫描单链表L,并记录当前已扫描结点中的最大值结点
*
*/
//return 返回LB,函数传参的方式返回LC
int LinkList_MAX(LinkList L) {
if (L->next == NULL) {
return NULL;
}
LNode* maxp = L->next; //默认第一个结点值最大
LNode* p = L->next->next;
while (p) {
if (p->data > maxp->data) {
maxp = p;
p = p->next;
}
else {
p = p->next;
}
}
return maxp->data;
}
//尾插法构造链表
LinkList List_TrailInsert(LinkList L) {
int x;
LNode* r = L;
scanf_s("%d", &x);
while (x != -1) {
LNode* s = (LNode*)malloc(sizeof(LNode));
s->data = x;
r->next = s;
r = s;
scanf_s("%d", &x);
}
r->next = NULL;
return L;
}
(4)总结
1. 该题相对简单,基本功;
2. 正确运算结果打印
20. 链表12(2023/09/01)
(1)题目描述
设计一个算法,删除递增带头结点有序表中值大于mink且小于maxk(mink和maxk是给定的连个参数,其值可以和表中的元素相同,也可以不同)的所有元素。
(2)算法思想
通过扫描单链表,找到属于范围类的结点,关键是记录待删除结点的前驱结点,当结点值大于范围最大值的时候,我们就可以停止扫描单链表,节省时间,否则就是任务完成,代码还在继续搜索。
(3)代码示例
#include <stdio.h>
#include <stdlib.h>
typedef struct LNode {
int data;
struct LNode* next;
}LNode, * LinkList;
//函数声明
LinkList List_TrailInsert(LinkList L);
void delete_x_y(LinkList* L, int x, int y);
int main()
{
LinkList L = (LinkList)malloc(sizeof(LNode));
L->next = NULL;
printf("===============请输入L链表的数据值(以-1为结束标志):\n");
List_TrailInsert(L);
int x, y;
printf("=====请输入删除范围x----y:");
scanf_s("%d%d", &x, &y);
delete_x_y(&L, x, y);
printf("\n\n===============删除值在%5d----%d范围内(不包含边界)的结点后,L链表的元素为:=====\n",x,y);
LNode* p = L->next;
while (p) {
printf("%5d", p->data);
p = p->next;
}
return 0;
}
/*
* 题目描述:
* 设计一个算法,删除递增带头结点有序表中值大于mink且小于maxk(mink和maxk是给定的连个参数,其值可以和表中的元素相同,也可以不同)的所有元素。
* 算法思想:
* 通过扫描单链表,找到属于范围类的结点,关键是要记录待删除结点的前驱结点,其次,因为整个链表是有序且递增的所以在结点值大于最大范围值的时候,
* 我们就可以停止扫描单链表,节省时间,否则就是任务完成,代码还在继续搜索
*/
void delete_x_y(LinkList* L, int x, int y) {
LNode* p = (*L)->next;
LNode* pre = *L;
LNode* tmp;
while (p && (p->data) < y) { //当没有搜索到链表末尾,且结点值小于范围最大值时进入循环
if ((p->data) > x && (p->data) < y) {
pre->next = p->next;
tmp = p;
p = p->next;
free(tmp); //删除完结点释放空间
}
else {
pre = p;
p = p->next;
}
}
}
//尾插法构造链表
LinkList List_TrailInsert(LinkList L) {
int x;
LNode* r = L;
scanf_s("%d", &x);
while (x != -1) {
LNode* s = (LNode*)malloc(sizeof(LNode));
s->data = x;
r->next = s;
r = s;
scanf_s("%d", &x);
}
r->next = NULL;
return L;
}
(4)总结
1. 该题值得注意的是,当我们把链表中在删除范围内的结点全部删除后,就可以停止扫描整个单链表(这个是可以预估的,因为整个链表单调有序)
2. 正确运算结果打印
21. 链表 13(2023/09/01)
(1)题目描述
在一个递增有序的的线性表中,有数值相同的元素存在。若存储方式为单链表,设计算法去掉数值相同的元素,使表中不再具有重复元素。
(2)算法思想
扫描单链表,若当前驱结点指针pre指向的结点值等于后继结点值,就删除后继结点,否则指针都后移。
(3)代码示例
#include <stdio.h>
#include <stdlib.h>
typedef struct LNode {
int data;
struct LNode* next;
}LNode, * LinkList;
//函数声明
LinkList List_TrailInsert(LinkList L);
void delete_same(LinkList* L);
int main()
{
LinkList L = (LinkList)malloc(sizeof(LNode));
L->next = NULL;
printf("===============请输入L链表的数据值(以-1为结束标志):\n");
List_TrailInsert(L);//构造单链表
delete_same(&L);
printf("\n\n===============删除相同值的结点后,L链表的元素为:=====\n");
LNode* p = L->next;
while (p) {
printf("%5d", p->data);
p = p->next;
}
return 0;
}
/*
* 题目描述:
* 在一个递增有序的的线性表中,有数值相同的元素存在。若存储方式为单链表,设计算法去掉数值相同的元素,使表中不再具有重复元素
* 算法思想:扫描单链表,若当前驱结点指针pre指向的结点值等于后继结点值,就删除后继结点,否则指针都后移
*/
void delete_same(LinkList* L) {
LNode* p = (*L)->next; //当前结点(相当于前驱结点的后继结点)
LNode* pre = *L; //前驱结点指针
LNode* tmp; //临时指针
while (p) {
if ((pre->data)== (p->data)) {
pre->next = p->next;
tmp = p;
p = p->next;
free(tmp); //删除完结点释放空间
}
else {
pre = p;
p = p->next;
}
}
}
//尾插法构造链表
LinkList List_TrailInsert(LinkList L) {
int x;
LNode* r = L;
scanf_s("%d", &x);
while (x != -1) {
LNode* s = (LNode*)malloc(sizeof(LNode));
s->data = x;
r->next = s;
r = s;
scanf_s("%d", &x);
}
r->next = NULL;
return L;
}
(4)总结
1. 删除一个节点的关键就是记录其前驱结点(在单链表中);
2. 正确运算结果打印
22. 链表 14(2023/09/02)
(1) 题目描述
已知单链表表示的线性表中,含有3类字符的数据元素(字母字符、数字字符和其他字符),试编写算法构造3个以循环链表表示的线性表,使每个表中只含同一类的字符,且利用原表中的节点空间作为这三个表的结点空间,头结点可另辟空间。
(2)算法思想
先创建3个头结点,用p扫描单链表L,将不同类型的数据元素采用头插法(无所谓头插法尾插法,只是头插法相对容易对于循环链表)插入到相应的循环单链表中。
注:升级到循环链表了!
(3)代码示例
#include <stdio.h>
#include <stdlib.h>
typedef struct LNode {
char data;
struct LNode* next;
}LNode, * LinkList;
//函数声明
LinkList List_TrailInsert(LinkList L);
void split(LinkList L, LinkList* LA, LinkList* LB, LinkList* LC);
int main()
{
LinkList L = (LinkList)malloc(sizeof(LNode));
L->next = NULL;
LinkList LA = NULL;
LinkList LB = NULL;
LinkList LC = NULL;
printf("===============请输入L链表的数据值(以-1为结束标志):\n");
List_TrailInsert(L);//构造单链表
split(L, &LA, &LB, &LC);
printf("\n\n===============将L链表中的三种字符元素分类后:=====\n");
LNode* pa = LA->next;
if (pa) {
printf("\n\n=================LA链表(字母):====");
while (pa != LA) {
printf("%5c", pa->data);
pa = pa->next;
}
}
LNode* pb = LB->next;
if (pb) {
printf("\n\n=================LB链表(数字):====");
while (pb != LB) {
printf("%5c", pb->data);
pb = pb->next;
}
}
LNode* pc = LC->next;
if (pc) {
printf("\n\n=================LC链表(其他):====");
while (pc != LC) {
printf("%5c", pc->data);
pc = pc->next;
}
}
return 0;
}
/*
* 题目描述:
* 已知单链表表示的线性表中,含有3类字符的数据元素(字母字符、数字字符和其他字符),试编写算法构造3个以循环链表表示的线性表,
* 使每个表中只含同一类的字符,且利用原表中的节点空间作为这三个表的结点空间,头结点可另辟空间
* 算法思想:
* 先创建3个头结点,用p扫描单链表L,将不同类型的数据元素采用头插法(无所谓头插法尾插法,只是头插法相对容易对于循环链表)
* 插入到相应的循环单链表中。
*/
void split(LinkList L, LinkList* LA, LinkList* LB, LinkList* LC) {
(*LA) = (LinkList)malloc(sizeof(LNode));
(*LA)->next = (*LA);
(*LB) = (LinkList)malloc(sizeof(LNode));
(*LB)->next = (*LB);
(*LC) = (LinkList)malloc(sizeof(LNode));
(*LC)->next = (*LC);
LNode* p = L->next;
LNode* s;
while (p) {
if (p->data >= 'A' && p->data <='Z' || p->data >= 'a' && p->data <= 'z') {
s = p;
p = p->next;
s->next = (*LA)->next;
(*LA)->next = s;
}
else if (p->data >= '0' && p->data <= '9') {
s = p;
p = p->next;
s->next = (*LB)->next;
(*LB)->next = s;
}
else {
s = p;
p = p->next;
s->next = (*LC)->next;
(*LC)->next = s;
}
}
}
//尾插法构造链表
LinkList List_TrailInsert(LinkList L) {
char x;
LNode* r = L;
scanf_s("%c", &x);
while (x != '#') {
LNode* s = (LNode*)malloc(sizeof(LNode));
s->data = x;
r->next = s;
r = s;
scanf_s("%c", &x);
}
r->next = NULL;
return L;
}
上面这种是使用了二级指针,因为头结点在函数中创建了,如果在主函数中创建传入,使用一级指针真没问题,(暂时还没弄懂原因,等研究明白后一定马上写出来)如下:
#include <stdio.h>
#include <stdlib.h>
typedef struct LNode {
char data;
struct LNode* next;
}LNode, * LinkList;
//函数声明
LinkList List_TrailInsert(LinkList L);
void split(LinkList L, LinkList LA, LinkList LB, LinkList LC);
int main()
{
LinkList L = (LinkList)malloc(sizeof(LNode));
L->next = NULL;
LinkList LA = (LinkList)malloc(sizeof(LNode));
LinkList LB = (LinkList)malloc(sizeof(LNode));
LinkList LC = (LinkList)malloc(sizeof(LNode));
LA->next = LA;
LB->next = LB;
LC->next = LC;
printf("===============请输入L链表的数据值(以-1为结束标志):\n");
List_TrailInsert(L);//构造单链表
split(L, LA, LB, LC);
printf("\n\n===============将L链表中的三种字符元素分类后:=====\n");
LNode* pa = LA->next;
if (pa) {
printf("\n\n=================LA链表(字母):====");
while (pa != LA) {
printf("%5c", pa->data);
pa = pa->next;
}
}
LNode* pb = LB->next;
if (pb) {
printf("\n\n=================LB链表(数字):====");
while (pb != LB) {
printf("%5c", pb->data);
pb = pb->next;
}
}
LNode* pc = LC->next;
if (pc) {
printf("\n\n=================LC链表(其他):====");
while (pc != LC) {
printf("%5c", pc->data);
pc = pc->next;
}
}
return 0;
}
/*
* 题目描述:
* 已知单链表表示的线性表中,含有3类字符的数据元素(字母字符、数字字符和其他字符),试编写算法构造3个以循环链表表示的线性表,
* 使每个表中只含同一类的字符,且利用原表中的节点空间作为这三个表的结点空间,头结点可另辟空间
* 算法思想:
* 先创建3个头结点,用p扫描单链表L,将不同类型的数据元素采用头插法(无所谓头插法尾插法,只是头插法相对容易对于循环链表)
* 插入到相应的循环单链表中。
*/
void split(LinkList L, LinkList LA, LinkList LB, LinkList LC) {
LNode* p = L->next;
LNode* s;
while (p) {
if (p->data >= 'A' && p->data <= 'Z' || p->data >= 'a' && p->data <= 'z') {
s = p;
p = p->next;
s->next = (LA)->next;
(LA)->next = s;
}
else if (p->data >= '0' && p->data <= '9') {
s = p;
p = p->next;
s->next = (LB)->next;
(LB)->next = s;
}
else {
s = p;
p = p->next;
s->next = (LC)->next;
(LC)->next = s;
}
}
}
//尾插法构造链表
LinkList List_TrailInsert(LinkList L) {
char x;
LNode* r = L;
scanf_s("%c", &x);
while (x != '#') {
LNode* s = (LNode*)malloc(sizeof(LNode));
s->data = x;
r->next = s;
r = s;
scanf_s("%c", &x);
}
r->next = NULL;
return L;
}
(4) 总结
1. 由于这次构造的链表的数据域所存储的是字符,以字符形式从键盘读入,而字符形式读入会将空格(输入间隔符)也当作字符读入,所以在输入的时候空格也会作为字符读入到链表;
2. 正确运算结果打印
23. 链表15(2023/09/02)
(1)题目描述
设有一个带头结点的双链表h,设计一个算法用于删除最大值的结点,假设这样的结点是唯一的。
(2)算法思想
先找到最大值的结点maxp,pre指向其前驱结点,在删除maxp结点。
(3)代码示例
#include <stdio.h>
#include <stdlib.h>
typedef struct DNode {
int data;
struct DNode* next;
struct DNode* prior;
}DNode, *DLinkList;
//函数声明
DLinkList List_TrailInsert(DLinkList L);
void Delete_MAX(DLinkList L);
int main()
{
DLinkList L = (DLinkList)malloc(sizeof(DNode));
L->next = NULL;
printf("===============请输入L链表的数据值(以-1为结束标志):\n");
List_TrailInsert(L);//构造单链表
Delete_MAX(L);
printf("\n\n===========删除链表中的最大值后链表的元素为:\n");
DNode* p = L->next;
while (p) {
printf("%5d", p->data);
p = p->next;
}
return 0;
}
/*
* 题目描述:
* 设有一个带头结点的双链表h,设计一个算法用于删除最大值的结点,假设这样的结点是唯一的。
* 算法思想:
* 先找到最大值的结点maxp,pre指向其前驱结点,在删除maxp结点
*/
void Delete_MAX(DLinkList L) {
DNode* p = (L)->next;
DNode* maxp = (L)->next;
DNode* pre;
while (p) {
if (p->data > maxp->data) {
maxp = p;
}
else {
p = p->next;
}
}
pre = maxp->prior; //双链表,本身记录了其前驱结点
pre->next = maxp->next;
if (maxp->next != NULL) {
maxp->next->prior = pre;
free(maxp);
}
}
//尾插法构造双链表
DLinkList List_TrailInsert(DLinkList L) {
int x;
DNode* r = L;
scanf_s("%d", &x);
while (x != -1) {
DNode* s = (DNode*)malloc(sizeof(DNode));
s->data = x;
r->next = s;
s->prior = r;
r = s;
scanf_s("%d", &x);
}
r->next = NULL;
return L;
}
(4)总结
(1)双循环链表删除结点相对简单,不需要再扫描时额外记录其前驱结点;
(2)正确运算结果打印
24. 链表16 (2023/09/03)
(1)题目描述
已知带头结点的循环单链表L中至少有两个结点,每个结点的两个字段为data和next,其中data的类型为整型。试设计一个算法判断该链表中每个元素的值是否小于其后续两个结点之和。若满足,则返回true,否则返回false。
(2)算法思想
用指针p扫描整个单链表,若当前指针p指向的结点的值不小于后续两个结点的值之和就中断并返回false,否则继续循环直到跳出循环返回true。
注:这里题目要求是小于,所以一定要注意if条件判断,应是大于等于就不满足条件。
(3)代码示例
#include <stdio.h>
#include <stdlib.h>
typedef struct LNode {
int data;
struct LNode* next;
}LNode, * LinkList;
//函数声明
LinkList List_TrailInsert(LinkList L);
bool judge(LinkList L);
int main()
{
LinkList L = (LinkList)malloc(sizeof(LNode));
L->next = NULL;
printf("===============请输入L链表的数据值(以-1为结束标志):\n");
List_TrailInsert(L);//构造单链表
bool result = judge(L);
if (result) {
printf("\n循环单链表L每个元素的值小于其后续两个结点的值;\n");
}
else {
printf("\n循环单链表L每个元素的值不全部小于其后续两个结点的值;\n");
}
return 0;
}
/*
* 题目描述:
* 已知带头结点的循环单链表L中至少有两个结点,每个结点的两个字段为data和next,其中data的类型为整型。
* 试设计一个算法判断该链表中每个元素的值是否小于其后续两个结点之和。若满足,则返回true,否则返回false。
* 算法思想:
* 用指针p扫描整个单链表,若当前指针p指向的结点的值不小于后续两个结点的值之和就中断并返回false,否则继续循环直到跳出循环返回true
*/
bool judge(LinkList L) {
LNode* p = L->next;
while (p->next->next != L) {
if ((p->data) >= (p->next->data + p->next->next->data)) {
return false; //不满足if条件,中断循环返回false
}
else {
p = p->next;
}
}
return true; //扫描完整个循环单链表,主动跳出循环,都满足条件,返回true
}
//尾插法构造链表
LinkList List_TrailInsert(LinkList L) {
int x;
LNode* r = L;
scanf_s("%d", &x);
while (x != -1) {
LNode* s = (LNode*)malloc(sizeof(LNode));
s->data = x;
r->next = s;
r = s;
scanf_s("%d", &x);
}
r->next = L;
return L;
}
(4)总结
(1)题目中提到L链表是循环单链表,不管是否循环都不影响此题的逻辑,除了循环条件判断不再是指针是否指向空,而是是否指向头节点。
(2)正确运算结果打印
返回false的情况:
(1)恰好全部等于(这种情况很容易被忽视)
(2)出现大于或等于 的情况
返回true的情况:
25. 链表17 (2023/09/03)
(1)题目描述
有一个双链表L,每个结点中除有prior、data、next三个领域外,还有一个访问频度freq,在链表被起用之前,其值均初始化为零。每当进行LocateNote(L,x)运算时,令元素值为x的结点中freq域的值加1,并调整表中结点的次序,使其按访问频度的递减序排列,一遍频繁访问结点总是靠近表头。试写一符合上述要求的LocateNode运算的算法。(假设每个结点值唯一)
(2)算法思想
1. 扫描双链表,找到值为x的结点;
2. 找到之后,将结点值为x的结点的访问频度+1;
3. 按访问频度将链表的结点按递减排序。
(3)代码示例
#include <stdio.h>
#include <stdlib.h>
typedef struct LNode {
int data;//数据域
int freq;//频度域
struct LNode* next;
struct LNode* prior;
}LNode, * DLinkList;
//函数声明
DLinkList List_TrailInsert(DLinkList L);
void LocateNode(DLinkList L, int x);
int main()
{
DLinkList L = (DLinkList)malloc(sizeof(LNode));
L->next = NULL;
printf("===============请输入L链表的数据值(以-1为结束标志):\n");
List_TrailInsert(L);//构造单链表
int x;
printf("\n请输入你要查找的结点的值(以-1为结束标志): ");//一次性输入需要查找的元素,模仿当前查找时(最后一个元素),假设前面已经有元素被查找过的痕迹
scanf_s("%d", &x);
while (x != -1) {
LocateNode(L, x);
scanf_s("%d", &x);
}
printf("\n\n===================经过当前查找后链表的状态为:\n");
LNode* p = L->next;
while (p) {
printf("%6d-%d", p->data, p->freq);
p = p->next;
}
return 0;
}
/*
* 题目描述:
* 有一个双链表L,每个结点中除有prior、data、next三个领域外,还有一个访问频度freq,在链表被起用之前,其值均初始化为零。每当进行
* LocateNote(L,x)运算时,令元素值为x的结点中freq域的值加1,并调整表中结点的次序,使其按访问频度的递减序排列,一遍频繁访问结点总是靠近表头。
* 试写一符合上述要求的LocateNode运算的算法。
* 算法思想:
* 1. 扫描双链表,找到值为x的结点;
* 2. 找到之后,将结点值为x的结点的访问频度+1;
* 3. 按访问频度将链表的结点按递减排序。
*/
void LocateNode(DLinkList L, int x) {
LNode* p = L->next;
LNode* pre;
//1. 扫描双链表,找到值为x的结点
while (p != NULL && (p->data != x)) {
p = p->next;
}
// 2. 找到之后,将结点值为x的结点的访问频度 + 1
if (p != NULL) {
p->freq++;
//3. 按访问频度将链表的结点按递减排序
pre = p->prior; //整个链表初始为0,在使用LocateNode()函数之后freq就会变化,且整个链表都是按freq降序排序的,所以往前找
while (pre->freq < p->freq && pre != L) {
pre = pre->prior;
}
p->prior->next = p->next;//先在原始位置删除结点(但p指针记录了这个结点,以便重新链接回链表)
if (p->next != NULL) { //如果p指向的已经是末尾结点,那么就不必有改变后继结点的前驱结点这一步
p->next->prior = p->prior;
}
p->next = pre->next; //插入
if (pre->next != NULL) {
pre->next->prior = p;
}
pre->next = p;
p->prior = pre;
}
}
//尾插法构造链表
DLinkList List_TrailInsert(DLinkList L) {
int x;
LNode* r = L;
scanf_s("%d", &x);
while (x != -1) {
LNode* s = (LNode*)malloc(sizeof(LNode));
s->freq = 0;
s->data = x;
s->prior = r;
r->next = s;
r = s;
scanf_s("%d", &x);
}
r->next = NULL;
return L;
}
注:在操作结点的prior指针域时,一定要判断该结点是否是头结点,或者是NULL(循环单链表中)
(4) 总结
(1)这个题比之前的题的难度确实要上升一个大步的难度,细品(有那味儿了😎);
(2)正确运算结果打印:
注:也可以查找链表中没有的元素,比如这个栗子中的 -100
26. 链表18(2023/09/04)
(1)题目描述
(2009统考)已知一个带有头结点的单链表,假设该链表只给出了头指针L,在不改变链表的前提下,请设计一个尽可能高效的算法,查找链表中倒数第k个位置上的结点(k为正整数)。若查找成功,算法输出该结点的data域的值,并返回1,否则,只返回 0。
(2)算法思想
使用快慢指针,(前提这两个指针都指向首元结点而非头结点,注释中有解释)当快指针移动到k+1个结点时,快慢指针之间隔了k个结点,这时快慢指针一起移动,当快指针移动到NULL时,慢指针也移动到了倒数第k个结点。
注:这里为什么不直接套用公式(倒数第k个结点就是整数第n-k+1个结点)因为题目中只给出了头结点,没有给出结点的个数,所以如果要用公式那么久的先扫描一遍,再重新扫描找到第n-k+1个结点,就不高效了。
(3)代码示例
#include <stdio.h>
#include <stdlib.h>
typedef struct LNode {
int data;
struct LNode* next;
}LNode, * LinkList;
//函数声明
LinkList List_TrailInsert(LinkList L);
int Search_k(LinkList L, int k, int* value);
int main()
{
LinkList L = (LinkList)malloc(sizeof(LNode));
L->next = NULL;
printf("===============请输入L链表的数据值(以-1为结束标志):\n");
List_TrailInsert(L);
printf("\n\n========请输入要查找倒数第几个结点值: ");
int k;
scanf_s("%d", &k);
int value;
int result = Search_k(L, k, &value);
if (result) {
printf("\n\n=========查找到的倒数第%2d个结点的值为:%5d\n", k, value);
}
else {
printf("\n======查找失败!=========\n");
}
return 0;
}
/*
* (2009年统考)题目描述:
* 已知一个带有头结点的单链表,假设该链表只给出了头指针L,在不改变链表的前提下,请设计一个尽可能高效的算法,查找链表中倒数第k个位置上的
* 结点(k为正整数)。若查找成功,算法输出该结点的data域的值,并返回1,否则,只返回 0。
* 算法思想:
* 使用快慢指针,(前提这两个指针都指向首元结点而非头结点,注释中有解释)当快指针移动到k+1个结点时,快慢指针之间隔了k个结点,
* 这时快慢指针一起移动,当快指针移动到NULL时,慢指针也移动到了倒数第k个结点
*/
int Search_k(LinkList L, int k, int* value) {
LNode* fast = L->next; //快慢指针同时指向第一个结点,这样是为了防止当k值刚好等于了链表的结点个数,最好查找到的是虚拟头结点
LNode* slow = L->next;
int i = 0;
while (fast) {
if (i < k) { //快指针先移动到整数第k个位置
i++;
fast = fast->next;
}
else { //然后慢指针和快指针一起移动
fast = fast->next;
slow = slow->next;
}
}
if (i == k) { //加一个判断,以防出现k值大于链表的长度这种情况(前面无法排除这种情况)
*value = slow->data;
return 1;
}
return 0;
}
//尾插法构造链表
LinkList List_TrailInsert(LinkList L) {
int x;
LNode* r = L;
scanf_s("%d", &x);
while (x != -1) {
LNode* s = (LNode*)malloc(sizeof(LNode));
s->data = x;
r->next = s;
r = s;
scanf_s("%d", &x);
}
r->next = NULL;
return L;
}
(4)总结
(1)这里要注意快慢指针的初始化(详见注解);
(2)正确运算结果打印
27. 链表19(2023/09/04)
(1)题目描述
给定一个链表,判断链表中是否有环。如果有环,返回1,否则返回0
(2)算法思想
利用快慢指针,快指针步距为2,慢指针步距为1,如果有环,快指针一定会反超慢指针而相遇
注:这里为什么快指针步距不再大点,如果大点需要判断其合法性,会使程序更复杂
(3)代码示例
#include <stdio.h>
#include <stdlib.h>
typedef struct LNode {
int data;
struct LNode* next;
}LNode, * LinkList;
//函数声明
LinkList List_TrailInsert01(LinkList L); //构造无环单链表
LinkList List_TrailInsert02(LinkList L); //构造有环单链表
int hasCycle(LinkList L);
int main()
{
LinkList LA = (LinkList)malloc(sizeof(LNode));
LA->next = NULL;
LinkList LB = (LinkList)malloc(sizeof(LNode));
LB->next = NULL;
printf("===============请输入LA链表的数据值(以-1为结束标志):\n");
List_TrailInsert01(LA); //无环
printf("\n===============请输入LB链表的数据值(以-1为结束标志):\n");
List_TrailInsert02(LB); //有环
int resultA = hasCycle(LA);
int resultB = hasCycle(LB);
if (resultA) {
printf("\n=======resultA有环\n");
}
else {
printf("\n=======resultA无环\n");
}
if (resultB) {
printf("\n=======resultB有环\n");
}
else {
printf("\n=======resultB无环\n");
}
return 0;
}
/*
* 题目描述:
* 给定一个链表,判断链表中是否有环。如果有环,返回1,否则返回0
* 算法思想:
* 利用快慢指针,快指针步距为2,慢指针步距为1,如果有环,快指针一定会反超慢指针而相遇
* (这里为什么快指针步距不再大点,如果大点需要判断其合法性,会使程序更复杂)
*/
int hasCycle(LinkList L) {
if (L == NULL || L->next == NULL) { //排除链表为空和只有头结点的情况,必定无环
return 0;
}
LNode* fast = L->next;
LNode* slow = L;
while (fast != slow) {
if (fast == NULL || fast->next->next == NULL) {
return 0;
}
slow = slow->next;
fast = fast->next->next;
}
//跳出循环两种情况:一个是if条件中断函数(无环),一个是确定有环跳出循环
return 1;
}
//尾插法构造链表(无环)
LinkList List_TrailInsert01(LinkList L) {
int x;
LNode* r = L;
scanf_s("%d", &x);
while (x != -1) {
LNode* s = (LNode*)malloc(sizeof(LNode));
s->data = x;
r->next = s;
r = s;
scanf_s("%d", &x);
}
r->next = NULL;
return L;
}
//有环(不构造空链表)
LinkList List_TrailInsert02(LinkList L) {
int x;
LNode* r = L;
scanf_s("%d", &x);
while (x != -1) {
LNode* s = (LNode*)malloc(sizeof(LNode));
s->data = x;
r->next = s;
r = s;
scanf_s("%d", &x);
}
r->next = L;
return L;
}
注:这里我构造了两个链表,一个无环单链表,一个循环单链表(一定有环)
(4)总结
(1)灵活使用快慢指针;
(2) 正确运算结果打印
28. 栈和队列01(2023/09/05)
(1)题目描述
回文是指正读反读均相同的字符序列,如“abba”和“abdba”均是回文,但“good”不是回文。试写一个算法判断给定字符序列是否是回文。(提示:先将一半字符入栈)
(2)算法思想
先将一半字符入栈,然后弹出栈中元素和字符后一半比较。即将第一个出栈元素和后一半第一个字符比较,若相等,则再重复操作,若不等,则立即下判断不是回文。
(3)代码示例
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MaxSize 50
typedef struct {
char data[MaxSize];
int top;
}SqStack;
//函数声明
//栈
void InitStack(SqStack* S); //初始化栈
bool isStackEmpty(SqStack S); //判断栈空
bool Push(SqStack* S, char x); //入栈
bool Pop(SqStack* S, char* x); //出栈
bool GetTop(SqStack S, char* x); //取栈顶元素的值
bool isPalindrome(char* A); //判断字符串序列是否为回文
int main()
{
char A[MaxSize];
printf("=========请输入字符序列:");
scanf_s("%s", A, sizeof(A));
printf("%s", A);
bool result = isPalindrome(A);
if (result) {
printf("\n\n是回文!!");
}
else {
printf("\n\n不是回文!!");
}
return 0;
}
/*
* 题目描述:
* 回文是指正读反读均相同的字符序列,如“abba”和“abdba”均是回文,但“good”不是回文。试写一个算法判断给定字符序列是否是回文。
* (提示:先将一半字符入栈)
* 算法思想:
* 先将一半字符入栈,然后弹出栈中元素和字符后一半比较。即将第一个出栈元素和后一半第一个字符比较,若相等,则再重复操作,若不等,则立即
* 下判断不是回文。
*/
bool isPalindrome(char* A) {
SqStack S;
InitStack(&S); //初始化栈
int len = strlen(A); //求字符串的长度
int i = 0;
for (i = 0; i < len / 2; i++) { //将一半的字符串入栈
Push(&S, A[i]);
}
if (len % 2 != 0) { //长度为奇数,跳过中间字段
i++;
}
char tmp;
while (!isStackEmpty(S)) { //不空
Pop(&S, &tmp); //出栈并带回出栈元素
if (tmp != A[i]) {
return false; //不相等返回false
}
else {
i++;
}
}
return true; //比较完毕均相等返回true
}
//栈的相关操作
//初始化
void InitStack(SqStack* S) {
S->top = -1;
}
//判断是否为空
bool isStackEmpty(SqStack S) {
if (S.top == -1) {
return true;
}
else {
return false;
}
}
//进栈
bool Push(SqStack* S, char x) {
if (S->top == MaxSize - 1) { //栈满
return false;
}
else {
S->data[++S->top] = x; //指针先加,再放值
return true;
}
}
//出栈
bool Pop(SqStack* S, char* x) {
if (S->top == -1) { //栈空
return false;
}
*x = S->data[S->top--]; //先取值,栈顶指针再减
return true;
}
//读栈顶元素
bool GetTop(SqStack S, char* x) {
if (S.top == -1) { //栈空
return false;
}
*x = S.data[S.top]; //取值
return true;
}
(4)总结
(1)主要掌握栈的基本操作(出栈,入栈,判空,取栈顶元素等)
(2)正确运算结果打印
29. 栈和队列02(2023/09/05)
(1)题目描述
假设以数组Q[m]存放循环队列的元素,同时设置一个标志域名tag,以tag==0和tag==1来区别 队头和队尾相等时,队列状态为“空”还是“满”。试编写此结构响应的插入(EnQueue)和删除(DeQueue)算法。
(2)算法思想
核心在于队满时必定是入队造成的,队空必定试出队造成的。
(3)代码示例
(1)正确代码
#include <stdio.h>
#include <stdlib.h>
#define MaxSize 10
//定义队列
typedef struct {
int data[MaxSize];
int front, rear; //队首队尾指针(队首指针指向队首元素,队尾指针指向队尾元素后一个)
int tag; //区分队空队满标识
}SqQueue;
//函数声明
void InitQueue(SqQueue* Q);//初始化
bool isQueueEmpty(SqQueue Q);//判空
bool isQueueFull(SqQueue Q);//判满
bool EnQueue(SqQueue* Q, int x);//入队
bool DeQueue(SqQueue* Q, int* x);//出队
int main()
{
SqQueue Q;
InitQueue(&Q); //初始化队列
int x;
//入队并(初始化)
printf("====请输入要放入队列中的数据:");
scanf_s("%d", &x);
while (x != -1) {
EnQueue(&Q, x);
scanf_s("%d", &x);
}
//printf("Q.data[%d]=%d\n", Q.rear, Q.data[Q.rear]);//读了队尾元素后一个单元
int len;
if (isQueueFull(Q)) { //这里要特别注意,如果输入的元素超出队列的最大限度,即队满,队首队尾指针相等,则长度就会为0,实际上部位0
len = MaxSize;
}
else {
len = (Q.rear + MaxSize - Q.front) % MaxSize;
}
printf("\n============(首次初始化)入队之后此刻队列元素(%d个)为:\n",len);
for (int i = Q.front; i < (len + Q.front); i++) {
printf("%5d-%d", Q.data[(i + MaxSize) % MaxSize], (i + MaxSize) % MaxSize);
}
//23 65 45 85 96 36 71 27 396 152 328 -1
//出队
int value;//记录出队的元素
if (isQueueEmpty(Q)) {
printf("\n=====出队失败!=====");
}
else {
DeQueue(&Q, &value);
len = (Q.rear + MaxSize - Q.front) % MaxSize;
printf("\n============队首元素(%d)出队之后,此刻队列元素(%d个)为:\n", value, len);
for (int i = Q.front; i < (len + Q.front); i++) {
printf("%5d-%d", Q.data[(i + MaxSize) % MaxSize], (i + MaxSize) % MaxSize);
}
}
//出队
if (isQueueEmpty(Q)) {
printf("\n=====出队失败!=====");
}
else {
DeQueue(&Q, &value);
len = (Q.rear + MaxSize - Q.front) % MaxSize;
printf("\n============队首元素(%d)出队之后,此刻队列元素(%d个)为:\n", value, len);
for (int i = Q.front; i < (len + Q.front); i++) {
printf("%5d-%d", Q.data[(i + MaxSize) % MaxSize], (i + MaxSize) % MaxSize);
}
}
//出队
if (isQueueEmpty(Q)) {
printf("\n=====出队失败!=====");
}
else {
DeQueue(&Q, &value);
len = (Q.rear + MaxSize - Q.front) % MaxSize;
printf("\n============队首元素(%d)出队之后,此刻队列元素(%d个)为:\n", value, len);
for (int i = Q.front; i < (len + Q.front); i++) {
printf("%5d-%d", Q.data[(i + MaxSize) % MaxSize], (i + MaxSize) % MaxSize);
}
}
//入队
printf("\n====请输入要放入队列中的数据:");
scanf_s("%d", &x);
EnQueue(&Q, x);
if (isQueueFull(Q)) {
printf("=====队满,入队失败!!");
}
else {
len = (Q.rear + MaxSize - Q.front) % MaxSize;
printf("\n============入队之后此刻队列元素(%d个)为:\n", len);
for (int i = Q.front; i < (len + Q.front); i++) {
printf("%5d-%d", Q.data[(i+MaxSize)%MaxSize], (i + MaxSize) % MaxSize);
}
}
//入队
printf("\n====请输入要放入队列中的数据:");
scanf_s("%d", &x);
EnQueue(&Q, x);
if (isQueueFull(Q)) {
printf("=====队满,入队失败!!");
}
else {
len = (Q.rear + MaxSize - Q.front) % MaxSize;
printf("\n============入队之后此刻队列元素(%d个)为:\n", len);
for (int i = Q.front; i < (len + Q.front); i++) {
printf("%5d-%d", Q.data[(i + MaxSize) % MaxSize], (i + MaxSize) % MaxSize);
}
}
return 0;
}
//队列的基本操作函数定义
//初始化
void InitQueue(SqQueue* Q) {
Q->rear = Q->front = 0;
Q->tag = 0;
}
//判空
bool isQueueEmpty(SqQueue Q) {
if (Q.front == Q.rear && Q.tag == 0) {
return true;
}
else {
return false;
}
}
//判满
bool isQueueFull(SqQueue Q) {
if (Q.front == Q.rear && Q.tag == 1) {
return true;
}
else {
return false;
}
}
//这里多出了多少个元素就会打印多少次入队失败
//入队
bool EnQueue(SqQueue* Q, int x) {
if (isQueueFull(*Q)) {
printf("\n队满,入队失败!\n");
return false;
}
else {
Q->data[Q->rear] = x; //Q->data[Q->rear++] = x; //因为是循环队列,所以不能这样干(要确保指针在最大值范围内移动)
Q->rear = (Q->rear + 1) % MaxSize; //先放进来,队尾指针再后移
Q->tag = 1;
//printf("\n入队成功!!\n");
return true;
}
}
//出队
bool DeQueue(SqQueue* Q, int* x) {
if (isQueueEmpty(*Q)) {
printf("\n队空,出队失败!\n");
return false;
}
else {
*x = Q->data[Q->front];
Q->front = (Q->front + 1) % MaxSize; //先取出元素,队首指针再后移
Q->tag = 0;
//printf("\n出队成功!!\n");
return true;
}
}
(2)这里想放一个错误的栗子,就是在输出的时候,队列的长度需要注意(可以自己体会)
#include <stdio.h>
#include <stdlib.h>
#define MaxSize 10
//定义队列
typedef struct {
int data[MaxSize];
int front, rear; //队首队尾指针(队首指针指向队首元素,队尾指针指向队尾元素后一个)
int tag; //区分队空队满标识
}SqQueue;
//函数声明
void InitQueue(SqQueue* Q);//初始化
bool isQueueEmpty(SqQueue Q);//判空
bool isQueueFull(SqQueue Q);//判满
bool EnQueue(SqQueue* Q, int x);//入队
bool DeQueue(SqQueue* Q, int* x);//出队
int main()
{
SqQueue Q;
InitQueue(&Q); //初始化队列
int x;
printf("====请输入要放入队列中的数据:");
scanf_s("%d", &x);
while (x != -1) {
EnQueue(&Q, x);
scanf_s("%d", &x);
}
//printf("Q.data[%d]=%d\n", Q.rear, Q.data[Q.rear]);//读了队尾元素后一个单元
int len = (Q.rear + MaxSize - Q.front) % MaxSize;
printf("\n============此刻队列元素(%d个)为:\n",len);
for (int i = 0; i < len; i++) {
printf("%5d", Q.data[i]);
}
return 0;
}
//队列的基本操作函数定义
//初始化
void InitQueue(SqQueue* Q) {
Q->rear = Q->front = 0;
Q->tag = 0;
}
//判空
bool isQueueEmpty(SqQueue Q) {
if (Q.front == Q.rear && Q.tag == 0) {
return true;
}
else {
return false;
}
}
//判满
bool isQueueFull(SqQueue Q) {
if (Q.front == Q.rear && Q.tag == 1) {
return true;
}
else {
return false;
}
}
//入队
bool EnQueue(SqQueue* Q, int x) {
if (isQueueFull(*Q)) {
printf("\n队满,入队失败!\n");
return false;
}
else {
Q->data[Q->rear] = x; //Q->data[Q->rear++] = x; //因为是循环队列,所以不能这样干(要确保指针在最大值范围内移动)
Q->rear = (Q->rear + 1) % MaxSize; //先放进来,队尾指针再后移
Q->tag = 1;
//printf("\n入队成功!!\n");
return true;
}
}
//出队
bool DeQueue(SqQueue* Q, int* x) {
if (isQueueEmpty(*Q)) {
printf("\n队空,出队失败!\n");
return false;
}
else {
*x = Q->data[Q->front];
Q->front = (Q->front + 1) % MaxSize; //先取出元素,队首指针再后移
Q->tag = 0;
printf("\n出队成功!!\n");
return true;
}
}
放出错误打印结果(这里实际上队列中是有元素的,而且队满了,队满的时候队尾和队首指针是相等的,所以这样计算实际上是不合理的,需要多加一个判断)
(3)这里初始化时多出了多少个元素就会打印多少次入队失败
(4)总结
(1)这次的题目虽然不是很难,但是要想写得相对完美,却是要注意很多细节,真的细节超多,不一一例举,看代码细品.
(2)放多个打印栗子出来供参考(正确的)
30. 栈和队列03(2023/09/06)
(1)题目描述
设计一个算法,判断输入的表达式中括号是否配对(假设只含有左、右括号)。
(2)算法思想
算法思想,扫描表达式,一旦遇到左括号就入栈,一旦遇到右括号就弹出栈顶元素比较,若是左括号,则继续扫描,否则返回false。
注:1. 这里要注意一下,在输入的时候输入的括号要和代码中的保持一致(中英文);
2. 考虑到右括号多余的情况,右括号多余,栈中为空,最后还是会返回true,所以要另加判断。
(3)代码示例
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MaxSize 50
typedef struct {
char data[MaxSize];
int top;
}SqStack;
//函数声明
//栈
void InitStack(SqStack* S); //初始化栈
bool isStackEmpty(SqStack S); //判断栈空
bool Push(SqStack* S, char x); //入栈
bool Pop(SqStack* S, char* x); //出栈
bool GetTop(SqStack S, char* x); //取栈顶元素的值
bool isMatch(char* A);
int main()
{
char A[MaxSize];
printf("=========请输入表达式(在输入的时候要注意区分中文英文的括号,假设我们这里统一使用英文括号):\n");
scanf_s("%s", A, sizeof(A));
printf("=====%s", A);
bool result = isMatch(A);
if (result) {
printf("\n\n该表达式中的括号匹配!\n");
}
else {
printf("\n\n该表达式中的括号不匹配!\n");
}
return 0;
}
/*
* 题目描述:
* 设计一个算法,判断输入的表达式中括号是否配对(假设只含有左、右括号)
* 算法思想,扫描表达式,一旦遇到左括号就入栈,一旦遇到右括号就弹出栈顶元素比较,若是左括号,则继续扫描,否则返回false
*/
bool isMatch(char* A) {
SqStack S;
InitStack(&S);//初始化栈
int i = 0;
bool tmp; //记录出栈是否成功(考虑到右括号多余的情况)
char e; //用来记录出栈的元素
while (A[i] != '\0') {
switch (A[i]) {
case '(':Push(&S, '('); break;
case ')':tmp = Pop(&S, &e);
if (e != '(' || !tmp) {
printf("\n不匹配\n");
return false;
}
break;
default:break;
}
i++;
}
if (isStackEmpty(S)) {
return true;
}
else {
return false;
}
}
//栈的相关操作
//初始化
void InitStack(SqStack* S) {
S->top = -1;
}
//判断是否为空
bool isStackEmpty(SqStack S) {
if (S.top == -1) {
return true;
}
else {
return false;
}
}
//进栈
bool Push(SqStack* S, char x) {
if (S->top == MaxSize - 1) { //栈满
return false;
}
else {
S->data[++S->top] = x; //指针先加,再放值
printf("\n压栈\n");
return true;
}
}
//出栈
bool Pop(SqStack* S, char* x) {
if (S->top == -1) { //栈空
printf("\n栈空\n");
return false;
}
printf("\n出栈\n");
*x = S->data[S->top--]; //先取值,栈顶指针再减
return true;
}
//读栈顶元素
bool GetTop(SqStack S, char* x) {
if (S.top == -1) { //栈空
return false;
}
*x = S.data[S.top]; //取值
return true;
}
(4)总结
(1)栈的应用
(2)正确运算结果打印
31. 栈和队列04(2023/09/06)
(1)题目描述
设计一个算法,判断输入的表达式中括号是否配对(含有圆括号、方括号、大括号)
(2)算法思想
算法思想,扫描表达式,一旦遇到左括号就入栈,一旦遇到右括号就弹出栈顶元素比较,若是左括号,则继续扫描,否则返回false
(3)代码示例
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MaxSize 50
typedef struct {
char data[MaxSize];
int top;
}SqStack;
//函数声明
//栈
void InitStack(SqStack* S); //初始化栈
bool isStackEmpty(SqStack S); //判断栈空
bool Push(SqStack* S, char x); //入栈
bool Pop(SqStack* S, char* x); //出栈
bool GetTop(SqStack S, char* x); //取栈顶元素的值
bool isMatch(char* A);
int main()
{
char A[MaxSize];
printf("=========请输入表达式(在输入的时候要注意区分中文英文的括号,假设我们这里统一使用英文括号):\n");
scanf_s("%s", A, sizeof(A));
printf("=====%s", A);
bool result = isMatch(A);
if (result) {
printf("\n\n该表达式中的括号匹配!\n");
}
else {
printf("\n\n该表达式中的括号不匹配!\n");
}
return 0;
}
//[(a + b) + c] + {a + c - b + d - (s + )}
/*
* 题目描述:
* 设计一个算法,判断输入的表达式中括号是否配对(含有圆括号、方括号、大括号)
* 算法思想,扫描表达式,一旦遇到左括号就入栈,一旦遇到右括号就弹出栈顶元素比较,若是左括号,则继续扫描,否则返回false
*/
bool isMatch(char* A) {
SqStack S;
InitStack(&S);//初始化栈
int i = 0;
bool tmp; //记录出栈是否成功(考虑到右括号多余的情况)
char e; //用来记录出栈的元素
while (A[i] != '\0') {
switch (A[i]) {
case '(':Push(&S, '('); break;
case '{':Push(&S, '{'); break;
case '[':Push(&S, '['); break;
case ')':tmp = Pop(&S, &e);
if (e != '(' || !tmp) {
return false;
}
break;
case '}':tmp = Pop(&S, &e);
if (e != '{' || !tmp) {
return false;
}
break;
case ']':tmp = Pop(&S, &e);
if (e != '[' || !tmp) {
return false;
}
break;
default:break;
}
i++;
}
if (isStackEmpty(S)) {
return true;
}
else {
return false;
}
}
//栈的相关操作
//初始化
void InitStack(SqStack* S) {
S->top = -1;
}
//判断是否为空
bool isStackEmpty(SqStack S) {
if (S.top == -1) {
return true;
}
else {
return false;
}
}
//进栈
bool Push(SqStack* S, char x) {
if (S->top == MaxSize - 1) { //栈满
return false;
}
else {
S->data[++S->top] = x; //指针先加,再放值
//printf("\n压栈\n");
return true;
}
}
//出栈
bool Pop(SqStack* S, char* x) {
if (S->top == -1) { //栈空
//printf("\n栈空\n");
return false;
}
//printf("\n出栈\n");
*x = S->data[S->top--]; //先取值,栈顶指针再减
return true;
}
//读栈顶元素
bool GetTop(SqStack S, char* x) {
if (S.top == -1) { //栈空
return false;
}
*x = S.data[S.top]; //取值
return true;
}
(4)总结
(1)和上一个题目一样,就是多几种类型括号而已;
(2)正确运算结果打印
这篇到这个题结束,另外一篇再写,从树开始(不知道是不是后台服务器太慢了,打个字卡半天)
(欢迎大家留言,如若有错误或更好的建议,谢谢大家不吝赐教😊 )