一、作業頭內容
這個作業屬於那個課程 | C語言程序設計II |
---|---|
這個作業要求在哪里 | C語言作業評價標准 |
我在這個課程的目標是 | 學習二級指針的概念;掌握指針數組的應用方法;理解指針與函數的關系,學習指針作為函數返回值的運用。 |
這個作業在那個具體方面幫助我實現目標 | 能更好的消化理論課上學習的指針進階,像加強對函數指針和指針函數的理解。 |
參考文獻 | C語言頭文件#include<stdlib.h>的作用 ; typedef語法 ; C語言程序設計 。 |
二、基礎作業:
PTA:
1.函數題:計算最長的字符串長度:
2.函數題:統計專業人數:
3.函數題:刪除單鏈表偶數結點:
第一題:
(1、6-1 計算最長的字符串長度
本題要求實現一個函數,用於計算有n個元素的指針數組s中最長的字符串的長度。
函數接口定義:
int max_len( char *s[], int n );
其中n
個字符串存儲在s[]
中,函數max_len
應返回其中最長字符串的長度。
裁判測試程序樣例:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAXN 10
#define MAXS 20
int max_len( char *s[], int n );
int main()
{
int i, n;
char *string[MAXN] = {NULL};
scanf("%d", &n);
for(i = 0; i < n; i++) {
string[i] = (char *)malloc(sizeof(char)*MAXS);
scanf("%s", string[i]);
}
printf("%d\n", max_len(string, n));
return 0;
}
/* 你的代碼將被嵌在這里 */
輸入樣例:
4
blue
yellow
red
green
輸出樣例:
6
(2、程序代碼:
int max_len( char *s[], int n )
{
int i=0,m=0;
for(;i<n;i++)
{
if( strlen(s[m]) < strlen(s[i]) )
{
m=i;
}
}
return strlen(s[m]);
}
(3、設計思路,流程圖:
(4、本題調試過程碰到的問題及解決辦法。
錯誤截圖:
問題:
提示返回值未聲明,經檢查發現在for循環內定義也只在for循環里有效。
解決辦法:
將要作為返回值得定義變量放置for循環外即可。
(5、程序運行結果的截圖或者效果錄像。
正確截圖:
第二題:
(2、6-2 統計專業人數
本題要求實現一個函數,統計學生學號鏈表中專業為計算機的學生人數。鏈表結點定義如下:
struct ListNode {
char code[8];
struct ListNode *next;
};
這里學生的學號共7位數字,其中第2、3位是專業編號。計算機專業的編號為02。
函數接口定義:
int countcs( struct ListNode *head );
其中head是用戶傳入的學生學號鏈表的頭指針;函數countcs統計並返回head鏈表中專業為計算機的學生人數。
裁判測試程序樣例:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct ListNode
{
char code[8];
struct ListNode *next;
};
struct ListNode *createlist(); /*裁判實現,細節不表*/
int countcs( struct ListNode *head );
int main()
{
struct ListNode *head;
head = createlist();
printf("%d\n", countcs(head));
return 0;
}
/* 你的代碼將被嵌在這里 */
輸入樣例:
1021202
2022310
8102134
1030912
3110203
4021205
#
輸出樣例:
3
(2、實驗代碼:
int countcs( struct ListNode *head )
{
int sum = 0;
struct ListNode *p=head;
while(p != NULL)
{
if(p->code[1] == '0' && p->code[2] == '2')
sum++;
p = p->next;
}
return sum;
}
(3、設計思路,流程圖:
(4、本題調試過程碰到的問題及解決辦法。
錯誤截圖:
問題:
在相關應用程序中提示語句錯誤,語句“struct ListNode *head=p;”有誤;原因是單鏈表的基本操作出錯。
解決辦法:
將語句修改為“struct ListNode *p=head;”。
(5、程序運行結果的截圖或者效果錄像。
正確截圖:
第三題:
(3:題目:6-3 刪除單鏈表偶數節點
本題要求實現兩個函數,分別將讀入的數據存儲為單鏈表、將鏈表中偶數值的結點刪除。鏈表結點定義如下:
struct ListNode
{
int data;
struct ListNode *next;
};
函數接口定義:
struct ListNode *createlist();
struct ListNode *deleteeven( struct ListNode *head );
函數createlist
從標准輸入讀入一系列正整數,按照讀入順序建立單鏈表。當讀到−1時表示輸入結束,函數應返回指向單鏈表頭結點的指針。
函數deleteeven
將單鏈表head
中偶數值的結點刪除,返回結果鏈表的頭指針。
裁判測試程序樣例:
#include <stdio.h>
#include <stdlib.h>
struct ListNode {
int data;
struct ListNode *next;
};
struct ListNode *createlist();
struct ListNode *deleteeven( struct ListNode *head );
void printlist( struct ListNode *head )
{
struct ListNode *p = head;
while (p) {
printf("%d ", p->data);
p = p->next;
}
printf("\n");
}
int main()
{
struct ListNode *head;
head = createlist();
head = deleteeven(head);
printlist(head);
return 0;
}
/* 你的代碼將被嵌在這里 */
輸入樣例:
1 2 2 3 4 5 6 7 -1
輸出樣例:
1 3 5 7
(2、程序代碼:
typedef struct ListNode *s;
struct ListNode *createlist()
{
s head=NULL, tail=NULL;
int n;
scanf("%d",&n);
while(n != -1)
{
s q = (s)malloc(sizeof(struct ListNode));
q->data = n;
q->next = NULL;
if(tail==NULL)
head = tail = q;
else
{
tail->next = q;
tail = q;
}
scanf("%d",&n);
}
return head;
}
struct ListNode *deleteeven( struct ListNode *head )
{
s newhead=NULL,p=head,tail;
while(p)
{
if(p->data%2==1)
{
s q = (s)malloc( sizeof(struct ListNode) );
q->data = p->data;
q->next = NULL;
if(newhead==NULL)
newhead = tail = q;
else
{
tail->next = q;
tail = q;
}
}
p = p->next;
}
return newhead;
}
(3、設計思路,流程圖:
(4、本題調試過程碰到的問題及解決辦法
錯誤截圖:
問題:
通過PTA反饋提示運行超時;而相關應用程序中檢測顯示程序代碼正確,表明邏輯應該正確。
解決辦法:
只能通過調試去設立斷點與根據數值變化,來判斷問題所在,在修改數據。
(5、程序運行結果的截圖或者效果錄像。
正確截圖:
三、挑戰作業:
PTA:
1.編程題:八皇后問題(用遞歸法)實現
2.編程題:求迷宮最短通道
(1:題目:7-1 ***八皇后問題
在國際象棋中,皇后是最厲害的棋子,可以橫走、直走,還可以斜走。棋手馬克斯·貝瑟爾 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 . . . .
(2、程序代碼:
#include <stdio.h>
#include <math.h>
int abc(int k);
int def(int k);
int hij(int i);
#define N 12 //N代表n皇后,其中正整數 n (0 < n ≤ 12);
int x[N], n, m=0, num=0; //n代表階數 或 皇后個數 ;num代表存在的“全部擺法 ”的個數 ;
//m主要為可行解是否存在;
//以上變量與數組全為“全局變量 ” ;
int main (void)
{
scanf("%d",&n);
num=abc(1);
if(m==0)
printf("None");
return 0;
}
int abc(int k)
{
if(k>n)
{
m=1;
num++;
hij(1);
}
else
for(int i=1;i<=n;i++)
{
x[k]=i;
if(def(k))
abc(k+1);
}
//m=1;
return num;
}
int def(int k)
{
for(int i=1;i<k;i++)
if(abs(k-i)==abs(x[k]-x[i])||x[k]==x[i])
return 0;
return 1;
}
int hij(int i)
{
if(num==1)
{
for(int j;i<=n;i++)
{
for(j=1;j<x[i];j++) printf(". ");
printf("Q");
for(j=x[i]+1;j<=n;j++) printf(" .");
printf("\n");
}
}
else //if(num>1)
{
printf("\n");
for(int j;i<=n;i++)
{
for(j=1;j<x[i];j++) printf(". ");
printf("Q");
for(j=x[i]+1;j<=n;j++) printf(" .");
printf("\n");
}
}
}
(3、設計思路,流程圖:
(4、本題調試過程碰到的問題及解決辦法
錯誤截圖:
問題:
PTA上全提示格式錯誤部分正確;而實驗代碼邏輯符合題意;結果也正確,問題是結尾部分總是多出一行在PTA上不符合格式要求。
解決辦法:
通過提示,針對方向為實驗代碼的格式,猜想問題出在換行符;由函數反復遞歸,代碼執行解讀來太抽象,邏輯設計需調試可得出改進思路,要修改為結尾的兩行;重新設計輸出時格式,首先可先輸出第一次后面按規則輸出即可。
(5、程序運行結果的截圖或者效果錄像。
正確截圖:
(2:題目:7-2 求迷宮最短通道
- 遞歸求解迷宮最短通道的總步長。
- 輸入一個迷宮,求從入口通向出口的可行路徑中最短的路徑長度。
- 為簡化問題,迷宮用二維數組 int maze[10][10]來存儲障礙物的分布,假設迷宮的橫向和縱向尺寸的大小是一樣的,並由程序運行讀入, 若讀入迷宮大小的值是n(3<n<=10),則該迷宮橫向或縱向尺寸都是n,規定迷宮最外面的一圈是障礙物;
- 迷宮的入口是maze[1][1],出口是maze[n-2][n-2], 若maze[i][j] = 1代表該位置是障礙物,若maze[i][j] = 0代表該位置是可以行走的空位(0<=i<=n-1, 0<=j<=n-1)。求從入口maze[1][1]到出口maze[n-2][n-2]可以走通的路徑上經歷的最短的總步長。
- 要求迷宮中只允許在水平或上下四個方向的空位上行走,走過的位置不能重復走。
輸入格式:
輸入迷宮大小的整數n, 以及n行和n列的二維數組(數組元素1代表障礙物,0代表空位)
輸出格式:
若有可行的通道則輸出一個整數,代表求出的通道的最短步長;若沒有通道則輸出`"No solution"`。
輸入樣例:
10
1 1 1 1 1 1 1 1 1 1
1 0 0 1 0 0 0 1 0 1
1 0 0 1 0 0 0 1 0 1
1 0 0 0 0 1 1 0 0 1
1 0 1 1 1 0 0 0 0 1
1 0 0 0 1 0 0 0 0 1
1 0 1 0 0 0 1 0 0 1
1 0 1 1 1 0 1 1 0 1
1 1 0 0 0 0 0 0 0 1
1 1 1 1 1 1 1 1 1 1
上述輸入代表的是如下這樣一個迷宮:
其中紅色的小方塊是障礙物,藍色的小方塊是空位,白色的小圓連起來是一條從入口到出口的通道,兩個圓之間代表一個步長。
輸出樣例:
14
(2、程序代碼:
#include<stdio.h>
#define M 10
struct P
{
int x;
int y;
};
int m[M][M],n,z = 10000;
struct P b;
struct P d[4]={{0,1},{1,0},{0,-1},{-1,0}};
void Try(struct P c,int t)
{
int i;
struct P q; // 下一個位置
for(i=0;i<=3;i++) // 動方向,依次為東南西北;依次試探下左上右四個方向
{
q.x=c.x+d[i].x;
q.y=c.y+d[i].y;
if(m[q.x][q.y] == 0) // 是通路
{
m[q.x][q.y]=++t;
if(q.x != b.x || q.y != b.y) // 沒到終點
Try(q,t); // 試探下一點(遞歸調用)
else
z = z>t?t:z;
m[q.x][q.y]=0; // 恢復為通路,試探下一條路
t--;
}
}
return;
}
int main(void)
{
struct P h; //起點
int i,j;
while(scanf("%d",&n)!=EOF)
for(i=0;i<n;i++)
for(j=0;j<n;j++)
scanf("%d",*(m+i)+j); //二維數組的指針調用
h.x = 1,h.y=1;
b.x = n-2,b.y= n-2;
m[h.x][h.y]=2;
Try(h,0); // 由第一步起點試探起
if(z == 10000)
printf("No solution");
else
printf("%d",z);
return 0;
}
(3、設計思路:
給定一個迷宮,此迷宮中有且僅有一個入口和出口,其中設有若干檢查點,要求從入口開始,經過所有檢查點后到達出口所需的最短路徑。其中路徑中允許多次經過入口或出口或某檢查點,但路徑的開始和結尾必須分別是入口和出口。更形象一點就是要把圖中所有的寶藏找出來帶出去的問題。
算法的核心描述就是先假設已經有一個最優路徑,然后插入一個新的檢查點時,如何使插入導致的路徑增量最小。
這種先假設存在最優路徑然后再在這條假設的路徑中插入新的結點的思想其實就是貪心算法,出現在很多算法當中,並且十分行之有效,其基本模型就是持有兩個關鍵集合,根據條件改變集合元素來動態規划集合中元素的組織方式。此題中的尋寶算法十分類似於貨郎擔問題,區別在於,貨郎在尋找旅行路線時不能經過已經走過的城市,且最終要回到出發點,而這里的模型是可以經過已經走過的城市,但最終不是要回到出發點,而是以達到一個指定點結束。但本質上都需要兩個動態集合來保存這些代表地點的結點。貨郎擔問題相對於此問題的簡單之處表現在其不重復的特點上,因為不重復,所以每個結點必有單前驅與單后繼,集合中元素都不相同,組合起來就很簡單。而此問題由於可以多次經過同一個節點,所以在一條路徑上就存在多個相同的結點,這就導致了同一結點有多前驅或多后繼的問題。本問題解決的關鍵就是要解決這種多前驅與多后繼的問題,也就是要決定同一結點如何在集合中表現出多種身份的方式。我在設計此算法時,到此便知道這就是問題的核心了,最終想到的辦法就是為多前驅或多后繼的結點建立多個虛擬結點,這些虛擬結點實質上共享同一個位置,但卻分別有着不同的前驅與后繼,因此可以構成一條完成的路線。然后問題就轉移到如何在兩個集合中標識這些虛擬結點的身份了。剩下的問題不再是難題,除了需要計算在插入新結點時如何查找最優插入區間、如何計算插入后帶來的代價增量、如何決定是采用正常插入模式還是采用創建新的虛擬結點的插入方式等問題了。其中用到了算法導論一書中介紹的“三角不等式(兩邊之和大於第三邊,兩邊之差小於第三邊)”原理來計算和比較代價增量。
最后,如設計算法時所描述,這其實是通過一個循環就可以實現四方向移動的遍歷。
引用:程序設計:迷宮的最短路徑
(4、本題調試過程碰到的問題及解決辦法
錯誤截圖:
問題:
該代碼借鑒c++類型,用c語言求解難免不對型,故出錯。
解決辦法:
了解和借鑒其思路改寫為C語言型的代碼,利用已學知識進行編寫新代碼。
(5、程序運行結果的截圖或者效果錄像。
正確截圖:
挑戰題的算法上有些什么新的想法和改進?
首先,遞歸算是入門,已經較為清晰的理解與學會遞歸;按我的理解,遞歸用一個形象的比喻,就是查詞典。我們查詞典的過程,本身就是遞歸。想象用一本純英文詞典查單詞,要查某一個單詞的意思,翻到這個單詞時,看解釋,發現解釋中有一個單詞不認識,所以,無法明白這個要查的單詞是什么意思;這時,再用這本詞典(函數本身)查那個不認識的單詞,又發現查的第2個單詞的解釋中又有一個單詞不認識,那么,又再用這本詞典查第3個不認識的單詞,這樣,一個一個查下去,直到解釋中所有單詞都認識,這樣就到底了,就明白了最后一個單詞是什么意思,然后一層一層`倒回來`,就知道我最初想查的第1個單詞是什么意思了,其中也可能不會直接就順利的一層層倒回來,其中可能繼續在某一基礎上繼續要去查其他的單詞,在返回那一層,直到返回到初始想知道的單詞,問題就解決了。
第二,對n皇后的流程圖進行新的制作,更加詳細一些,代碼本就完善的較好了所以未做修改。
第三,對第二題的挑戰題《求迷宮最短通道》,上次是借用c++的編程環境和函數進行引用的代碼,這周編寫為c語言型代碼,更能助於理解和學習。
我向哪些同學進行了挑戰題的講解和我是如何講解的?
暫無。
四、預習作業:
從第十三周開始,將進入課程設計階段,在本次作業中給出:
1.所在小組想要開發的項目的名稱和目標;
根據一定程度上C語言基礎,小組討論決定制作的小游戲名稱被命名為《小鬼戰斗機》的飛行游戲,
目標:軟件能順利運行,能靈活控制。
2.項目主體功能的描述;
1、頁面初始化;
2、按規律發射子彈;
3、敵機移動;
4、自動產生新敵機;
……
3.現階段已做的准備工作;
參考資料:《C語言課程設計與游戲開發實踐教程》;
……
4.小組成員名單和進度安排。(課程設計階段:13-17周)
組員:陳小強、鄧 鵬。
安排:先了解軟件制作的流程。
……
五、學習進度統計和學習感悟 :
1)、進度條:
2)、學習進度表:
第N周 | 日 期 | 這周花的時間 | 代碼行數 | 學到的知識點簡介 | 目前比較迷惑的問題 |
---|---|---|---|---|---|
第十二周 | 5/13-/18 | 計12小時 | 480 | 指針進階:指針數組,數組指針,指針函數,函數指針 | 全局變量中靜態變量與動態變量不太清楚 |
第十一周 | 5/5-/10 | 計9小時 | 580 | 多函數結構;遞歸函數;宏;編譯譯預處理的概念 | -- |
第十周 | 4/28-5/4 | 計16小時 | 110 | 結構指針 | -- |
第九周 | 4/22-/26 | 計14小時 | 360 | 結構指針的概念、結構指針作為函數參數 | -- |
第八周 | 4/15-/19 | 計13小時 | 150 | 動態內存分配,字符指針 | -- |
第七周 | 4/8-/11 | 計11小時 | 520 | 復習冒泡排序法;詳解指針、數組與地址間的關系;指針之間的運算 | -- |
第六周 | 3/29-4/5 | 計 9小時 | 390 | 指針變量的初始化、指針作為函數參數、指針變量的賦值與運算 | -- |
第五周 | 3/25-/28 | 計 8小時 | 200 | 字符串的基本概念、操作方法 | -- |
第四周 | 3/18-/22 | 計 8小時 | 400 | 冒泡排序法、選擇排序法、二維數組 | -- |
第三周 | 3/11-/16 | 計 4小時 | 150 | 指針、二維數組 | -- |
3)、學習感悟:
(1)本周你學習哪些內容(不限於課上)?你有哪些收獲?
主要學習為指針進階部分,通過課堂學習和例題分析,其中更深刻的理解指針數組、數組指針與指針函數,和函數指針有了初步了解。
(2)本周所學內容中你覺得哪些地方是難點?對此你做了哪些措施去克服這些困難?
我認為較難的在於數組指針和指針數組的區別,首先數組指針的定義可以為: int (*p)[n];而指針數組的定義是 int *p[n];兩者看似相差一個括號卻優先級不同,含義有很大的差別,數組指針只是一個指針變量,似乎是C語言里專門用來指向二維數組的,它占有內存中一個指針的存儲空間。指針數組是多個指針變量,以數組形式存在內存當中,占有多個指針的存儲空間。
六、結對編程感受:
1)、過程:
本周的編程作業基礎題都是先理解題目給出的程序代碼在根據題目要求補寫自定義函數;以完善程序;我們先一起討論各自對已有代碼的解讀,再設計邏輯,因為有些解法很多但又復雜,且思路形象來表達簡單,可以很好的進行建立各自觀點,再對代碼脈絡進行編寫與修改;
兩人一起編寫代碼,再通過相關軟件運行進行檢查代碼,有問題一起檢查與修改。
最后,進行討論,對問題理解清晰、相對簡單的題目談論不同思路。
2)、照片:
3)、看法:
一起結對編程是真的可以節省時間與提高效率。