暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

NGE问题

阿飞算法 2021-11-04
435

Next Greater Element(下一个较大元素)

给定一个数组,为每个元素打印下一个较大元素(NGE)。元素x的下一个较大元素是数组中x右侧的第一个较大元素。没有更大元素的元素,考虑下一个更大的元素为-1。

Examples:

  • 1.对于数组,最右边的元素始终具有下一个较大的元素-1。

  • 2.对于按降序排序的数组,所有元素的下一个较大元素为-1。

  • 3.对于输入数组[4,5,2,25],每个元素的下一个较大元素如下所示。

Element       NGE
   4      -->   5
   5      -->   25
   2      -->   25
   25     -->   -1

  • 4.对于输入数组[13,7,6,12],每个元素的下一个较大元素如下所示。

 Element        NGE
   13      -->    -1
   7       -->     12
   6       -->     12
   12      -->     -1

方法1(简单):

使用两个循环:外部循环逐个遍历所有元素。内部循环为外部循环拾取的元素查找第一个较大的元素。如果找到较大的元素,则该元素将作为下一个元素打印,否则将打印-1。

Code:

 static class _1st {
        public static void main(String[] args) {
            _1st handler = new _1st();
            int arr[] = {1113213};
            int n = arr.length;
            handler.printNGE(arr, n);

        }

        public void printNGE(int arr[], int n) {
            int next, i, j;
            for (i = 0; i < n; i++) {
                next = -1;
                for (j = i + 1; j < n; j++) {
                    if (arr[i] < arr[j]) {
                        next = arr[j];
                        break;
                    }
                }
                System.out.println(arr[i] + " -- " + next);
            }
        }
    }
/**
11 -- 13
13 -- 21
21 -- -1
3 -- -1
**/


方法2(使用栈)

  • 将第一个元素推送到栈。

  • 逐个遍历其余元素,并在循环中遵循以下步骤:

  • 1.将当前元素标记为next。

  • 2.若栈不为空,则将栈的顶部元素与next进行比较。

  • 3.如果next大于top元素,则从栈中弹出元素。next是弹出元素的下一个较大元素。

  • 4.当弹出的元素小于next时,保持从栈弹出。next将成为所有此类弹出元素的下一个较大元素。

  • 最后,推入栈中的next。

  • 步骤2中的循环结束后,从栈中弹出所有元素并打印-1作为它们的下一个元素。

Code:

 static class _2nd {
        public static void main(String[] args) {
            _2nd handler = new _2nd();
            int arr[] = {1113213};
            arr = new int[]{45225};
            handler.printNGE(arr);
        }


        public void printNGE(int[] arr) {
            Stack<Integer> stk = new Stack<>();
            int ele, next;
            //第一个元素进栈
            stk.push(arr[0]);
            for (int i = 1; i < arr.length; i++) {
                next = arr[i];
                if (!stk.isEmpty()) {
                    //栈不为空,弹出栈顶元素赋值给ele
                    ele = stk.pop();
                    //如果栈顶元素比next小,打印ele->next 不断弹出栈顶元素,直到栈空
                    while (ele < next) {
                        System.out.printf("%d ---> %d \n", ele, next);
                        if (stk.isEmpty()) break;
                        ele = stk.pop();
                    }
                    //如果当前元素大于next,将当前元素入栈
                    if (ele > next) {
                        stk.push(ele);
                    }
                }
                stk.push(next);
            }
            while (!stk.isEmpty()) {
                ele = stk.pop();
                next = -1;
                System.out.printf("%d ---> %d \n", ele, next);
            }
        }
    }

//4 ---> 5 
//2 ---> 25 
//5 ---> 25 
//25 ---> -1 

复杂度分析:

  • 时间复杂度:O(N)

  • 空间复杂度:O(N)

最坏的情况发生在所有元素按降序排序时。如果元素按降序排序,则每个元素最多处理4次。

  • 1.初始时被推到栈中。

  • 2.在处理next时从栈中弹出。

  • 3.因为next更小,所以被推回栈。

  • 4.在算法的步骤3中从堆栈中弹出。

如何以与输入相同的顺序获取元素?

上述方法可能不会以与输入相同的顺序生成输出元素。为了达到相同的顺序,我们可以按相反的顺序遍历相同的对象

Code:

 static class _1st_2 {
        public static void main(String[] args) {
            _1st_2 handler = new _1st_2();
            int[] arr = new int[]{1113213};
            handler.printNGE(arr);
        }

        public void printNGE(int[] arr) {
            Stack<Integer> stk = new Stack<>();
            int[] res = new int[arr.length];
            for (int i = arr.length - 1; i >= 0; --i) {
                if (!stk.isEmpty()) {
                    while (!stk.isEmpty() && stk.peek() <= arr[i]) {
                        stk.pop();
                    }
                }
                res[i] = stk.isEmpty() ? -1 : stk.peek();
                stk.push(arr[i]);
            }
            for (int i = 0; i < res.length; i++) {
                System.out.printf("%d ---> %d \n", arr[i], res[i]);
            }

        }
    }

复杂度分析:

  • 时间复杂度:O(N)

  • 空间复杂度:O(N)

496. 下一个更大元素 I

