第三章 尋找滿足條件的兩個或多個數及其擴充套件

2021-06-07 02:21:37 字數 3552 閱讀 8623

第一節、尋找滿足條件的兩個數

題目:輸入乙個陣列和乙個數字,在陣列中查詢兩個數,使得它們的和正好是輸入的那個數字。

要求時間複雜度是o(n)。如果有多對數字的和等於輸入的數字,輸出任意一對即可。例如輸入陣列1、2、4、7、11、15和數字15。由於4+11=15,因此輸出4和11。

分析:

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

題目相當於,對每個a[i],然後查詢判斷sum-a[i]是否也在原始序列中,每一次要查詢的時間都要花費為o(n),這樣下來,最終找到兩個數還是需要o(n^2)的複雜度。那如何提高查詢判斷的速度? 對了,二分查詢,將原來o(n)的查詢時間提高到o(logn),這樣對於n個a[i],都要花logn的時間去查詢相對應的sum-a[i]是否在原始序列中,總的時間複雜度已降為o(n*logn),且空間複雜度為o(1)。(如果有序,直接二分o(n*logn),如果無序,先排序後二分,複雜度同樣為o(n*logn+n*logn)=o(n*logn),空間總為o(1))。

有沒有更好的辦法列?咱們可以依據上述思路2的思想,a[i]在序列中,如果a[i]+a[k]=sum的話,那麼sum-a[i](a[k])也必然在序列中,,舉個例子,如下:

原始序列:1、 2、 4、 7、11、15     用輸入數字15減一下各個數,得到對應的序列為:

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

第乙個陣列以一指標i 從陣列最左端開始向右掃瞄,第二個陣列以一指標j 從陣列最右端開始向左掃瞄,如果下面出現了和上面一樣的數,即a[*i]=a[*j],就找出這倆個數來了。如上,i,j最終在第乙個,和第二個序列中找到了相同的數4和11,,所以符合條件的兩個數,即為4+11=15。怎麼樣,兩端同時查詢,時間複雜度瞬間縮短到了o(n),但卻同時需要o(n)的空間儲存第二個陣列(這個陣列是有序的陣列)(要達到o(n)的複雜度,第乙個陣列以一指標i 從陣列最左端開始向右掃瞄,第二個陣列以一指標j 從陣列最右端開始向左掃瞄,首先初始i指向元素1,j指向元素0,誰指的元素小,誰先移動,由於1(i)>0(j),所以i不動,j向左移動。然後j移動到元素4發現大於元素1,故而停止移動j,開始移動i,直到i指向4,這時,i指向的元素與j指向的元素相等,故而判斷4是滿足條件的第乙個數;然後同時移動i,j再進行判斷,直到它們到達邊界)。

當然,你還可以構造hash表,正如程式設計之美上的所述,給定乙個數字,根據hash對映查詢另乙個數字是否也在陣列中,只需用o(1)的時間,這樣的話,總體的演算法通上述思路3 一樣,也能降到o(n),但有個缺陷,就是構造hash額外增加了o(n)的空間,此點同上述思路 3。不過,空間換時間,仍不失為在時間要求較嚴格的情況下的一種好辦法。

如果陣列是無序的,先排序(n*logn),然後用兩個指標i,j,各自指向陣列的首尾兩端,令i=0,j=n-1,然後i++,j--,逐次判斷a[i]+a[j]?=sum,如果某一刻a[i]+a[j]>sum,則要想辦法讓sum的值減小,所以此刻i不動,j--,如果某一刻a[i]+a[j]

總結:

第二節、尋找滿足條件的多個數

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

解法一、用遞迴的方法進行求解

[cpp]view plain

copy

#include

#include

using

namespace

std;  

list>list1;  

void

find_factor(

intsum, 

intn)   

list1.push_front(n);      //典型的01揹包問題

find_factor(sum-n, n-1);   //放n,n-1個數填滿sum-n

