C博客作業04--數組


第四次C博客作業


Q0.展示PTA總分

  • 一維數組

  • 二維數組

  • 字符數組


Q1.本章學習總結

1.1 學習內容總結

  • 數組中數據的查詢
    • 遍歷查找法:顧名思義,將數組中的數據都遍歷一遍,如果數組都遍歷完了則說明沒有找到數據,反之即找到了目標數據
    • 二分查找法:注意使用的前提是數組中的元素有序排列!如果不是有序排列則需要在查找前將數組排序。二分查找具體操作為:將數組正中間的元素與查找的數據比較,兩者相等即為找到了,否則將數組分為左右兩個部分,如果中間的元素大於查找的數據,則進一步查找左半部分,否則進一步查找右半部分,重復以上過程。
/*遍歷查找和二分查找的簡單樣例:輸出目標數據在數組中的位置*/
	int a[5] = { 1,2,3,4,5 };
	輸入要查找的數據
	for i=0 to 4 do   //遍歷查找法
		if 查找的數據等於數組中當前下標的元素 then
			輸出i並結束循環
		end if
        end for

	int left = 0, right = 4;   //二分查找法。左部開始為0,右部開始為數組長度減一
	int mid;
	while left的值小於right    //循環條件,如果left>right則說明沒有找到目標數據
		mid = (left + right) / 2;   //這句是關鍵
		if 查找的數據等於數組中當前下標的元素 then
			輸出i並結束循環
		else if 查找的數據大於中間元素 then   //如果沒有找到則將左部或右部折半
			left = mid + 1;
		else
			right = mid - 1;
        end if
	end while
  • 數組中數據的插入
    • 一個思路是將插入的數據和數組中已有的數據做比較,記錄下比插入的數據大的數組元素的下標,將數據插入到該位置,再將后面所有的元素后移動一位
    • 也可以將數據直接插入到數組的末尾,再與前面的元素逐個進行比較、交換的操作
/*插入新數據到數組中的簡單樣例*/
	int a[6] = { 1,2,3,4,5 };
	輸入要插入的新數據insert
	for i=0 to 4 do   //尋找第幾個元素比插入的數據大
		if 當前的元素大於插入的數據 then
			儲存當前i為index並結束循環
		end if
        end for

	for i=5 to index do   //將index后的元素全部右移1位
		a[i] = a[i - 1];
	end for
	a[index] = insert;  //再將要插入的數據傳給記錄的位置
     
    a[5] = insert;        //先插末尾再排序的方法
    for i=5 to 0 do
        if a[i]的值小於a[i-1]的值 then
                交換a[i]和a[i-1]
        end if
    end for
  • 數組中數據的刪除
    • 可以遍歷數組,當數組當前元素等於要刪除的數據時直接將后面的元素全部左移1位,將該元素覆蓋掉,從而實現刪除
    • 或是再定義一個數組,用來儲存原數組中要留下的元素
/*刪除數組中的元素的簡單樣例*/
	int a[5] = { 1,2,3,4,5 };
	輸入要刪除的數據del
	for i=0 to 4 do   
		if 當前的元素等於要刪除的數據 then
			儲存當前i為index並結束循環
		end if
        end for

	for i=index to 4 do   //將index后的元素全部左移1位
		a[i] = a[i + 1];
	end for
     
    int b[5];        //再定義一個數組的方法
    定義數組b的下標j,長度k,初值均為0
    for i=0 to n do
        if a[i]的值不等於要刪除的值 then
                b[j++]=a[i];
                k++;
        end if
    end for
  • 數組的排序
    • 選擇法:對數組中固定的一個位置和后面的所有元素逐個進行比較,它每輪會將最小的數放到最前
    • 冒泡法:數組按順序對兩個相鄰的元素進行比較,它每輪會將最大的數放到最后
    • 用一張圖表示一下它們的區別(畫的都是第一輪比較)

/*冒泡排序和選擇排序的簡單樣例*/
    輸入數組長度n;
    輸入n個數組元素;
    
    for i=0 to n do     //選擇法排序
	for j=i+1 to n do
		if a[i]的值比a[j]的值大
			交換a[i]和a[j]

	for i=1 to n do   //冒泡法排序,這個循環控制冒泡的次數
	for j=0 to n-i do
		if a[j]的值大於a[j+1]
			交換a[j]和a[j+1]
  • 數組做枚舉用法
    • 說實話我並沒有看懂這個是什么意思,百度到的也都是enum的枚舉類型,只能按我個人的理解寫了
    • 個人感覺是將數組的元素都列舉出來,之后直接使用的意思,這里我直接用2840題庫中的查找星期題目來做例子吧
#include <stdio.h>
#include <string.h>

#define MAXS 80

int getindex(char* s);

