把昨天看的第二章鞏固一下,做一做程式設計習題。
第一天交2元罰金,以後每一天都是前一天的平方,第n天罰金將是多少?
這個題目和2.4.4-3介紹的冪運算基本一致。若按相同的遞迴思路分析,比那個問題要簡單,因為從1次冪開始並且指數呈2^(n-1)分布,即1,2,3,4,16……所以沒有對指數是奇數時的判定。實際上用迴圈來求要比用遞迴快。在不考慮溢位的前提下,解法如下:
#includeusingnamespace
std;
typedef unsigned
long
long
uint64;
uint64 r(
intn)
else
}uint64 w(
intn)
return
result;
}int
main()
確定乙個正整數是否是素數。這個問題可以簡化一下,因為n*n>(n-1)*(n+1)。所以只需要求n是否能被2到sqrt(n)整除就可以了,這裡需要注意的就是2是最小的素數:for(i=2;i
#include#includeusing
namespace
std;
bool ispn(int
n)else
}return
true;}
intmain()
當然,也可以if(n==2)然後for(i=2;i<=max;i++)。需要注意的是平方根(max)的條件不是小於,是小於等於。接下來有乙個厄拉多塞篩的時間複雜度問題,這裡僅記錄厄拉多塞篩:取從2到n,n>=2之間的全部素數只需要濾掉那些非素數。根據前面講述的原理,去掉這些非素數的方法如下:迴圈去掉2-sqrt(n)之間的全部非素數:
#include#include#include
using
namespace
std;
typedef unsigned
long
long
uint64;
void es(vector &arr)}}
}int
main()
}}
注意以下幾點(設n為上限):
1、**中arr預設值為0,用以表示假定為素數,最終結果中被標誌為1的都不是素數;其下標與數字一一對應,即若輸入11,則陣列最大下標為11,亦即陣列元素為n+1個。
2、根據前面的結論,i只需要從2搜尋到sqrt(n),包含sqrt(n)。
3、在進行標誌時,從當前素數的2倍開始標誌。
在chinaunix上面看到一篇類似的演算法,1、2都犯了,只能低效的輸出n-1以內的,漏掉了n。例如輸入11,得到的是2、3、5、7,例如輸入2什麼也沒出來。
歇一歇,2023年1月1日12:45,以上。
2.26:
大小為n的陣列a,若某個值出現次數超過n/2則稱為主要元素。例如:3,3,4,2,4,4,2,4,4的主要元素是4。
書中給出了一種解決思路:如果a0,a1相等,則把a0放入陣列b,否則無操作;對後續陣列a2,a3……依次做同樣操作,這樣得到乙個陣列b,對b陣列進行同樣操作……就可以找到a的主要元素。然後檢驗得到的元素是不是主要元素。這不失為一種好做法:根據主要元素的定義知道,它在陣列中過半,也就是說無論如何排列,根據上述做法我們一定能找到一組相鄰的主要元素,但當陣列元素個數為奇數時,可以正好間隔開(此時需要把最後乙個新增到b而不是省略——書中問題b)。遞推可知,由b得到的c一定含有a的主要元素,即主要元素總會剩下的,反過來講:即使陣列只剩下乙個元素那也一定是主要元素,那麼遞迴終止條件就是最終的陣列只有乙個元素。等等,也許沒有主要元素,那麼最終陣列中就不會剩下元素,所以遞迴終止條件是傳入的陣列中沒有元素或只有乙個元素——書中問題a。再等等,為什麼題目中說還有第二步——檢驗是不是真的是主要元素呢?根據我們的思路,兩兩捉對,那麼構建乙個特例:3,4,5,5,3,4就可以回答這個問題了。
到現在為止這個**是這樣的:
constint inf=0x7fffffff
;int me(const
int a,int
n)else
if(n==1
)
int i,bidx=0,b[(n+1)/2
];
for(i=0;i1;i+=2
) }
if((n%2)==1
)
return
me(b,bidx);
}bool test(const
int a,int n,int
v) }
if(cnt*2>n)
else
}int
main();
int alen=sizeof(a)/4
;
int val=me(a,alen);
cout
<"
"}
以上**我只進行了簡單的測試,可能存在問題,敬請指正。
這個問題實際上還有更好的解決方法,能夠達到線性時間——用一次迴圈即可。但是在談這種解決方案之前,我想先說乙個類似問題:最大子串行和問題。這兩個問題的解決思想非常相似。最大子串行和是這樣乙個問題:
求乙個陣列中連續部分的最大和。例如陣列:-1,4,-3,5,-2,-1,2,6,-2的最大子串行和為11(從4開始到6結束)。這個問題可以有很多解法,但其中較好的解法無一不是根據這樣的特性:達到最大和的子串行不可能以負數開頭;優秀的解法會更充分的利用這一點的推論:達到最大和的子串行任意前n個只和不可能以負數開頭。若某一段序列的和為0,我們忽略它。
現在根據我們的推論來考慮一下實現:從頭遍歷求和,當和小於0時,放棄之前的一段重新開始;當和大於0時,比較當前和是否大於已記錄的最大和,若比記錄的最大和還大,則替換最大和為當前值——所以即使後面加了若干負數導致和小於0被捨棄,也不會丟失這個最大和,編碼如下:
#includeusingnamespace
std;
int maxsum(const
int arr,int
arrcnt)
else
if(cursum<0)
}return
maxsum;
}int
main();
cout
<8)<}
**真的很少,而且時間複雜度為o(n)。所以,任何優秀的**都是建立在對問題的深入剖析基礎上的,而這往往需要敏銳的觀察。怎麼能敏銳呢?個人覺得就是遇到問題多思考,多動筆,多寫**嘗試;對問題多模擬,多對比,多歸納總結。
那麼,讓我們繼續剖析陣列的主要元素這個問題:依然用上面的陣列3,3,4,2,4,4,2,4,4,根據主要元素的定義我們知道它的個數超過任何其他元素個數的總和,即主元素個數至少比其他元素總和多1個。向上面最大子串行和靠攏:我們把陣列中的元素分成兩派:主元素派和其他元素派,然後進行計數:主元素使得計數+1,其他元素使得計數-1。可以預見的是,最終計數大於等於1。現在,問題已經基本和最大子串行和相同,只是最大子串行和中求的是元素和,而這裡我們求的是計數和——其他元素計數-1,主元素計數+1而已。所以我們是不是可以考慮,從頭開始遍歷,假定a[0]是主元素,那麼我們數一數,當到達a[4]時,計數和變為-1——從這裡繼續,假定a[4]為主元素,數到陣列結尾計數為3。那麼我們認為a[4]就是主元素。等等,這還不一定正確,讓我們檢驗一下,如果開始我們找到的就是主元素,但是和前面的a[0]一樣,被若干其他元素消除了計數結論還正確嗎?這顯而易見,把計數變負數時需要的其他元素個數多於前面的主元素個數,這導致後面會堆積至少相同個數個主元素。所以,如果存在主元素,那麼我們的結論是正確的。現在,考慮不存在主元素時,我們的掃瞄結果的正確性:陣列3,3,1,2,5掃瞄結果是5。所以,不存在主元素時我們的演算法返回了錯誤結果,同樣需要用test函式來檢驗。而這種演算法本身的編碼很簡單:
int me1(constint a,int
n)else
}return
curval;
}
在之前的main函式結尾新增以下**來測試它:
val=me1(a,alen);
cout
<"
"<2017-1-1 22:40 以上
資料結構與演算法分析第二章12題
問題描述 1 求最小子序列和 2 求最小正子串行和 3 求最大子串行乘積 分析 1 求最小子序列和其實和求最大子串行和是一樣一樣的。最高效的o n 的時間複雜度演算法思想是 假設有n個元素的陣列,最大子串行和為從i到j。那麼想想 i,j 是否可以繼續向左右擴充套件呢?如果不能,制約條件是什麼?對於最...
第二章 資料結構與演算法基礎
資料 描述客觀事物的數值 字元已經能輸入機器且能被處理的各種符號集合。資料元素 資料的基本單位,是資料集合的個體。資料物件 性質相同的資料元素的集合,是資料的子集。資料結構 相互之間存在一種或多種特定關係的資料元素的集合。邏輯結構 資料結構的邏輯層面。按照元素之間的相互關係的特性,分為 集合 線性結...
讀《演算法與資料結構》第二章
1 基本概念與抽象資料型別 1 線性表 是零個或者多個元素的有窮序列 2 順序表示 1 順序儲存是表示線性表最簡單的方法 3 順序表基本操作 1 建立空順序表 2 判斷線性表是否為空 3 在順序表中求某元素的下標 4 順序表的插入 5 順序表的刪除 4 演算法分析與評價 1 插入和刪除的平均時間代價...