PTA刷題總結(jié)-Part3.2 二分法專題

1 基本的二分查找

01-復(fù)雜度3 二分查找 (20分)
最開始使用的是遞歸,發(fā)現(xiàn)最后一個測試點超時了

#include <stdio.h>
#include <stdlib.h>

#define MAXSIZE 10
#define NotFound 0
typedef int ElementType;

typedef int Position;
typedef struct LNode *List;
struct LNode {
    ElementType Data[MAXSIZE];
    Position Last; /* 保存線性表中最后一個元素的位置 */
};

List ReadInput(); /* 裁判實現(xiàn)舞丛,細(xì)節(jié)不表刁岸。元素從下標(biāo)1開始存儲 */
Position BinarySearch( List L, ElementType X );

int main()
{
    List L;
    ElementType X;
    Position P;

    L = ReadInput();
    scanf("%d", &X);
    P = BinarySearch( L, X );
    printf("%d\n", P);

    return 0;
}

/* 你的代碼將被嵌在這里 */


Position search(ElementType Data[], Position l, Position r,ElementType X){
    if (l==r){
        return Data[l]==X? l:NotFound; 
    }
    Position mid = (l+r)>>1;
    if (Data[mid] == X){
        return mid; 
    } else if (Data[mid] < X){
        return search(Data, mid+1, r,X); 
    } else {
        return search(Data, l, mid-1,X);
    }
}

Position BinarySearch( List L, ElementType X ){
    Position p = search(L->Data, 1, L->Last,X); 
    return p;
}

于是改成非遞歸的形式

Position BinarySearch( List L, ElementType X ){
    Position p = 0;
    Position l =1,r = L->Last;
    while (l<=r){
        Position mid = l +((r-l)>>1);
        if (L->Data[mid] == X){
            p = mid;
            break; 
        }
        else if (L->Data[mid] < X){
            l = mid +1;
        } 
        else {
            r = mid -1;
        }
    }
    if (p==0){
        p = NotFound;
    }
    return p;
}

注意:

  • 上面寫的是簡單的二分查找脏里,要求數(shù)據(jù)順序存儲在數(shù)組中(如果是在鏈表中,查找的時候就不是O(1)復(fù)雜度了)虹曙,而且沒有重復(fù)數(shù)據(jù)
  • 循環(huán)退出條件是l<=r迫横,不是l<r
  • 取mid可以寫為mid = l +((r-l)>>1);避免溢出,注意不要掉了兩對括號酝碳,根據(jù)優(yōu)先級(先算術(shù)運算员淫,后移位運算,最后位運算)击敌,加法優(yōu)先于右移。
  • left 和 right 更新時不應(yīng)該使用mid拴事,而應(yīng)該使用mid-1或者mid+1沃斤。因為當(dāng)數(shù)組中沒有指定元素時,我們的判斷條件while(l<=r)會導(dǎo)致程序陷入無限循環(huán)刃宵,相等才退出
  • 數(shù)據(jù)量太小不適合二分查找衡瓶,比如只有10個數(shù)據(jù)元素,循環(huán)就好了
  • 數(shù)據(jù)量太大牲证,比如1GB哮针,由于二分查找需要連續(xù)的內(nèi)存空間,所以也不適合

題外話:基于鏈表的二分查找其實是有的。Redis中的有序集合(sorted set)使用的“跳表(Skip List)”數(shù)據(jù)結(jié)構(gòu)十厢,就是一種基于鏈表的查找方法等太,查詢效率O(logn), 構(gòu)建多級索引蛮放。

2 二分查找拓展

  1. 查找第一個值等于給定值的元素
  2. 查找最后一個值等于給定值的元素
  3. 查找第一個大于等于給定值的元素
  4. 查找最后一個小于等于給定值的元素
  5. 求一個數(shù)的算術(shù)平方根缩抡,精確到小數(shù)點后6位
  6. 木棒切割問題

1. 查找第一個值等于給定值的元素
如在數(shù)組{1,3,4,5,6,8,8,8,11,18}中希望查找第一個等于8的下標(biāo)位置(下標(biāo)從0開始),輸入數(shù)據(jù)格式:

10
1 3 4 5 6 8 8 8 11 18
8

運行程序后得到的結(jié)果是5

