日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

08查找满足条件的n个数

發布時間:2025/7/14 编程问答 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 08查找满足条件的n个数 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

第一節、尋找和為定值的兩個數

? ? ? ? 題目:輸入一個數組和一個數字,在數組中查找兩個數,使得它們的和正好是輸入的那個數字。要求時間復雜度是O(n)。如果有多對數字的和等于輸入的數字,輸出任意一對即可。

? ? ? ??例如輸入數組1、2、4、7、11、15和數字15。由于4+11=15,因此輸出4和11。

?

??? 思路如下:

??? 1:直接窮舉,從數組中任意選取兩個數,判定它們的和是否為輸入的那個數字。此舉復雜度為O(n^2) 。很顯然,我們要尋找效率更高的解法。

?

??? 2:題目相當于,對每個a[i],查找sum-a[i]是否也在原始序列中,每一次要查找的時間都要花費為O(n),這樣下來,最終找到兩個數還是需要O(n^2) 的復雜度。那如何提高查找判斷的速度呢?

??? 答案是二分查找。二分查找的時間復雜度為O(lg n)?,如果原數組有序,直接二分查找,n個數的總時間為O(n lg n) ;如果原數組無序,則需要先排序后二分,復雜度同樣為O(nlgn + nlgn) =O(nlgn) ,空間復雜度為O(1) 。

???

??? 3:有沒有更好的辦法呢?可以依據上述思路2的思想,進一步縮小查找時間。如果數組無序,則先在O(nlgn)?時間內排序

??? 舉個例子,如下:

原始序列:1、 2、 4、 7、11、15????

??? 用輸入數字15減一下各個數,得到對應的序列為:

對應序列:14、13、11、8、4、0?????

??? 第一個數組以一指針i 從數組最左端開始向右掃描,第二個數組以一指針j 從數組最右端開始向左掃描,誰指的元素小,誰先移動,如果a[*i]=a[*j],就找出這倆個數來了。兩端同時查找,時間復雜度瞬間縮短到了O(n),但卻同時需要O(n)的空間存儲第二個數組。

??? 因此,如果原數組無序,則需要時間O(n +nlgn) =O(nlgn)?;如果原數組有序,則需要時間O(n)

?

??? 4:針對上述思路進一步改進,使空間復雜度由O(n)變為O(1)。如果數組是無序的,則先在O(nlgn) 時間內排序,然后用兩個指針 i,j,各自指向數組的首尾兩端,令i=0,j=n-1,逐次判斷a[i]+a[j]?=sum,如果a[i]+a[j]>sum,則要想辦法讓a[i]+a[j]的值減小,所以此刻i不動,j--,如果某一刻a[i]+a[j]<sum,則要想辦法讓a[i]+a[j]的值增大,所以此 刻i++,j不動。所以,數組無序的時候,時間復雜度最終為O(n +nlgn)?=O(nlgn),若原數組是有序的,則不需要事先的排序,直接O(n)搞定,且空間復雜度還是O(1) ,此思路是相對于上述所有思路的一種改進。

?

??? 5:還可以構造hash表,正如編程之美上的所述,給定一個數字,根據hash映射查找另一個數字是否也在數組中,只需用O(1) 的時間,這樣的話,總體的算法通上述思路3 一樣,也能降到O(n) ,但有個缺陷,就是構造hash額外增加了O(n) 的空間,不過,空間換時間,仍不失為在時間要求較嚴格的情況下的一種好辦法。

?

??? 所以,要想達到時間O(n) ,空間O(1) 的目標,除非原數組是有序的(指針掃描法),不然,當數組無序的話,就只能先排序,后指針掃描法或二分查找(時間O(nlgn) ,空間O(1) ),或映射或hash(時間O(n) ,空間O(n) )。時間或空間,必須犧牲一個。

?

二:二分查找

??? “二分查找可以解決已排序數組的查找問題:只要數組中包含T(即要查找的值),那么通過不斷縮小包含T的范圍,最終就可以找到它。一開始,范圍覆蓋整個數組。將數組的中間項與T進行比較,可以排除一半元素,范圍縮小一半。就這樣反復比較,反復縮小范圍,最終就會在數組中找到T,或者確定原以為T所在的范圍實際為空。對于包含N個元素的表,整個查找過程大約要經過logN次比較。

??? 多數程序員都覺得只要理解了上面的描述,寫出代碼就不難了;但事實并非如此。我在貝爾實驗室和IBM的時候都出過這道考題。那些專業的程序員有幾個小時的時間,可以用他們選擇的語言把上面的描述寫出來。考試結束后,差不多所有程序員都認為自己寫出了正確的程序。于是,我們花了半個鐘頭來看他們編寫的代碼經過測試用例驗證的結果。幾次課,一百多人的結果相差無幾:90%的程序員寫的程序中有bug(我并不認為沒有bug的代碼就正確)。

??? 我很驚訝:在足夠的時間內,只有大約10%的專業程序員可以把這個小程序寫對。但寫不對這個小程序的還不止這些人:高德納在《計算機程序設計的藝術 第3卷 排序和查找》第6.2.1節的“歷史與參考文獻”部分指出,雖然早在1946年就有人將二分查找的方法公諸于世,但直到1962年才有人寫出沒有bug的二分查找程序。”——喬恩·本特利,《編程珠璣(第1版)》第35-36頁。

?

??? 二分查找算法的邊界,一般來說分兩種情況,一種是左閉右開區間,類似于[left, right),一種是左閉右閉區間,類似于[left, right].需要注意的是, 循環體外的初始化條件,與循環體內的迭代步驟, 都必須遵守一致的區間規則,也就是說,如果循環體外初始化時,是以左閉右開區間為邊界的,那么循環體內部的迭代也應該如此.如果兩者不一致,會造成程序的錯誤.比如下面就是錯誤的二分查找算法:

????left?=?0,?right?=?n;

????while?(left?<?right)

????{

????????middle?=?(left?+?right)?/?2;

????????if?(array[middle]?>?v)

????????{

????????????right?=?middle?-?1;

????????}

????????else?if?(array[middle]?<?v)

????????{

????????????left?=?middle?+?1;

????????}

????????else

????????{

????????????return?middle;

????????}

????}

??? 這個算法的錯誤在于, 在循環初始化的時候,初始化right=n,也就是采用的是左閉右開區間,而當滿足array[middle]> v的條件是, v如果存在的話應該在[left,middle)區間中,但是這里卻把right賦值為middle - 1了,這樣,如果恰巧middle-1就是查找的元素,而且middle-1=left,那么就會找不到這個元素。

?

??? 下面給出兩個算法, 分別是正確的左閉右閉和左閉右開區間算法,可以與上面的進行比較:

int?search2(int?array[],?int?n,?int?v)

{

????int?left,?right,?middle;

????left?=?0,?right?=?n?-?1;

????while?(left?<=?right)

????{

????????middle?=?(left?+?right)?/?2;

????????if?(array[middle]?>?v)

????????{

????????????right?=?middle?-?1;

????????}

????????else?if?(array[middle]?<?v)

????????{

????????????left?=?middle?+?1;

????????}

????????else

????????{

????????????return?middle;

????????}

????}

????return?-1;

}

?

int?search3(int?array[],?int?n,?int?v)

{

????int?left,?right,?middle;

????left?=?0,?right?=?n;

????while?(left?<?right)

????{

????????middle?=?(left?+?right)?/?2;

????????if?(array[middle]?>?v)

????????{

????????????right?=?middle;

????????}

????????else?if?(array[middle]?<?v)

????????{

????????????left?=?middle?+?1;

????????}

????????else

????????{

????????????return?middle;

????????}

????}

????return?-1;

}

???????? 另外:在循環體內,計算中間位置的時候,使用的是這個表達式:

middle?=?(left?+?right)?/?2;

??? 假如,left與right之和超過了所在類型的表示范圍的話,那么middle就不會得到正確的值.所以,更穩妥的做法應該是這樣的:

middle?=?left?+?(right?-?left)?/?2;

(http://www.cppblog.com/converse/archive/2009/10/05/97905.html)

?

三:編程求解

?????? 輸入兩個整數 n 和 m,從數列1,2,3.......n 中 隨意取幾個數,使其和等于 m ,要求將其中所有的可能組合列出來。

?????? 分析:用f(m, n)表示從1到n的序列中,和等于m的所有可能的組合,分幾種情況:

?????? 1:如果m<n,則m+1, m+2,..n這些數不可能出現在任何組合中,所以:

f(m, n) = f(m, m)。

?????? 2:如果m=n,則單獨的n是組合之一,所以f(m, n) = 。

?????? 3:如果m>n,則可以根據組合中是否包含n進行區分,所以:

f(m, n) = 。

?????? 所以,該題目的解法可以用遞歸實現,考慮到要保存中間結果以便打印出最終所有的組合,所以可以借用棧實現,下面的代碼直接使用list來模擬棧的操作:

void findsum(int sum, intn)

{

???????? static list<int> indexStack;?

?

???????? if(sum < 1)return;

???????? if(n < 1)return;

?

???????? if(sum < n)

???????? {

????????????????? return findsum(sum, sum);

???????? }

???????? else if(sum == n)

???????? {

????????????????? cout << sum <<"+";

????????????????? for(list<int>::iteratoriter = indexStack.begin();iter!=indexStack.end();++iter)?

????????????????? {

????????????????????????? cout<<*iter<<"+";?

????????????????? }

????????????????? printf("\b \n");?

????????????????? /*

????????????????????????? \b是退格符,顯示的時候是將光標退回前一個字符,

????????????????????????? 但不會刪除光標位置的字符,

????????????????????????? 如果后邊有新的字符,將覆蓋退回的那個字符,

????????????????????????? 這與我們在文本編器中按Backspace的效果不一樣。?? ???????? */

????????????????? return findsum(sum, sum-1);

???????? }

???????? else

???????? {

????????????????? indexStack.push_front(n);

????????????????? findsum(sum-n, n-1);

????????????????? indexStack.pop_front();

????????????????? findsum(sum, n-1);

???????? }

}

?

四:最長遞增子序列(LongestIncreasing Subsequence,LIS問題)

?????? 給定一個長度為n的數組,找出一個最長的單調遞增子序列(不一定連續)。 更正式的定義是:

?????? 設L=<a1,a2,…,an>是n個不同的實數的序列,L的遞增子序列是這樣一個子序列Lin=<ak1,ak2,…,akm>,其中k1<k2<…<km且ak1<ak2<…<akm。求最大的m值。

?????? 比如數組A 為{10, 11, 12, 13, 1,2, 3, 15},那么最長遞增子序列為{10,11,12,13,15}。

?

1:動態規劃(http://blog.chinaunix.net/uid-26548237-id-3757779.html)

?????? 對于數組a[n], 假設LIS[i]表示:以a[i]為結尾的,最長遞增子序列的長度。

?????? 初始情況下,LIS[i]=1,因為最長遞增子序列只包含a[i];

?????? 如果對于j,0<=j<i,假設LIS[j]已經求解出來了,則可以根據LIS[j]來計算LIS[i]:對于所有j(0<=j<i),如果a[i]>a[j],則LIS[j] + 1的最大值。也就是:

?????? LIS[i]= 。所以,代碼如下:

??????

?????? for(i = 0; i<len; i++)

???????? {

????????????????? LIS[i] = 1;

???????? }

?

???????? for(i = 0; i < len; i++)

???????? {

????????????????? for(j = 0; j < i; j++)

????????????????? {

????????????????????????? if(a[i] > a[j] &&LIS[i]<LIS[j]+1)

????????????????????????? {

?????????????????????????????????? LIS[i] = LIS[j]+1;

????????????????????????? }

????????????????? }

???????? }

?

???????? max = LIS[0];

???????? for(i = 1; i < len; i++)

???????? {

????????????????? printf("the LIS[%d] is %d\n", i, LIS[i]);

????????????????? if(max < LIS[i])

????????????????? {

????????????????????????? max = LIS[i];

????????????????? }

???????? }

?

2:二分查找法(http://www.felix021.com/blog/read.php?1587)

?? 假設存在一個序列d[1..9]= 2 1 5 3 6 4 8 9 7,可以看出來它的LIS長度為5。

下面一步一步試著找出它。

?????? 我們定義一個序列B,然后令 i = 1 to 9 逐個考察這個序列。B[i]中存儲的是:對于長度為i的遞增子序列的中的最大元素,其中的最小值。也就是長度為i的遞增子序列的最小末尾。

?????? 定義LEN記錄數組B中,已經得到的LIS的值。

?

?????? 首先,把d[1]有序地放到B里,令B[1] = 2,就是說當只有1一個數字2的時候,長度為1的LIS的最小末尾是2。這時Len=1

?

?????? 然后,讀取d[2],因d[2] < B[1],所以令B[1] = 1,就是說長度為1的LIS的最小末尾是1,d[1]=2已經沒用了,很容易理解吧。這時Len=1

?

?????? 接著,d[3]= 5,d[3]>B[1],所以令B[1+1]=B[2]=d[3]=5,就是說長度為2的LIS的最小末尾是5,很容易理解吧。這時候B[1..2]= 1, 5,Len=2

?

?????? 再來,d[4]= 3,它正好加在1,5之間,放在1的位置顯然不合適,因為1小于3,長度為1的LIS最小末尾應該是1,這樣很容易推知,長度為2的LIS最小末尾是3,于是可以把5淘汰掉,這時候B[1..2]= 1, 3,Len =2

?

?????? 繼續,d[5]= 6,它在3后面,因為B[2] = 3, 而6在3后面,于是很容易可以推知B[3]= 6, 這時B[1..3]= 1, 3, 6,還是很容易理解吧? Len= 3 了噢。

?

?????? 第6個, d[6] = 4,你看它在3和6之間,于是我們就可以把6替換掉,得到B[3]= 4。B[1..3] = 1, 3, 4, Len繼續等于3

?

?????? 第7個, d[7] = 8,它很大,比4大,嗯。于是B[4]= 8。Len變成4了

?

?????? 第8個, d[8] = 9,得到B[5] = 9,嗯。Len繼續增大,到5了。

?

?????? 最后一個,d[9] = 7,它在B[3]= 4和B[4] = 8之間,所以我們知道,最新的B[4]=7,B[1..5] = 1, 3,4, 7, 9,Len =5。

?

?????? 于是我們知道了LIS的長度為5。之所以B數組中記錄遞增子序列的最小末尾,是為了遍歷后續數組的時候,盡可能的增加遞增子序列的長度。比如某一時刻B[1]= 1, B[2]=5,說明目前的長度為2的遞增子序列中,最小末尾是5,之后碰到6,7,8,...等數的時候才會增加遞增子序列的長度。如果碰見了一個數3,因3<5,所以可以用3替換5:B[2]=3。這樣比如后續碰到了4,則就能增加遞增子序列的長度了。

?

?????? 然后應該發現一件事情了:在B中插入數據是有序的,而且是進行替換而不需要挪動——也就是說,我們可以使用二分查找,將每一個數字的插入時間優化到O(logN)~~~~~于是算法的時間復雜度就降低到了O(nlogn)~!代碼如下:

int BIN_LIS(int *a, intlen)

{

???????? int *b = malloc((len+2) * sizeof(int));

???????? int i;

???????? int max = 0;

???????? int left, right, mid;

?

???????? b[1] = a[0];

???????? max = 1;

???????? for(i = 1; i < len; i++)

???????? {

????????????????? if(a[i] > b[max])

????????????????? {

????????????????????????? max++;

????????????????????????? b[max] = a[i];

????????????????????????? continue;

????????????????? }

?????????????????

????????????????? left = 1;

????????????????? right = max;

?

????????????????? while(left <= right)

????????????????? {?????? //注意下面的邊界條件

????????????????????????? mid = left + (right -left)/2;

????????????????????????? if(b[mid] < a[i])

????????????????????????? {

?????????????????????????????????? left = mid +1;

????????????????????????? }

????????????????????????? else

????????????????????????? {

?????????????????????????????????? right = mid -1;

????????????????????????? }

????????????????? }

???????? ???????? b[left]= a[i];

???????? }

???????? return max;

}

?

五:雙端LIS問題

?????? 問題描述:從一列數中篩除盡可能少的數使得從左往右看,這些數是從小到大再從大到小的。

?????? 思路:這是一個典型的雙端LIS問題。

?????? 對于數組a,用數組b記錄從左向右看時的LISb[i]表示,以a[i]結尾的,從a[0]a[i]之間的LIS

?????? 用數組c記錄,從右向左看是的LISc[i]表示,以a[i]結尾的,從a[n-1]a[i]之間的LIS

?????? 所以,b[i] + c[i] - 1就表示以i為“頂點”的,滿足題目要求的最長序列。所以n-max{b[i] + c[i] -1}就是題目的解。

??? 這個問題同樣需要輔助數組LIS, LIS[i]中存儲的是:對于長度為i的遞增子序列的中的最大元素,其中的最小值。也就是遞增子序列的最小末尾。每當掃描到數組元素a[i]時,同LIS中的二分查找一樣,需要找到在LIS中合適的位置,這個位置,也就是b[i]或者c[i]的值。

?

代碼如下(自己寫的,JULY的程序有問題):

int Double_LIS(int *a,int len)

{

???????? int *Lis = malloc((len+2) * sizeof(int));

???????? int *b = malloc(len * sizeof(int));

???????? int *c = malloc(len * sizeof(int));

????????

???????? int i;

???????? int max = 0;

???????? int left, right, mid;

?

???????? int maxlen = 0;

???????? int index;

????????

???????? Lis[1] = a[0];

???????? max = 1;

???????? b[0] = max;

????????

???????? for(i = 1; i < len; i++)

???????? {

????????????????? if(a[i] > Lis[max])

????????????????? {

????????????????????????? max++;

????????????????????????? Lis[max] = a[i];

????????????????????????? b[i] = max;

????????????????????????? printf("b[%d] is %d\n", i, b[i]);

????????????????????????? continue;

????????????????? }

?????????????????

????????????????? left = 1;

????????????????? right = max;

????????????????? while(left <= right)

????????????????? {

????????????????????????? mid = left + (right - left)/2;

????????????????????????? if(Lis[mid] < a[i])

????????????????????????? {

?????????????????????????????????? left = mid + 1;

????????????????????????? }

????????????????????????? else

????????????????????????? {

?????????????????????????????????? right = mid - 1;

????????????????????????? }

????????????????? }

????????????????? Lis[left] = a[i];

????????????????? b[i] = left;

????????????????? printf("b[%d] is %d\n", i, b[i]);

???????? }

?

?

???????? memset(Lis, 0, (len+2) * sizeof(int));

???????? Lis[1] = a[len-1];

???????? max = 1;

???????? c[len-1] = max;

????????

???????? for(i = len - 2; i >= 0; i--)

???????? {

????????????????? if(a[i] > Lis[max])

????????????????? {

????????????????????????? max++;

????????????????????????? Lis[max] = a[i];

????????????????????????? c[i] = max;

????????????????????????? printf("c[%d] is %d\n", i, c[i]);

????????????????????????? continue;

????????????????? }

?????????????????

????????????????? left = 1;

????????????????? right = max;

????????????????? while(left <= right)

????????????????? {

????????????????????????? mid = left + (right - left)/2;

????????????????????????? if(Lis[mid] < a[i])

????????????????????????? {

?????????????????????????????????? left = mid + 1;

????????????????????????? }

????????????????????????? else

????????????????????????? {

?????????????????????????????????? right = mid - 1;

????????????????????????? }

????????????????? }

????????????????? Lis[left] = a[i];

????????????????? c[i] = left;

????????????????? printf("c[%d] is %d\n", i, c[i]);

???????? }

?

???????? maxlen = 0;

???????? for(i = 0; i < len; i++)

???????? {

????????????????? if(b[i]+c[i] > maxlen)

????????????????? {

????????????????????????? maxlen = b[i]+c[i];

????????????????????????? index = i;

????????????????? }

???????? }

???????? printf("index is %d\n", index);

???????? return len - maxlen + 1;

}

?

(http://blog.csdn.net/v_JULY_v/article/details/6419466)

轉載于:https://www.cnblogs.com/gqtcgq/p/7247179.html

總結

以上是生活随笔為你收集整理的08查找满足条件的n个数的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 91超碰在线观看 | 日韩视频一区在线 | 伊人影院在线视频 | 呦呦色| 在线h片| 久久精品无码Av中文字幕 | 欧美日性视频 | 久久精品亚洲精品国产欧美 | 香蕉一区二区 | 日韩一区二区在线观看视频 | 91免费精品 | 精品一区二区三区日韩 | 91精品综合久久久久久 | 99久久精品免费看 | www.激情五月.com | www国产在线| 日韩av一区在线播放 | 不卡视频在线观看免费 | 亚洲欧美制服丝袜 | www.三级| 精品视频一二区 | 久久精品无码一区 | 精品成人无码久久久久久 | 国产精品久久久影院 | 成人欧美视频 | 成人国产精品视频 | 在线视频中文字幕 | 伊人婷婷综合 | 欧美一级视频免费观看 | 69精品丰满人妻无码视频a片 | 黄色网址你懂得 | 伊人91在线| 亚洲精品国偷拍自产在线观看蜜桃 | 久久精品视 | 成人性视频网 | 国产青青视频 | 国产视频不卡一区 | 国产精品青青草 | 日本肉体xxxx裸体137大胆图 | 男人在线天堂 | 午夜精品无码一区二区三区 | 一级片aaa | av在线不卡网站 | av不卡一区二区三区 | 精品一区二区三区在线播放 | 小辣椒福利视频导航 | 超碰免费公开 | 午夜a级片 | 午夜欧美精品久久久久久久 | 黄色影音 | 99re最新| 国产一区二区在线观看免费 | 午夜免费视频网站 | 欧美人与性动交α欧美精品 | 亚洲美女毛片 | 色老头在线视频 | 三级免费毛片 | 污污在线免费观看 | 日b视频免费 | 午夜aaa片一区二区专区 | 亚洲狼人av | 日本精品影院 | 日本少妇喷水视频 | 永久免费观看av | 美女被捅个不停 | 人妻精油按摩bd高清中文字幕 | 日韩成人黄色 | 国产主播在线播放 | 夜晚福利视频 | 午夜视频网站在线观看 | 国产chinese男男gaygay视频 | 永久国产 | 国产精品99久久久 | 亚洲一区二区免费 | 日韩精品手机在线 | 午夜不卡影院 | 日本熟女一区二区 | 黄色成人小视频 | 国产精品成人69xxx免费视频 | 污视频在线观看免费 | 成人免费版 | 欧美人xxxx| 不卡的av网站| 男人的网址 | 国产99久久久国产精品免费看 | 国产chinese男男网站大全 | 中文字幕免费在线观看 | 自拍超碰在线 | 国产免费av一区二区三区 | 麻豆一区二区在线 | 国产日韩欧美在线观看 | 中文字幕一区二区三区四区欧美 | 中国浓毛少妇毛茸茸 | 蜜桃成人无码区免费视频网站 | 亚洲专区一区二区三区 | 精品人妻av一区二区 | 这里只有精品在线播放 | 午夜免费福利网站 | 久热伊人 |