第十一周作业
这个作业属于哪个课程 | C语言程序设计 |
---|---|
这个作业要求在哪里 | https://edu.cnblogs.com/campus/zswxy/software-engineering-class2-2018/homework/3201 |
这个作业在哪个具体方面帮我实现了目标 | 合理定义程序的多函数结构;能够使用递归函数进行编程;掌握宏的基本用法;掌握编译预处理的概念 |
参考文献 | C语言程序设计和Cprimer plus |
1.[选择题] | |
2.[7-1 汉诺塔问题] | |
汉诺塔是一个源于印度古老传说的益智玩具。据说大梵天创造世界的时候做了三根金刚石柱子,在一根柱子上从下往上按照大小顺序摞着64片黄金圆盘,大梵天命令僧侣把圆盘移到另一根柱子上,并且规定:在小圆盘上不能放大圆盘,每次只能移动一个圆盘。当所有圆盘都移到另一根柱子上时,世界就会毁灭。 |
请编写程序,输入汉诺塔圆片的数量,输出移动汉诺塔的步骤。
输入格式
圆盘数 起始柱 目的柱 过度柱
输出格式
移动汉诺塔的步骤
每行显示一步操作,具体格式为:
盘片号: 起始柱 -> 目的柱
其中盘片号从 1 开始由小到大顺序编号。
输入样例
3
a c b
输出样例
1: a -> c
2: a -> b
1: c -> b
3: a -> c
1: b -> a
2: b -> c
1: a -> c
实验代码;
#include<stdio.h>
void hanio(int n,char a,char b,char c);
int main (void)
{
int n;
char a,b,c;
scanf("%d\n",&n);
scanf("%c %c %c",&a,&b,&c);
hanio(n,a,b,c);
return 0;
}
void hanio (int n,char a,char b,char c)
{
if(n==1)
printf("%d: %c -> %c\n",n,a,b);
else {
hanio (n-1,a,c,b);
printf("%d: %c -> %c\n",n,a,b);
hanio(n-1,c,b,a);
}
}
正确截图;
错误截图:
错误原因:
没有理解理解递归函数的真正意思,是调用函数本身,就单纯套用书本上的,移动方式虽然清楚但做起来就容易出现错误
实验流程图:
3.[7-2 估值一亿的AI核心代码:]
以上图片来自新浪微博。
本题要求你实现一个稍微更值钱一点的 AI 英文问答程序,规则是:
无论用户说什么,首先把对方说的话在一行中原样打印出来;
消除原文中多余空格:把相邻单词间的多个空格换成 1 个空格,把行首尾的空格全部删掉,把标点符号前面的空格删掉;
把原文中所有大写英文字母变成小写,除了 I;
把原文中所有独立的 can you、could you 对应地换成 I can、I could—— 这里“独立”是指被空格或标点符号分隔开的单词;
把原文中所有独立的 I 和 me 换成 you;
把原文中所有的问号 ? 换成惊叹号 !;
在一行中输出替换后的句子作为 AI 的回答。
输入格式:
输入首先在第一行给出不超过 10 的正整数 N,随后 N 行,每行给出一句不超过 1000 个字符的、以回车结尾的用户的对话,对话为非空字符串,仅包括字母、数字、空格、可见的半角标点符号。
输出格式:
按题面要求输出,每个 AI 的回答前要加上 AI: 和一个空格。
输入样例:
6
Hello ?
Good to chat with you
can you speak Chinese?
Really?
Could you show me 5
What Is this prime? I,don 't know
输出样例:
Hello ?
AI: hello!
Good to chat with you
AI: good to chat with you
can you speak Chinese?
AI: I can speak chinese!
Really?
AI: really!
Could you show me 5
AI: I could show you 5
What Is this prime? I,don 't know
AI: what Is this prime! you,don't know
本题不会做
4.[***八皇后问题]
在国际象棋中,皇后是最厉害的棋子,可以横走、直走,还可以斜走。棋手马克斯·贝瑟尔 1848 年提出著名的八皇后问题:即在 8 × 8 的棋盘上摆放八个皇后,使其不能互相攻击 —— 即任意两个皇后都不能处于同一行、同一列或同一条斜线上。
现在我们把棋盘扩展到 n × n 的棋盘上摆放 n 个皇后,请问该怎么摆?请编写程序,输入正整数 n,输出全部摆法(棋盘格子空白处显示句点“.”,皇后处显示字母“Q”,每两格之间空一格)。
输入格式
正整数 n (0 < n ≤ 12)
输出格式
若问题有解,则输出全部摆法(两种摆法之间空一行),否则输出 None。
要求:试探的顺序逐行从左往右的顺序进行,请参看输出样例2。
输入样例1
3
输出样例1
None
输入样例2
6
输出样例2
. Q . . . .
. . . Q . .
. . . . . Q
Q . . . . .
. . Q . . .
. . . . Q .
. . Q . . .
. . . . . Q
. Q . . . .
. . . . Q .
Q . . . . .
. . . Q . .
. . . Q . .
Q . . . . .
. . . . Q .
. Q . . . .
. . . . . Q
. . Q . . .
. . . . Q .
. . Q . . .
Q . . . . .
. . . . . Q
. . . Q . .
. Q . . . .
本题不会做
预习作业:
预习作业:
···
第十二周的教学内容是:第十一章 指针进阶
请大家查阅资料,思考如下问题:
请举实例解释以下几个概念:数组指针,指针数组,指针函数,函数指针,二级指针,单向链表。(无实例不给分)
请用自己的理解回答。如果有引用他人的文字,请一定要标出出处(使用Markdown的链接方式)。
一、指针数组
是一个存放指针的数组。
eg:
int arr[];//[]优先级高,所以arr首先是一个数组,里面存放的是一个个指针int* arr[];//数组中放入二级指针
二、数组指针
是一个指向数组的指针。
eg:
int (arr)[10];//指向int[10]的指针解释:arr先和结合,说明arr是一个指针变量量,然后指针指向的是一个大小为10个整型
的数组。所以arr是一个指针,指向一个数组,叫数组指针
那么,数组的地址应该如何存放呢?
int arr[10] = {0};intp1 = &arr;int (p2)[10] = &arr;
看上面两种存放方式,显然,选择p2比较合适,因为p2是数组指针。
指针函数
定义
指针函数,简单的来说,就是一个返回指针的函数,其本质是一个函数,而该函数的返回值是一个指针。
声明格式为:*类型标识符 函数名(参数表)
这似乎并不难理解,再进一步描述一下。
看看下面这个函数声明:
int fun(int x,int y);
1
这种函数应该都很熟悉,其实就是一个函数,然后返回值是一个 int 类型,是一个数值。
接着看下面这个函数声明:
int *fun(int x,int y);
这和上面那个函数唯一的区别就是在函数名前面多了一个*号,而这个函数就是一个指针函数。其返回值是一个 int 类型的指针,是一个地址。
这样描述应该很容易理解了,所谓的指针函数也没什么特别的,和普通函数对比不过就是其返回了一个指针(即地址值)而已。
指针函数的写法
int fun(int x,int y);
int * fun(int x,int y);
int fun(int x,int y);
123
这个写法看个人习惯,其实如果*靠近返回值类型的话可能更容易理解其定义。
示例
来看一个非常简单的示例:
typedef struct _Data{
int a;
int b;
}Data;
//指针函数
Data* f(int a,int b){
Data * data = new Data;
data->a = a;
data->b = b;
return data;
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
//调用指针函数
Data * myData = f(4,5);
qDebug() << "f(4,5) = " << myData->a << myData->b;
return a.exec();
}
1234567891011121314151617181920212223
输出如下:
f(4,5) = 4 5
1
注意:在调用指针函数时,需要一个同类型的指针来接收其函数的返回值。
不过也可以将其返回值定义为 void*类型,在调用的时候强制转换返回值为自己想要的类型,如下:
//指针函数
void* f(int a,int b){
Data * data = new Data;
data->a = a;
data->b = b;
return data;
}
调用:
Data * myData = static_cast<Data*>(f(4,5));
12345678910
其输出结果是一样的,不过不建议这么使用,因为强制转换可能会带来风险。
函数指针
定义
函数指针,其本质是一个指针变量,该指针指向这个函数。总结来说,函数指针就是指向函数的指针。
声明格式:类型说明符 (*函数名) (参数)
如下:
int (*fun)(int x,int y);
1
函数指针是需要把一个函数的地址赋值给它,有两种写法:
fun = &Function;
fun = Function;
12
取地址运算符&不是必需的,因为一个函数标识符就表示了它的地址,如果是函数调用,还必须包含一个圆括号括起来的参数表。
调用函数指针的方式也有两种:
x = (*fun)();
x = fun();
12
两种方式均可,其中第二种看上去和普通的函数调用没啥区别,如果可以的话,建议使用第一种,因为可以清楚的指明这是通过指针的方式来调用函数。当然,也要看个人习惯,如果理解其定义,随便怎么用都行啦。
示例
int add(int x,int y){
return x+y;
}
int sub(int x,int y){
return x-y;
}
//函数指针
int (*fun)(int x,int y);
int main(int argc, char argv[])
{
QApplication a(argc, argv);
//第一种写法
fun = add;
qDebug() << "(fun)(1,2) = " << (fun)(1,2) ;
//第二种写法
fun = ⊂
qDebug() << "(fun)(5,2) = " << (*fun)(5,3) << fun(5,3);
return a.exec();
}
12345678910111213141516171819202122
输出如下:
(fun)(1,2) = 3
(fun)(5,2) = 2 2
12
上面说到的几种赋值和调用方式我都分别使用了,其输出结果是一样的。
二者区别
通过以上的介绍,应该都能清楚的理解其二者的定义。那么简单的总结下二者的区别:
###定义不同
指针函数本质是一个函数,其返回值为指针。
函数指针本质是一个指针,其指向一个函数。
写法不同
指针函数:int* fun(int x,int y);
函数指针:int (*fun)(int x,int y);
可以简单粗暴的理解为,指针函数的*是属于数据类型的,而函数指针的星号是属于函数名的。
再简单一点,可以这样辨别两者:函数名带括号的就是函数指针,否则就是指针函数。
在如下的A指向B、B指向C的指向关系中:
首先
C是"一段内容",比如你用malloc或者new分配了一块内存,然后塞进去"一段内容",那就是C了。C的起始地址是0x00000008。
B是一个指针变量,其中存放着C的地址,但是B也要占空间的啊,所以B也有地址,B的起始地址是0x00000004,但是B内存中存放的是C的地址,所以B里面的内容就是0x00000008。
那么到此为止都比较好理解:
B= 0x00000008; //B的内容 *B = "一段内容"; //B解引用,也就是B指针指向的C的值&B = 0x00000004; //B取地址,B的地址是0x00000004那么,再来看A:
A是二级指针变量,其中存放着B的地址0x00000004,A也有地址,是0x00000000;
*A = B= 0x00000008; //A解引用也就是B的内容 **A = *B = "一段内容"; //B解引用,也就是B指针指向的C的值A = &B = 0x00000004; //A存的是B的地址,B的地址是0x00000004&A = 0x00000000; //A取地址
二、使用
二级指针作为函数参数的作用:在函数外部定义一个指针p,在函数内给指针赋值,函数结束后对指针p生效,那么我们就需要二级指针。
.二级指针操作
include using namespace std;
int a= 10;int b = 100;int q; void func(int **p) //2
{
cout<<"func:&p="<<&p<<",p="<<p<<endl;
p = &b; //3
cout<<"func:&p="<<&p<<",p="<<p<<endl;
}
int main()
{
cout<<"&a="<<&a<<",&b="<<&b<<",&q="<<&q<<endl;
q = &a;
cout<<"q="<<q<<",q="<<q<<",&q="<<&q<<endl;
func(&q); //1
cout<<"q="<<q<<",q="<<q<<",&q="<<&q<<endl;
system("pause");
return 0;
}
--这里只改了三个地方,变成传二级指针。我们再看:
因为传了指针q的地址(二级指针**p)到函数,所以二级指针拷贝(拷贝的是p,一级指针中拷贝的是q所以才有问题),(拷贝了指针但是指针内容也就是指针所指向的地址是不变的)所以它还是指向一级指针q(*p = q)。在这里无论拷贝多少次,它依然指向q,那么*p = &b;自然的就是
q = &b;了。
链表是离散存储线性结构
n个节点离散分配,彼此通过指针相连,每个节点只有一个前驱节点,每个节点只有一个后续节点,首节点没有前驱节点,尾节点没有后续节点。
链表优点:
空间没有限制
插入删除元素很快
链表缺点:
存取速度很慢
链表相关术语介绍,我还是通过上面那个图来说明吧:
确定一个链表我们只需要头指针,通过头指针就可以把整个链表都能推导出来了~
链表又分了好几类:
单向链表
一个节点指向下一个节点
双向链表
一个节点有两个指针域
循环链表
能通过任何一个节点找到其他所有的节点,将两种(双向/单向)链表的最后一个结点指向第一个结点从而实现循环
操作链表要时刻记住的是:
节点中指针域指向的就是一个节点
实例:
#include <stdio.h>
#include <stdlib.h>
struct grade {
int score;
struct grade *next;
};
typedef struct grade NODE; //typedef为C语言的关键字,作用是为一种数据类型定义一个新名字。
//使用typedef目的一般有两个,一个是给变量一个易记且意义明确的新名字,
//另一个是简化一些比较复杂的类型声明。
struct grade *create(); //创建链表
void insert(NODE *head,NODE *pnew,int i); //插入链表
void pdelete(NODE *head,int i); //删除列表
void display(NODE *head); //输出链表
void Pfree(NODE *head); //销毁链表
int main(int argc, char *argv[]) {
struct grade *head,*pnew;
head=create();
if (head==NULL)
return 0;
printf("输出创建的链表:");
display(head);
pnew=(NODE *)malloc(sizeof(NODE));
if (pnew==NULL) {
printf("创建失败!");
return 0;
}
pnew->score=88;
insert(head,pnew, 3); //将新节点插入节点3的后面
printf("插入后的链表:");
display(head);
pdelete(head,3); //删除节点3
printf("删除后的链表:");
display(head);
Pfree(head);
return 0;
}
struct grade *create() {
NODE *head,*tail,*pnew;
int score;
head=(NODE *)malloc(sizeof(NODE)); //创建头节点。
if (head==NULL) { //创建失败返回
printf("创建失败!");
return NULL;
}
head->next=NULL; //头节点指针域置NULL
tail=head; // 开始时尾指针指向头节点
printf("输入学生成绩:");
while (1) { //创建链表
scanf("%d",&score);
if (score<0) //成绩为负是退出循环
break;
pnew=(NODE *)malloc(sizeof(NODE)); //创建新节点
if (pnew==NULL) { //创建失败返回
printf("创建失败!");
return NULL;
}
pnew->score=score; //新节点数据域存放输入的成绩
pnew->next=NULL; //新节点指针域置NULL
tail->next=pnew; //新节点插入到表尾
tail=pnew; //为指针指向当前的尾节点
}
return head; //返回创建链表的头指针
}
void insert(NODE *head,NODE *pnew,int i) {
NODE *p; //当前指针
int j;
p=head;
for (j=0; j<i&&p!=NULL; j++) //p指向要插入的第i个节点
p=p->next;
if (p==NULL) { //节点i不存在
printf("与插入的节点不存在!");
return;
}
pnew->next=p->next; //插入节点的指针域指向第i个节点的后继节点
p->next=pnew; //犟第i个节点的指针域指向插入的新节点
}
void pdelete(NODE *head,int i) {
NODE *p,*q;
int j;
if (i==0) //删除的是头指针,返回
return;
p=head;
for (j=1; j<i&&p->next!=NULL; j++)
p=p->next; //将p指向要删除的第i个节点的前驱节点
if (p->next==NULL) { //表明链表中的节点不存在
printf("不存在!");
return;
}
q=p->next; //q指向待删除的节点
p->next=q->next; //删除节点i,也可写成p->next=p->next->next
free(q); //释放节点i的内存单元
}
void display(NODE *head) {
NODE *p;
for (p=head->next; p!=NULL; p=p->next)
printf("%d ",p->score);
printf("\n");
}
void pfree(NODE *head) {
NODE *p,*q;
p=head;
while (p->next!=NULL) { //每次删除头节点的后继节点
q=p->next;
p->next=q->next;
free(q);
}
free(head); //最后删除头节点
}
void Pfree(NODE *head) {
NODE *p,*q;
p=head;
while (p->next!=NULL) {
q=p->next;
p->next=q->next;
free(q);
}
free(p);
}
原文:https://blog.csdn.net/majianfei1023/article/details/46629065
链接出处:<https://blog.csdn.net/luoyayun361/article/details/80428882 >
链接出处:<https://blog.csdn.net/weixin_40417029/article/details/78580080 >
学习总结:
后面的大题都看不懂,知识感觉太薄弱
结对编程总结:
感觉就还好,互相都有进步
学习计划:
时间 | 代码行数 | 这周花的时间 | 学到的知识点 |
---|---|---|---|
3/2-3/12 | 50 | 5天 | 主要是文件的相关问题 |
3/9-3/20 | 70 | 5天 | 一维数组 |
3/20-3/30 | 80 | 5天 | 二位数组 |
3/22-3/28 | 100 | 5天 | 字符串的使用 |
3/28-4/5 | 80 | 5天 | 指针 |
4/5-4/12 | 124 | 5天 | 指针的具体操作 |
4/14-4/19 | 113 | 5天 | 字符串函数以及使用指针操作字符串的方法和动态内存分配 |
4/21-4/26 | 68 | 5天 | 使用结构变量与结构数组进行编程,掌握结构指针的操作,并应用于函数传递 |
4/29-5/3 | 74 | 5天 | 怎样花两年时间面试一个人,如何有效地记忆与学习?以及如何提问? |
5/5-5/10 | 72 | 5天 | 合理定义程序的多函数结构;能够使用递归函数进行编程;掌握宏的基本用法;掌握编译预处理的概念 |