public class Solution {
    /**
     * @param nums: The integer array.
     * @param target: Target to find.
     * @return: The first position of target. Position starts from 0.
     */
    public int binarySearch(int[] nums, int target) {
        if (nums == null || nums.length == 0) {
            return -1;
        }
        int start = 0;
        int end = nums.length - 1;
        while (start + 1 < end) {
            int mid = start + (end - start) / 2;
            if (nums[mid] == target) {
                end = mid;
            } else if (target < nums[mid]) {
                end = mid;
            } else {
                start = mid;
            }
        }
        
        if (nums[start] == target) {
            return start;
        }
        if (nums[end] == target) {
            return end;
        }
        
        return -1;
    }
}
#include <stdio.h>
int a[100];

int search(int a[], int size, int X){
    int left = 0, right = size-1;
    while(left<=right){
        int mid = left + ((right - left) >> 1);
        if (X > a[mid]){
            left = mid + 1;
        }
        else if (X < a[mid]){
            right = mid -1;
        }
        else{ // a[mid] == X
            if (mid==0 || a[mid-1] < a[mid]){
                return mid;
            }
            else {
                right = mid - 1;
            }
        }
    }
    return -1;
}
int main(){
    int n = 0,x=0;
    scanf("%d", &n);
    for (int i=0;i<n;i++){
        scanf("%d", &a[i]);
    }
    scanf("%d", &x);
    int pos = search(a,n,x);
    printf("%d", pos);
}

2. 查找最后一個值等于給定值的元素
如在數(shù)組{1,3,4,5,6,8,8,8,11,18}中希望查找最后一個等于8的下標(biāo)位置(下標(biāo)從0開始)包颁,輸入數(shù)據(jù)格式:

10
1 3 4 5 6 8 8 8 11 18
8

運行程序后得到的結(jié)果是7

public class Solution {
    /**
     * @param nums: An integer array sorted in ascending order
     * @param target: An integer
     * @return: An integer
     */
    public int lastPosition(int[] nums, int target) {
        if (nums == null || nums.length == 0) {
            return -1;
        }
        
        int start = 0;
        int end = nums.length - 1;
        
        while (start + 1 < end) {
            int mid = start + (end - start) / 2;
            if (nums[mid] == target) {
                start = mid;
            } else if (target < nums[mid]) {
                end = mid;
            } else {
                start = mid;
            }
        }
        
        if (nums[end] == target) {
            return end;
        }
        if (nums[start] == target) {
            return start;
        }
        
        return -1;
    }
}
#include <stdio.h>
int a[100];

int search(int a[], int size, int X){
    int left = 0, right = size-1;
    while(left<=right){
        int mid = left + ((right - left) >> 1);
        if (X > a[mid]){
            left = mid + 1;
        }
        else if (X < a[mid]){
            right = mid -1;
        }
        else{ // a[mid] == X
            if (mid==size-1 || a[mid+1] > a[mid]){
                return mid;
            }
            else {
                left = mid + 1;
            }
        }
    }
    return -1;
}
int main(){
    int n = 0,x=0;
    scanf("%d", &n);
    for (int i=0;i<n;i++){
        scanf("%d", &a[i]);
    }
    scanf("%d", &x);
    int pos = search(a,n,x);
    printf("%d", pos);
}

3. 查找第一個大于等于給定值的元素
如在數(shù)組{3,4,6,7,10}中希望查找第一個大于等于5的下標(biāo)位置(下標(biāo)從0開始)瞻想,輸入數(shù)據(jù)格式:

5
3 4 6 7 10
5

運行程序后得到的結(jié)果是2

#include <stdio.h>
int a[100];

int search(int a[], int size, int X){
    int left = 0, right = size-1;
    while(left<=right){
        int mid = left + ((right - left) >> 1);
        if (a[mid]>=X){
            if (mid == 0 || a[mid-1] < X){
                return mid;
            }
            else {
                right = mid - 1;
            }
        }
        else {
            left = mid +1;
        }
    }
    return -1;
}
int main(){
    int n = 0,x=0;
    scanf("%d", &n);
    for (int i=0;i<n;i++){
        scanf("%d", &a[i]);
    }
    scanf("%d", &x);
    int pos = search(a,n,x);
    printf("%d", pos);
}

4. 查找最后一個小于等于給定值的元素
如在數(shù)組{3,4,6,7,10}中希望查找最后一個小于等于5的下標(biāo)位置(下標(biāo)從0開始),輸入數(shù)據(jù)格式:

