在1D阵列/直方图中查找局部最小值/最大值

时间:2012-10-31 12:03:22

标签: java algorithm

我正在尝试查找数组中所有局部最小值和最大值的索引。

示例:

int[] array = {5,4,3,3,3,3,3,2,2,2,  6,6,8,5,5,5,3,3,2,1,  1,4,4,7};
//                             |         |                 |
// Indices:    0,1,2,3,4,5,6,7,8,9, 10,1,2,3,4,5,6,7,8,9, 20,1,2,3
// Minima: 8, 20
// Maxima: 12

我提出了一个算法,我几乎没有问题:

  • 有更好的吗? :)
  • 我使用Enum方法来实现这种二元论,UP和STRAIGHT_UP都是“UP”。对我来说似乎很麻烦。有什么建议吗?
  • 你有更好的方法名吗? direction()(+返回值)表示STRAIGHT不是dir。但同时它也是,因为它是Emum中的一个元素。 HM。
  • 适用于给定的数组。你看到它没有的情况吗?

-

import java.util.ArrayList;


public class MinMaxFinder {
    private int[] array;
    private ArrayList<Integer> minima;
    private ArrayList<Integer> maxima;


    private enum Direction{
        UP, DOWN, STRAIGHT_UP, STRAIGHT_DOWN, STRAIGHT;

        public Direction direction(){
            if(this==UP || this==STRAIGHT_UP){
                return UP;
            }else if(this==DOWN || this==STRAIGHT_DOWN){
                return DOWN;
            }else{
                return STRAIGHT;
            }
        }

        public boolean isStraight(){
            if(this==STRAIGHT_DOWN || this==STRAIGHT_UP || this==STRAIGHT){
                return true;
            }else{
                return false;
            }
        }

        public boolean hasDifferentDirection(Direction other){
            if(this!=STRAIGHT && other!=STRAIGHT && this.direction() != other.direction() ){
                return true;
            }
            return false;
        }
    }

    public MinMaxFinder(int[] array){
        this.array = array;
    }

    public void update() {
        minima = new ArrayList<Integer>();
        maxima = new ArrayList<Integer>();

        Direction segmentDir = Direction.DOWN;
        int indexOfDirectionChange = 0;
        int prevVal = array[0];
        int arrayLength = array.length; 
        for(int i=1; i<arrayLength; i++){
            int currVal = array[i];
            Direction currentDir = currVal<prevVal?Direction.DOWN:(currVal>prevVal?Direction.UP:Direction.STRAIGHT);
            prevVal = currVal;

            if(currentDir.hasDifferentDirection(segmentDir)){
                int changePos = (indexOfDirectionChange+i-1)/2;
                if(currentDir.direction() == Direction.DOWN){
                    maxima.add(changePos);
                }else{
                    minima.add(changePos);
                }

                segmentDir = currentDir;
                indexOfDirectionChange = i;
            }else if( currentDir.isStraight() ^ segmentDir.isStraight() ){
                indexOfDirectionChange = i;

                if(currentDir.isStraight() && segmentDir.direction()==Direction.UP){
                    segmentDir=Direction.STRAIGHT_UP;
                }else if(currentDir.isStraight() && segmentDir.direction()==Direction.DOWN){
                    segmentDir=Direction.STRAIGHT_DOWN;
                }else{
                    segmentDir = currentDir;
                }
            }
        }
    }

    public ArrayList<Integer> getMinima() {
        return minima;
    }

    public ArrayList<Integer> getMaxima() {
        return maxima;
    }
}

3 个答案:

答案 0 :(得分:1)

考虑一系列第一个差异d[i] = a[i] - a[i-1]

如果d[i]为正,则a在最后一步增加,如果d[i]为负,则a减少。因此,d从正数到负数的符号变化表明a正在增加,现在正在减少,是一个局部最大值。同样,阴性到阳性表示当地最小值。

答案 1 :(得分:0)

像这样的“应该”工作,它可能在概念上不那么复杂。 扫描数组一次并记录分钟和最大值。

值得一提的是:
1)if(方向&lt; 0){} else {}可能会被删除,但我没有时间考虑细节。
2)关键的想法是,取决于第一个“方向”(无论我们看到最小值还是最大值),for循环顺序变化。
3)如果是多个项目,它将始终保留最后一个元素(最高索引)。

if(a.length < 2){
       return;
   }

   List<Integer> mins = new ArrayList<Integer>();
   List<Integer> maxs = new ArrayList<Integer>();
   int i=1;
   int prev = 0;

   int direction = 0;
   for(int j=1, k = 0;j<a.length && (direction = a[j]-a[k]) == 0;j++, k++);

   if(direction == 0){
       //Array contains only same value.
       return;
   }

   if(direction < 0){
       while(i<a.length){
           for(;i<a.length && a[prev] >= a[i];i++,prev++);
           mins.add(prev);
           for(;i<a.length && a[prev] <= a[i];i++,prev++);
           maxs.add(prev);
           i++;prev++;
       }    
   }
   else{
       while(i<a.length){
           for(;i<a.length && a[prev] <= a[i];i++,prev++);
           maxs.add(prev);
           for(;i<a.length && a[prev] >= a[i];i++,prev++);
           mins.add(prev);
           i++;prev++;
       }
   }

   //maxs and mins now contain what requested

答案 2 :(得分:0)

我想我明白了。多谢你们!你的想法给了我很多帮助!

以下解决方案适用于我:

    ArrayList<Integer> mins = new ArrayList<Integer>();
    ArrayList<Integer> maxs = new ArrayList<Integer>();

    int prevDiff = array[0] - array[1];
    int i=1;
    while(i<array.length-1){    
        int currDiff = 0;
        int zeroCount = 0;
        while(currDiff == 0 && i<array.length-1){
            zeroCount++;
            i++;
            currDiff = array[i-1] - array[i];
        }

        int signCurrDiff = Integer.signum(currDiff);
        int signPrevDiff = Integer.signum(prevDiff);
        if( signPrevDiff != signCurrDiff && signCurrDiff != 0){ //signSubDiff==0, the case when prev while ended bcoz of last elem
            int index = i-1-(zeroCount)/2;
            if(signPrevDiff == 1){
                mins.add( index );
            }else{
                maxs.add( index );
            }               
        }
        prevDiff = currDiff;
    }