算法演练2:山脉数组的峰顶

这个leetcode题目是这样的:

这个数组的特点是会形成一个山峰,而题目要求返回这个山峰的索引。

一般的解法

如果按题意来想,很快就想到一个解决办法:把a[i]跟a[i+1]作比较,如果a[i+1]比a[i]小了,那就是到山峰了,返回i即可。这个算法需要遍历数组,一直找到开始变小的情况。

对于这个算法,可以这样实现(因为山峰一定不会在最后出现,所以不必担心A[i+1]会越界):

int peakIndexInMountainArray(int* A, int ASize) {
    int i;
    for (i = 0; i < ASize; i ++) {
        if (A[i] > A[i+1]) {
            break;
        }
    }
    return i;
}

把这个实现提交到leetcode,反馈是这样的:

从反馈来看,这并不是一个速度很快的算法设计,但这不代表它不可行,实际上,很多情况,我们宁愿简单一点的实现也不要复杂但性能很佳的设计,因为有时简单就是最重要的。但是,这里的训练就是要追求更好的设计,而不是考虑实际情况。

经过设计的解法

小程之前已经提过:一般自然的想法,都不是高效的算法设计。

所以,除了理解题意外,还需要有独立的时间来进行算法设计(而不是简单地按着题意来解答),这对于其它场景也是一样,使用什么套路,有时候是很关键的。

另外,高效是相对的,检测是不是高效有两个办法,一个办法是运行起来看执行速度,一般需要对比其它解决办法,另一个办法是分析时间复杂度,这两个都不是小程重点讲解的内容,读者不应该陷入时间复杂度的分析当中,而应该把时间放在经典的算法套路的理解上。

对于这个题目,如果换一个角度,那实际就是求最大值的索引,依然可以套用小程之前反复提到的“分治”与“重用”的套路。

假如一开始就决定使用分治与重用的套路,那思考的过程可能是这样的:

1. 这里的分治,只需要简单的分,从中间一分为二即可。
2. 然后就是重用,也就是标准作业,就是自己,即返回最大值的索引。

整体的思路是这样的:把数列分为两部分,然后重用自己,求得两部分的最大值的索引,然后对这两个索引对应的值再比较一次,最终返回整体的最大值的索引。

分到什么时候结束呢?分到只有一个元素时结束,或者,在程序实现时再考虑都可以。在算法设计上,可以不考虑细节。

这个算法的实现是这样的:

int maxindex(int* A, int b, int e) {
    if (b < e) {
        int m = (b+e)>>1;
        int l = maxindex(A, b, m);
        int r = maxindex(A, m+1, e);
        int ret = l;
        if (A[l] < A[r]) {
            ret = r;
        }
        return ret;
    }
    return b;
}

int peakIndexInMountainArray(int* A, int ASize) {
    return maxindex(A, 0, ASize-1);
}

把这个代码提交到leetcode,反馈如下(可见速度大幅提升了):

以上是同时使用“分治”与“重用”,而且“重用”的标准作业是自身,所设计出来的算法。

实际上,还有其它套路可以优雅地解决这个问题,比如“排除”的套路,不断地排除掉不符合答案的区域,最终定位到答案。对于这个“排除”套路,可以用折半查找的办法来实现。

对于“排除”套路,小程另作介绍,这里只给出对应的代码实现并结束本文的主要内容:

public class Solution {
    public int PeakIndexInMountainArray(int[] A) {
        int ret = 0;
        int l = 0, r = A.Length - 1;
        while (l < r) {
            int m = (l+r) >> 1;
            if (A[m] < A[m + 1]) l = m;
            else if (A[m-1] > A[m]) r = m;
            else {
                ret = m;
                break;
            }
        }
        return ret;
    }
}

简单来说。分治,就是大事化小,难点在于,怎么分,跟分到什么程度为止。重用,就是反复使用标准作业,可能要先假设有某种能力,再使用它,并真的达到某种能力,需要有空手套白狼的勇气。你自己领悟一下。

总结一下,本文再次介绍“分治”与“重用”套路的使用,分治能使问题规模变小并最终得到解决,重用能简化思考的复杂度并让思路简洁,这两个套路经常出现在算法设计当中。



原文地址:https://www.cnblogs.com/freeself/p/10950612.html

时间: 2024-10-09 00:37:48

算法演练2:山脉数组的峰顶的相关文章

力扣——山脉数组的峰顶索引

我们把符合下列属性的数组 A 称作山脉: A.length >= 3 存在 0 < i < A.length - 1 使得A[0] < A[1] < ... A[i-1] < A[i] > A[i+1] > ... > A[A.length - 1] 给定一个确定为山脉的数组,返回任何满足 A[0] < A[1] < ... A[i-1] < A[i] > A[i+1] > ... > A[A.length - 1]

山脉数组的峰顶索引

我们把符合下列属性的数组 A 称作山脉: A.length >= 3存在 0 < i < A.length - 1 使得A[0] < A[1] < ... A[i-1] < A[i] > A[i+1] > ... > A[A.length - 1]给定一个确定为山脉的数组,返回任何满足 A[0] < A[1] < ... A[i-1] < A[i] > A[i+1] > ... > A[A.length - 1] 的

力扣(LeetCode) 852. 山脉数组的峰顶索引

我们把符合下列属性的数组 A 称作山脉: A.length >= 3 存在 0 < i < A.length - 1 使得A[0] < A[1] < ... A[i-1] < A[i] > A[i+1] > ... > A[A.length - 1] 给定一个确定为山脉的数组,返回任何满足 A[0] < A[1] < ... A[i-1] < A[i] > A[i+1] > ... > A[A.length - 1]

算法题:求数组中最小的k个数

说明:本文仅供学习交流,转载请标明出处,欢迎转载! 题目:输入n个整数,找出其中最小的k个数. <剑指offer>给出了两种实现算法: 算法1:采用Partition+递归法,该算法可以说是快速排序和二分查找的有机结合.算法的时间复杂度为O(n),缺点在于在修改Partition的过程中会修改原数组的值. 算法2:采用top-k算法.如果要找最小的K个数,我们才用一个含有K个值的大顶堆:如果要找最大的K个数,我们采用小顶堆.该算法的时间复杂度为O(nlogK),是一种比较好的算法,启发于堆排序

编程算法 - 数字在排序数组中出现的次数 代码(C)

数字在排序数组中出现的次数 代码(C) 本文地址: http://blog.csdn.net/caroline_wendy 题目: 统计一个数字在排序数组中出现的次数. 通过折半查找, 找到首次出现的位置, 再找到末次出现的位置, 相减即可. 时间复杂度O(logn). 代码: /* * main.cpp * * Created on: 2014.6.12 * Author: Spike */ /*eclipse cdt, gcc 4.8.1*/ #include <stdio.h> #inc

KMP算法 --- 深入理解next数组

KMP算法的前缀next数组最通俗的解释 我们在一个母字符串中查找一个子字符串有很多方法.KMP是一种最常见的改进算法,它可以在匹配过程中失配的情况下,有效地多往后面跳几个字符,加快匹配速度. 当然我们可以看到这个算法针对的是子串有对称属性,如果有对称属性,那么就需要向前查找是否有可以再次匹配的内容. 在KMP算法中有个数组,叫做前缀数组,也有的叫next数组,每一个子串有一个固定的next数组,它记录着字符串匹配过程中失配情况下可以向前多跳几个字符,当然它描述的也是子串的对称程度,程度越高,值

KMP算法中求next数组的实质

在串匹配模式中,KMP算法较蛮力法是高效的算法,我觉得其中最重要的一点就是求next数组: 看了很多资料才弄明白求next数组是怎么求的,我发现我的忘性真的比记性大很多,每次看到KMP算法求next数组都得花很长时间去看怎么求,虽然看了很多遍了,但还是容易忘,所以我今天非得把它记下来,这样我下次看到的时候就可以直接看我的总结了,哈,可恶的记性,总是这么不争气.设目标串为S,需要匹配串为T: next[j]数组生成的实质:     next[j]数组的值其实就等于串T1T2...Tj-1中相同的前

1142: 零起点学算法49——找出数组中最大元素的位置(下标值)

1142: 零起点学算法49--找出数组中最大元素的位置(下标值) Time Limit: 1 Sec  Memory Limit: 64 MB   64bit IO Format: %lldSubmitted: 1937  Accepted: 977[Submit][Status][Web Board] Description 找出数组中最大的元素的下标. Input 多组测试,每组先输入一个不大于10的整数n 然后是n个整数 Output 输出这n个整数中最大的元素及下标值 Sample I

[算法]一整型数组,除了0之外,其他不重复,判断数组元素是否相邻

数组元素是非负整数,0可以化为任意正整数,要求时间复杂度为O(n) 觉得很奇怪的题目,遍历一次找到最值相减得到极差就行了,如果极差小于数组长度n,则元素相邻,否则不相邻 bool is_adjacent(int* a, int n){ int min = 65535, max = 0; for(int i = 0; i < n; ++i){ if(!a[i] && a[i] < min){ min = a[i]; } else if(!a[i] && a[i]