本份代码练习题来自B站up主:白话拆解数据结构!
最近科研实验做的差不多了,想着学点技术,顺便温习一下数据结构!
就从群里的代码集训开始把,从Day2开始,Day1是c语言基础,就不敲了。
Day2 的题目是设计一个高效算法,将顺序表中的元素逆置!
首先是结构体定义:
typedef int ElemType;
struct SqList {
ElemType data[100];
int length;
};
将int重命名成了ElemType,后面ElemType a就相当于int a,我觉得有没有这句都无所谓!
顺序表结构体包括两个部分:存数据的定长数组、表长记录。顺序表长度只与length相关,length之外的数组部分全为0。
每次初始化一个结构体变量,就要占用上图的内存,其中红色部分为我们的顺序表所需内存,但顺序表的length不能超过100!
然后就开始解决问题了:所谓逆置,就是把12345的存储顺序换成54321而已!注意观察,3的位置是不变的,5和1调换了位置,2和4调换了位置,找到规律了。
接下来要验证这种规律,试试一组更长的数123456789,逆序后是987654321,ok规律成立!下面就要写代码来实现它。
从表头和表尾开始,交换元素,交换完后,就是表头的下一个元素和表尾的前一个元素交换了;由于顺序表的length已知,所以表头和表尾的下标都知道了(L.data[0]和L.data[L.length-1]),所以可以分别设置两个标志位i和j(也可以叫逻辑指针),一个标识表头,一个表尾,让L.data[i]和L.data[j]执行交换即可,交换完毕,i++,j--,就能继续执行下一轮交换了。
然后问题又来了,对于12345的表,只需执行两次交换,对于123456789的表需要执行4次交换,所以怎么设置循环条件呢?表长为length,执行了(L.length-1)/2次,设置成i<(L.length-1)/2就可以了吗?不是,我们只考虑了单数的情况,如果是1234和12345678的表,虽然同样执行了2和4次交换,但是循环条件如果设置成(L.length-1)/2,(4-1)/2在c语言中等于1,那就会少一次交换,所以我们要多考虑一步,将i<(L.length-1)/2设置成i<=(L.length-1)/2。
表长的单复数的循环条件还不一样,是不是显得过于复杂了呀,有没有方法可以结合起来呢?结合c语言的特性:5/2=2,我们可以将循环条件设成i<L.length/2;当表长为5的时候,执行两次循环,当表长为4的时候也执行两次循环,完美!
理论成立,开始实操:(元素交换的操作也可以直接调用swap函数,已经被标准库封装好了)
// 算法思想:取一个中间哨兵,两边往中间交换值即可
bool reverse(SqList &L) {
if (L.length == 0) return false;
int value = L.length/2;
int temp = 0;
//int j=L.length-1;
for (int i = 0; i < value; i++) {
temp = L.data[i];
L.data[i] = L.data[L.length-i-1]; // L.data[i] = L.data[j];
L.data[L.length-i-1] = temp; // L.data[j] = temp;
// j--;
}
return true;
}//我这里没有用j,如果要用j的话,看上面的//部分
SqList &L中的&代表引用,在c语言中是没有引用的,而c++中引入了这个概念,引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间。简单来说加了这个&,你函数内如果改变了顺序表,那函数调用结束后这种改变仍然存在,不会随着函数结束作用域而消失!
在笔试中(考研、期末、专升本中),可以直接用引用,无须担心他是c语言还是C++!
if (L.length == 0) return false; 这是健壮性判断,空表你还逆置个毛啊!好的算法要尽可能考虑到所有非法的情况!
一些小白同学(也包括我),老是在c和c++之间纠结,其实完全没这必要!对于考试来说,就是scanf、printf、cin、cout;malloc,free,new,delete等的区别,这里介绍一个方法,在cpp文件的开头加句:#include<cstdio>;可以实现cin和printf混着用。
功能函数是写好了,得写个main函数造个表实践一下啊,看看写错没。
int main() {
SqList L;
L.length = 6;
// 随机赋值
for(int i =0;i<L.length;i++){
L.data[i] = rand()%20;
}
printf("Original list: ");
for (int i = 0; i < L.length; i++) {
printf("%d ",L.data[i]);
}
printf("\n");
if (reverse(L)) {
printf("The new list is: ");
for (int i = 0; i < L.length; i++) {
printf("%d ",L.data[i]);
}
} else {
cout << "error." << endl;
}
return 0;
}
首先初始化一个顺序表L,随机赋6个值,然后打印原始表和逆置后的表:
OK,成功,再来试试奇数情况:
代码是正确的,且printf和cout是不是实现了混用!
完整代码如下,在vscode环境下已成功编译
#include <iostream>
#include<cstdio>
using namespace std;
// 设计一个高效算法,将顺序表中元素逆置
typedef int ElemType;
struct SqList {
ElemType data[100];
int length;
};
// 算法思想:取一个中间哨兵,两边往中间交换值即可
bool reverse(SqList &L) {
if (L.length == 0) return false;
int value = L.length/2;
int temp = 0;
for (int i = 0; i < value; i++) {
temp = L.data[i];
L.data[i] = L.data[L.length-i-1];
L.data[L.length-i-1] = temp;
}
return true;
}
int main() {
SqList L;
L.length = 6;
// 随机赋值
for(int i =0;i<L.length;i++){
L.data[i] = rand()%20;
}
printf("Original list: ");
for (int i = 0; i < L.length; i++) {
printf("%d ",L.data[i]);
}
printf("\n");
if (reverse(L)) {
printf("The new list is: ");
for (int i = 0; i < L.length; i++) {
printf("%d ",L.data[i]);
}
} else {
cout << "error." << endl;
}
return 0;
}