第十一周作业 include using namespace std;


这个作业属于哪个课程 C语言程序设计
这个作业要求在哪里 https://edu.cnblogs.com/campus/zswxy/software-engineering-class2-2018/homework/3201
这个作业在哪个具体方面帮我实现了目标 合理定义程序的多函数结构;能够使用递归函数进行编程;掌握宏的基本用法;掌握编译预处理的概念
参考文献 C语言程序设计和Cprimer plus


1.[选择题]
第十一周作业
include using namespace std;
第十一周作业
include using namespace std;

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);
    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,a,b);
    else {
        hanio (n-1,a,c,b);
        printf("%d: %c -> %c
",n,a,b);
        hanio(n-1,c,b,a);
    }
}

正确截图;
第十一周作业
include using namespace std;
错误截图:
第十一周作业
include using namespace std;
错误原因:
没有理解理解递归函数的真正意思,是调用函数本身,就单纯套用书本上的,移动方式虽然清楚但做起来就容易出现错误
实验流程图:
第十一周作业
include using namespace std;

3.[7-2 估值一亿的AI核心代码:]

第十一周作业
include using namespace std;
以上图片来自新浪微博。
本题要求你实现一个稍微更值钱一点的 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生效,那么我们就需要二级指针。

.二级指针操作

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("
");
}
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天 合理定义程序的多函数结构;能够使用递归函数进行编程;掌握宏的基本用法;掌握编译预处理的概念