方法1:栈(Stack)

   static class _1st {
        public static void main(String[] args) {
            _1st handler = new _1st();
            int[] nums1 = {412}, nums2 = {1342};
            handler.nextGreaterElement(nums1, nums2);

        }


        public int[] nextGreaterElement(int[] nums1, int[] nums2) {
            int[] res = new int[nums1.length];
            Stack<Integer> stk = new Stack<>();
            //k:nums2的当前元素,v:当前元素的next greater element
            Map<Integer, Integer> map = new HashMap<>();
            for (int x : nums2) {
                while (!stk.isEmpty() && stk.peek() < x) {
                    map.put(stk.pop(), x);
                }
                stk.push(x);
            }
            //如果当前元素没有NGE,返回-1
            for (int i = 0; i < nums1.length; i++) {
                res[i] = map.getOrDefault(nums1[i], -1);
            }
            return res;
        }
    }

方法2:栈(Deque)

基础

Deque有三种使用形式:

//普通队列(一端进另一端出):
Queue queue = new LinkedList()或Deque deque = new LinkedList()
//双端队列(两端都可进出)
Deque deque = new LinkedList()
//堆栈
Deque deque = new LinkedList()

  • Deque是一个线性collection,支持在两端插入和移除元素。名称 deque 是“double ended queue(双端队列)”的缩写,通常读为“deck”。大多数 Deque 实现对于它们能够包含的元素数没有固定限制,但此接口既支持有容量限制的双端队列,也支持没有固定大小限制的双端队列。

  • 此接口定义在双端队列两端访问元素的方法。提供插入、移除和检查元素的方法。每种方法都存在两种形式:一种形式在操作失败时抛出异常,另一种形式返回一个特殊值(null 或 false,具体取决于操作)。插入操作的后一种形式是专为使用有容量限制的 Deque 实现设计的;在大多数实现中,插入操作不能失败。
    下面是12种方法:

  • Deque接口扩展(继承)了 Queue 接口。在将双端队列用作队列时,将得到 FIFO(先进先出)行为。将元素添加到双端队列的末尾,从双端队列的开头移除元素。从 Queue 接口继承的方法完全等效于 Deque 方法

  • 双端队列也可用作 LIFO(后进先出)堆栈。应优先使用此接口而不是遗留 Stack 类。在将双端队列用作堆栈时,元素被推入双端队列的开头并从双端队列开头弹出。堆栈方法完全等效于 Deque 方法

Deque的实现:

  • 一般场景

  • LinkedList 大小可变的链表双端队列,允许元素为 null

  • ArrayDeque 大下可变的数组双端队列,不允许 null

  • 并发场景

  • LinkedBlockingDeque 如果队列为空时,获取操作将会阻塞,知道有元素添加

Demo示例

    private static void dequeTest() {
        Deque<String> deque = new LinkedList<String>();
        deque.push("a");
        deque.push("b");
        deque.push("c");
        System.out.println(deque);
        //获取栈首元素后,元素不会出栈
        String str = deque.peek();
        System.out.println(str);
        System.out.println(deque);
        while (deque.size() > 0) {
            //获取栈首元素后,元素将会出栈
            System.out.println(deque.pop());
        }
        System.out.println(deque);
    }
//
[c, b, a]
c
[c, b, a]
c
b
a
[]

    private static void dequeTest1() {
        Deque<Integer> deque = new LinkedList<>();
        deque.offer(1);
        deque.offer(2);
        deque.offer(3);
        System.out.println(deque);
        Integer in = deque.peek();
        System.out.println(in);
        System.out.println(deque);
        while (deque.size() > 0) {
            System.out.println(deque.pop());
        }
        System.out.println(deque);
    }  

//
[123]
1
[123]
1
2
3
[]

public int[] nextGreaterElement(int[] nums1, int[] nums2) {
            int[] res = new int[nums1.length];
            Deque<Integer> stk = new ArrayDeque<>();
            //k:nums2的当前元素,v:当前元素的next greater element
            Map<Integer, Integer> map = new HashMap<>();
            for (int x : nums2) {
                while (!stk.isEmpty() && stk.peekLast() < x) {
                    map.put(stk.pollLast(), x);
                }
                stk.addLast(x);
            }
            //如果当前元素没有NGE,返回-1
            for (int i = 0; i < nums1.length; i++) {
                res[i] = map.getOrDefault(nums1[i], -1);
            }
            return res;
        }

方法3:栈(Deque) 倒序

  • 倒序,存储res结果

  public int[] nextGreaterElement(int[] nums1, int[] nums2) {
            int[] res = new int[nums1.length];
            Deque<Integer> deque = new ArrayDeque<>();
            Map<Integer, Integer> map = new HashMap<>();
            for (int i = nums2.length - 1; i >= 0; --i) {
                int t = nums2[i];
                while (!deque.isEmpty() && deque.peekLast() <= t) {
                    deque.pollLast();
                }
                map.put(t, deque.isEmpty() ? -1 : deque.peekLast());
                deque.offerLast(t);
            }
            for (int i = 0; i < nums1.length; i++) {
                res[i] = map.get(nums1[i]);
            }
            return res;
        }

503. 下一个更大元素 II

方法1:栈

  • 需要考虑越界返回开头的情况

       public int[] nextGreaterElements(int[] nums) {
            int n = nums.length;
            int[] res = new int[n];
            Arrays.fill(res, -1);
            Stack<Integer> stk = new Stack<>();
            for (int i = 0; i < 2 * n; i++) {
                int ele = nums[i % n];
                System.out.printf("%d-->%d\n", i, ele);
                while (!stk.isEmpty() && nums[stk.peek()] < ele) {
                    res[stk.pop()] = ele;
                }
                stk.push(i % n);
            }
            return res;
        }

文章转载自阿飞算法,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论