int main()
{
	int n;
	char s[MAXS];

	scanf("%s", s);
	n = getindex(s);
	if (n == -1) printf("wrong input!\n");
	else printf("%d\n", n);

	return 0;
}
int getindex(char* s)
{
	const char week[7][10] = { "Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday" };   //將所有元素都枚舉出來,賦予week[0]到week[6],例如在后面使用week[5]即代表字符串Friday
	int i;

	for (i = 0; i < 7; i++)
	{
		if (strcmp(week[i], s) == 0)   //比較輸入的字符串是否與week數組中的元素相同
			return i;
	}

	return -1;
}
  • 哈希數組
    • 散列表(Hash table,也叫哈希表),是根據關鍵碼值(Key value)而直接進行訪問的數據結構。也就是說,它通過把關鍵碼值映射到表中一個位置來訪問記錄,以加快查找的速度。這個映射函數叫做散列函數,存放記錄的數組叫做散列表。

引用自哈希表的百度百科

  • 根據上面的資料我們可以知道哈希數組是按照一個key值直接訪問該位置儲存的元素(我的推測)
  • 目前我們做過的跟哈希數組有關的題目例子:有重復的數據,就是直接將數據元素作為下標,直接查找里面的內容
  • 一般的方法我們選擇按順序儲存輸入的內容,再通過遍歷的方式來判斷有無重復,但在這題中如果這樣寫,又遇到輸入100000個不同數的極端情況,那么我們的循環要走100000x100000遍,導致運行超時,所以我們選擇直接將輸入的數據作為下標,元素內容為該數字出現的次數,當輸入的數字出現次數超過1時即有重復數據,結束循環。這樣子寫即使遇到最極端的情況,循環也只需要走100000遍,運行的效率大大提高了
#include<stdio.h> 

int main()
{
	int a[100001] = { 0 };   //初始化數字1-100000的出現次數均為0
	int n;
	int i;
	int num;

	scanf("%d", &n);
	for (i = 0; i < n; i++)
	{
		scanf("%d", &num);
		a[num]++;
		if (a[num] > 1)     //如果這個數字出現超過1次即有重復數據,直接結束循環
		{
			printf("YES");
			break;
		}
	}

	if (i >= n)   //如果上面這個循環能走完就說明沒有重復數據
		printf("NO");

	return 0;
}