5
3 4 6 7 10
5

運行程序后得到的結(jié)果是1

#include <stdio.h>
int a[100];

int search(int a[], int size, int X){
    int left = 0, right = size-1;
    while(left<=right){
        int mid = left + ((right - left) >> 1);
        if (a[mid] <= X){
            if (mid == size-1 || a[mid+1] > X){
                return mid;
            }
            else {
                left = mid +1;
            }
        }
        else {
            right = mid - 1;
        }
    }
    return -1;
}
int main(){
    int n = 0,x=0;
    scanf("%d", &n);
    for (int i=0;i<n;i++){
        scanf("%d", &a[i]);
    }
    scanf("%d", &x);
    int pos = search(a,n,x);
    printf("%d", pos);
}

5. 求一個數(shù)的算術(shù)平方根娩嚼,精確到小數(shù)點后6位

#include <stdio.h>
const double eps=1e-7;

double f(double x){
    return x*x;
}

double mysqrt(double n){
    double left = 0;
    double right = n;
    double mid = -1;
    while (right - left > eps){
        mid = (left + right)/2;
        if (f(mid) > n){
            right = mid;
        }
        else {
            left = mid;
        }
    }
    return mid;
}
int main(){
    int n = 0;
    scanf("%d", &n);
    double root = mysqrt(n);
    printf("%lf", root);
}

6. 木棒切割問題

給出N個木棒蘑险,長度已知。現(xiàn)在希望通過切割它們得到至少K個長度相等的木棒(長長度是整數(shù))岳悟。問這些長度相等的木棒最長有多長佃迄。
例如對于3根木棒,長度分別為10竿音,24和屎,15。想要得到至少7個長度相等的木棒春瞬,那么切割的木棒最長為6柴信。

有N條繩子,它們的長度分別為Li宽气。如果從它們中切割出K條長度相同的繩子随常,這K條繩子每條最長能有多長?

#include <stdio.h>
int a[10000];

int getCurrK(int len, int n){
    int currK = 0;
    for (int i=0;i<n;i++){
        currK += a[i]/len;
    }
    return currK;
}

int main(){

    int n=0,k=0,left=1,right=0,len = 0,sum=0;
    scanf("%d %d", &n, &k);
    for (int i=0;i<n;i++){
        scanf("%d", &a[i]);
        sum += a[i];
    }
    right = sum / k;
    while (left <= right){
        int mid = left + ((right - left)>>1);
        int currK = getCurrK(mid, n);
        if (currK < k){
            right = mid - 1;
        } else if (currK > k){
            left = mid + 1;
        } else {
            if (mid == sum / k || getCurrK(mid+1, n)<k ){
                len = mid;
                break;
            } else {
                left = mid + 1;
            }

        }
    }
    printf("%d", len);
    return 0;
}

3 最大子列和問題

最大子列和問題的三種解法

#include <stdio.h>
// O(n^3)
int maxSubSum(int a[], int size,int max){
    
    for (int i=0;i<size;i++){
        for (int j=i;j<size;j++){
            int r = 0;
            for (int k=i;k<=j;k++){
                r+=a[k];
            }
            if (r > max){
                max = r;
            }
        }
    }
    return max;
}
// O(n^2)
int maxSubSum2(int a[], int size,int max){
    
    for (int i=0;i<size;i++){
        int r = 0;
        for (int j=i;j<size;j++){
            r+=a[j];
            if (r > max){
                max = r;
            }
        }
    }
    return max;
}


int conquer(int a[], int left,int mid,int right,int lr,int rr){
    int maxl = 0;
    int r = 0;
    for(int i=mid;i>=left;i--){
        r+=a[i];
        if (r>maxl){
            maxl = r;
        }
    }
    int maxr = 0;
    r = 0;
    for (int i=mid+1;i<=right;i++){
        r+=a[i];
        if (r>maxr){
            maxr = r;
        }
    }
    int maxlr = maxl + maxr;
    if (maxlr >= lr && maxlr >= rr){
        return maxlr;
    } else if (lr > maxlr && lr > rr){
        return lr;
    } else if (rr > maxlr && rr > lr){
        return rr;
    }
}
// O(NLogN) 二分法
int maxSubSum3(int a[], int left,int right){
    if (left == right){
        return (a[left]>0)?a[left]:0;
    }
    else if (left < right){
        int mid = (left + right) >> 1;
        int lr = maxSubSum3(a,left,mid); // 獲取左邊最大
        int rr = maxSubSum3(a,mid+1,right); // 獲取右邊最大
        int max = conquer(a,left,mid,right,lr,rr); // 獲取中間向兩邊擴(kuò)展的最大值萄涯,并和左邊绪氛、右邊最大比較
        return max;
    }
}
int main(){
    int a[8] = {4,-3,5,-2,-1,2,6,-2};
    int max = 0;
    max = maxSubSum3(a,0,7);
    printf("%d", max);
    return(0);
}

當(dāng)然,最大子列和還有“在線處理”這種耗時O(N)的算法
01-復(fù)雜度2 Maximum Subsequence Sum (25分)

#include <stdio.h>
int a[100000];
int main(){
    int n=0; // 讀入n個數(shù)
    int max=0, thisSum = 0; // max=最終結(jié)果涝影,thisSum=當(dāng)前和
    scanf("%d", &n);
    for (int i=0; i<n; i++){
        scanf("%d", &a[i]);
        thisSum += a[i];
        if (thisSum < 0 ){ // 不可能使得后面的值增大了枣察,拋棄
            thisSum = 0;
        }
        if (thisSum >max){ // 更新當(dāng)前結(jié)果
            max = thisSum;
        }
    }
    printf("%d", max);
    return 0;
}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市燃逻,隨后出現(xiàn)的幾起案子序目,更是在濱河造成了極大的恐慌,老刑警劉巖伯襟,帶你破解...
    沈念sama閱讀 217,509評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件猿涨,死亡現(xiàn)場離奇詭異,居然都是意外死亡姆怪,警方通過查閱死者的電腦和手機(jī)叛赚,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評論 3 394
  • 文/潘曉璐 我一進(jìn)店門澡绩,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人俺附,你說我怎么就攤上這事肥卡。” “怎么了昙读?”我有些...
    開封第一講書人閱讀 163,875評論 0 354
  • 文/不壞的土叔 我叫張陵召调,是天一觀的道長。 經(jīng)常有香客問我蛮浑,道長唠叛,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,441評論 1 293
  • 正文 為了忘掉前任沮稚,我火速辦了婚禮艺沼,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘蕴掏。我一直安慰自己障般,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,488評論 6 392
  • 文/花漫 我一把揭開白布盛杰。 她就那樣靜靜地躺著挽荡,像睡著了一般。 火紅的嫁衣襯著肌膚如雪即供。 梳的紋絲不亂的頭發(fā)上定拟,一...
    開封第一講書人閱讀 51,365評論 1 302
  • 那天,我揣著相機(jī)與錄音逗嫡,去河邊找鬼青自。 笑死,一個胖子當(dāng)著我的面吹牛驱证,可吹牛的內(nèi)容都是我干的延窜。 我是一名探鬼主播,決...
    沈念sama閱讀 40,190評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼抹锄,長吁一口氣:“原來是場噩夢啊……” “哼逆瑞!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起伙单,我...
    開封第一講書人閱讀 39,062評論 0 276
  • 序言:老撾萬榮一對情侶失蹤呆万,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后车份,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,500評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡牡彻,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,706評論 3 335
  • 正文 我和宋清朗相戀三年扫沼,在試婚紗的時候發(fā)現(xiàn)自己被綠了出爹。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,834評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡缎除,死狀恐怖严就,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情器罐,我是刑警寧澤梢为,帶...
    沈念sama閱讀 35,559評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站轰坊,受9級特大地震影響铸董,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜肴沫,卻給世界環(huán)境...
    茶點故事閱讀 41,167評論 3 328
  • 文/蒙蒙 一粟害、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧颤芬,春花似錦悲幅、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,779評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至菱魔,卻和暖如春留荔,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背豌习。 一陣腳步聲響...
    開封第一講書人閱讀 32,912評論 1 269
  • 我被黑心中介騙來泰國打工存谎, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人肥隆。 一個月前我還...
    沈念sama閱讀 47,958評論 2 370
  • 正文 我出身青樓既荚,卻偏偏與公主長得像,于是被迫代替她去往敵國和親栋艳。 傳聞我的和親對象是個殘疾皇子恰聘,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,779評論 2 354

推薦閱讀更多精彩內(nèi)容