list1.pop_front();  

find_factor(sum, n-1);     //不放n,n-1個數填滿sum 

}  int

main()    

解法二(此思路不是太懂)

這個問題屬於子集和問題(也是揹包問題)。本程式採用 回溯法+剪枝

x陣列是解向量,t=∑(1,..,k-1)wi*xi, r=∑(k,..,n)wi

若t+wk+w(k+1)<=m,則xk=true,遞迴左兒子(x1,x2,..,x(k-1),1);否則剪枝;

若t+r-wk>=m && t+w(k+1)<=m,則置xk=0,遞迴右兒子(x1,x2,..,x(k-1),0);否則剪枝;

本題中w陣列就是(1,2,..,n),所以直接用k代替wk值。

**如下:

[cpp]view plain

copy

#include 

#include 

#include 

/** 

* 輸入t, r, 嘗試wk

*/void

sumofsub(

intt, 

intk ,

intr, 

int& m, 

bool

& flag, 

bool

* x)  

}  printf("\n"

);  

}  else

// 若不選第k個數,選第k+1個數滿足條件,則遞迴右子樹

if((t + r - k >= m) && (t + (k+1) <= m))  

}  }  void

search(

int& n, 

int& m)  

bool

f = 

false

;  sumofsub(0, 1, sum, m, f, x);  

if(!f)  

free(x);  

}  int

main()    

第三節、尋找滿足條件的數的擴充套件問題

1. 從一列數中篩除盡可能少的數使得從左往右看,這些數是從小到大再從大到小的(沒有理解)

分析:雙端 lis 問題,用 dp 的思想可解,目標規劃函式 max, 其中 b[i] 為從左到右, 0 ~ i 個數之間滿足遞增的數字個數; c[i] 為從右到左, n-1 ~ i 個數之間滿足遞增的數字個數。最後結果為 n - max + 1。其中 dp 的時候,可以維護乙個 inc 陣列表示遞增數字序列,inc[i] 為從小到大第 i 大的數字,然後在計算 b[i] c[i] 的時候使用二分查詢在 inc 中找出區間 inc[0] ~ inc[i-1] 中小於 a[i] 的元素個數(low)。

2.2、有兩個序列a,b,大小都為n,序列元素的值任意整數,無序;要求:通過交換a,b中的元素,使[序列a元素的和]與[序列b元素的和]之間的差最小。

例如:  var a=[100,99,98,1,2, 3];var b=[1, 2, 3, 4,5,40];(沒有理解)

from: 

快速尋找滿足條件的兩個數或三個數

問題 1.快速找出乙個陣列中的兩個數,讓這兩個數之和等於乙個給定的值。2.快速找出乙個陣列中的三個數,讓這三個數之和等於乙個給定的值。1.解法 演算法複雜度為o nlogn 先用快速排序對陣列排序,讓後用雙指標 雙索引 法對排序好的陣列進行反向遍歷,並且遍歷的方向不變。若是計算兩個數的和,則初始化為...

005尋找滿足和為定值的兩個或多個數

題目 輸入乙個陣列和乙個數字,在陣列中查詢兩個數,使得它們的和正好是輸入的那個數字。要求時間複雜度是o n 如果有多對數字的和等於輸入的數字,輸出任意一對即可。例如輸入陣列1 2 4 7 11 15和數字15。由於4 11 15,因此輸出4和11。分析 思路一 排序,兩頭向中間遍歷,兩頭和大於sum...

尋找和為定值的兩個或多個數

一 尋找和為定值的兩個數 題目 輸入乙個陣列和乙個數字,在陣列中查詢兩個數,使得它們的和正好是輸入的那個數字。要求時間複雜度是o n 如果有多對數字的和等於輸入的數字,輸出任意一對即可。例如輸入陣列1 2 4 7 11 15和數字15。由於4 11 15,因此輸出4和11。詳情參考 用乙個hash表...