1.2 本章學習體會

  • 數組對前面部分的內容的綜合性較強,要求對循環、分支等結構內容要有一定的熟練度
  • 題目的難度又又又提升了,但還是不能ctrl c+v,也最好不要說什么“我看看答案,看懂了就可以直接復制過來了”之類的話,如果不動手寫的話真的很容易就忘了(大腦:我懂了 手:不你不懂)
  • 個人認為數組的概念和使用部分並不難理解,只要稍加練習和思考都能做出來
  • 遇到小問題試着問一下同學/老師,有時候就是一點小錯誤反而找不出來,別人一下就能發現,但也不能啥問題都一股腦的都丟給別人,別人看着講的累,自己聽的也累
  • 代碼量統計(題目太難了刷不上去了

Q2.PTA實驗作業

2.1 閱覽室

2.1.1 偽代碼

	定義day記錄要輸入幾天的數據
	定義count記錄當前為第幾天
	定義hour、minute、number、ch記錄借書時間、書號、借出/歸還
	定義數組book行長1001,列長6,其中每列代表如下:0-S,1-E,2、4-借出去的時間,3、5-歸還的時間
	定義數組borrow、totaltime、avgtime記錄每天借出去的書本數量、總閱讀時間、平均閱讀時間

	輸入總天數
	for count=1 to day do
		讓book數組里的全部內容均為0
		輸入書號、借出/歸還、小時、分鍾
		while 書號不為0 do
			if 輸入了S then
				記錄該書號S、借出的小時和分鍾
			end if
			else if 輸入了E且該書號之前有及記錄過S then
				記錄該書號E、歸還的小時和分鍾
			end if
			if 該書號有同時記錄S和E then
				當天借書量+1
				計算閱讀時間加到當天總閱讀時間內
				清空該書號的S、E記錄
			end if
			輸入書號、借出/歸還、小時、分鍾
		end while
		if 該天的借書量不為0 then
			計算平均閱讀時間
	end for

	輸出每天的借書量和平均閱讀時間

	return 0;

2.1.2 代碼截圖

2.1.3 造數據測試

輸入數據 輸出數據 備注
題目例子
0 0 有連續S和連續E,有S和E全反
2 90 同一本書被借走多次

2.1.4 PTA提交列表及說明

  • 第一個部分正確:連續S E和同一本書被借多次測試點未過,嘗試在寫入E的條件判斷中加入前面要有讀入過S
  • 第二個部分正確:這兩個測試點還是沒有過,經測試后發現問題沒有解決,在S E都讀入計算時間后將該書號的S E都清空
  • 答案正確:經過上面兩次修正后成功通過

2.2 螺旋方陣

2.2.1 偽代碼

	定義number為要螺旋輸出的數,初值為1
	定於circle記錄要轉幾圈才能輸出完畢
	定義數組a行長列長均為10

	輸入行數n

	for circle=1 to (n + 1) / 2 do   
		for j=circle-1 to n-circle do
			a[circle - 1][j] = number;
			number加1
		end for
		for i=circle to n-circle do
			a[i][n - circle] = number;
			number加1
		end for
		for j=n-circle-1 to circle-1 do
			a[n - circle][j] = number;
			number加1
		end for
		for i=n-circle-1 to circle do
			a[i][circle - 1] = number;
			number加1
		end for
	end for
	輸出數組a

	return 0;

2.2.2 代碼截圖

2.2.3 造數據測試

輸入數據 輸出數據 備注
5 題目例子
1 1 最小n
8 最大偶數n
9 最大n

2.2.4 PTA提交列表及說明

  • 雖然這題提交列表是一遍過,但在2840題庫中做這道題的時候做了很久,但在vs中一直有奇怪的錯誤,而我也不可能連測試樣例都過不了就提交上PTA吧?改到正確且各種情況都測試了以后我才提交了PTA於是一遍過
  • 一開始做的時候我完全沒有想到利用轉的圈數去做,直接右下左上的順序一步步寫循環,寫的漏洞百出,只完成了最外圈往右和往下的輸出,其他部分全部爆炸
  • 然而我並沒有死心,還是按照這個思路再次優化代碼,優化到了第一圈完美輸出,但第二圈開始又會將第一圈的內容覆蓋掉
  • 這時候我發現這個方法的問題,越到內圈越難寫,變量太少,對i、j的循環的起始終止值很難控制,於是把所有代碼刪除,重新構思
  • 又經過幾次嘗試后引入了變量circle,由轉的圈數控制起始、終止值,思路開闊了許多,寫的也很順利,但同時也遇到了新問題,什么時候循環條件加等號,循環初值要等於多少,n、circle和i、j之間又有什么關系等等
  • 寫完后因為測試數據較少就將1到9都測試了一遍,很幸運偶數部分沒有遇到問題,於是就提交PTA通過了

2.3 刪除重復字符

2.3.1 偽代碼

	定義字符型數組ch,長度為96,全部賦0
	定義字符變量chh

	while 讀入字符chh不為回車時 do
		ch[chh - ' '] = chh;

	for i=0 to 95 do
		if ch[i]的內容不為0 then
			輸出ch[i]

	return 0;

2.3.2 代碼截圖

2.3.3 造數據測試

輸入數據 輸出數據 備注
ad2f3adjfeainzzzv 23adefijnvz 題目例子
(全部空格) (一個空格) 最長字符,全空格
$!@REewt@@jdzoi23412 !$1234@ERdeijotwz
a a 最短字符串

2.3.4 PTA提交列表及說明

  • 部分正確:一開始寫的不是這個方法,用的是先讀入數據再遍歷刪除排序的方法,但是這樣寫有空格的測試點又過不了,又突然靈光一現又有了新的方法
  • 答案正確:因為我的代碼不太一樣,就說一下思路吧。
    • 首先是ch的長度96,查閱課本后的ASCII碼表,發現是從32到127,這樣字符減去空格字符就是0~95了,而且這樣讀入已經是按ASCII碼排好序的狀態
    • 然后是刪除重復部分,並沒有特意去寫,這個字符減去32對應的下標的數組直接就賦值這個字符,再次遇到這個元素也只是再次賦值,不會有兩個該元素
    • 最后控制只輸出不為\0的數組元素就完成了題目的要求

Q3.代碼閱讀

#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <string.h>
#include <limits.h>
using namespace std;
int main(void)
{
    int n,begin = 0,end = INT_MAX;
    int x;
    char str[10];
    while( cin >> n && n )
    {
        getchar();
        begin = 0,end = INT_MAX;
         while ( gets(str) && strcmp(str,"right on") )
         {
            if( strcmp(str,"too high") == 0 && n < end )
                end = n;
            else
                if( strcmp(str,"too low") == 0 && n > begin )
                    begin = n;
            cin >> n;
            getchar();
        }
        if( n > begin && n < end )
            printf("Stan may be honest\n");
        else
            printf("Stan is dishonest\n");
    } 
return 0;
}
  • 代碼閱讀
    • 這題比較簡單,說一下題意,Stan和Ollie玩猜數游戲,Ollie猜一個數,Stan告訴她答案太大/太小/正確,但玩了幾輪后Ollie發現不對勁,覺得Stan肯定在耍老娘,他肯定是改過答案!我們要根據他們游戲的過程來判斷Stan有沒有作弊在耍Ollie
    • 每次輸入right on就輸出一次Stan是否誠實,之后輸入0可以退出循環結束程序
    • 雖然是C++的代碼,但他似乎除了cin >> n以外也沒有C++的代碼了
    • 代碼很注意細節,每次讀取完數字n后都會getchar()吸收換行符
    • 外部循環條件是輸入數字n,如果n不為0就進入循環;內部循環條件是輸入字符串str,如果str不為right on就繼續循環
    • 內部循環將字符串str和too high(too low)作對比,如果輸入的數字小於原來的最大數(大於原來的最小數)就更新尾部(頭部)為n
    • 輸入right on后將最后讀到的數和頭部、尾部做比較,如果大於頭部小於尾部,那么Stan可能是個誠實的孩子沒有說謊,否則他就是不誠實的壞孩子
    • 個人覺得這題完全可以改成用C編寫,而且他引用了這么多頭文件似乎也沒怎么用到,還有個不知道干啥用的變量x


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM