386,链表中的下一个更大节点

想了解更多数据结构以及算法题,可以关注微信公众号“数据结构和算法”,每天一题为你精彩解答。也可以扫描下面的二维码关注
在这里插入图片描述

给一个链表,返回每个节点下一个比他大的值,如果不存在就返回0 。


示例 1:

输入:[2,1,5]

输出:[5,5,0]

解释:2的下一个比他大的是5,1的下一个比他大的也是5,5后面没有比他大的值,所以是0。

示例 2:

输入:[2,7,4,3,5]

输出:[7,0,5,5,0]

示例 3:

输入:[1,7,5,1,9,2,5,1]

输出:[7,9,9,9,0,5,0,0]


问题分析
这题和382,每日温度的5种解题思路很像,不过有点不同的是第382题求的是下一个比他大的数的位置和当前数位置的差值,而这里求的是下一个比他大的数的值。还有一点是382题使用的是数组,我们这道题使用的是链表。


所以我们完全可以参照382题的做法,由于链表访问比较麻烦,需要从前往后一个个遍历,我们这里直接把链表转化为list集合的方式来求解。我们不把链表转为数组,因为数组需要先声明大小,而链表的大小我们是不知道的(如果先计算链表的长度再转化为数组感觉有点多余),我们直接把链表转化为list集合,这样会更方便些。解题思路完全可以参照第382题,我们来看下代码


暴力求解

public int[] nextLargerNodes(ListNode head) {
    List<Integer> list = new ArrayList<>();
    //链表元素存储到集合中
    while (head != null) {
        list.add(head.val);
        head = head.next;
    }
    int[] res = new int[list.size()];
    for (int i = 0; i < list.size() - 1; i++) {
        for (int j = i + 1; j < list.size(); j++) {
            if(list.get(j)>list.get(i)){
                res[i]=list.get(j);
                break;
            }
        }
    }
    return res;
}

使用栈求解

public int[] nextLargerNodes(ListNode head) {
    List<Integer> list = new ArrayList<>();
    //链表元素存储到集合中
    while (head != null) {
        list.add(head.val);
        head = head.next;
    }
    //栈中存储的是元素的下标,并且从栈底到栈顶元素在集合中对应的
    //值是从大到小的
    Stack<Integer> stack = new Stack<>();
    int[] res = new int[list.size()];
    for (int i = 0; i < list.size(); i++) {
        while (!stack.empty() && list.get(stack.peek()) < list.get(i)) {
            //如果栈顶元素对应的值小于当前值,说明栈顶元素遇到了比他小的
            int index = stack.pop();
            res[index] = list.get(i);
        }
        stack.push(i);
    }
    return res;
}

剪枝计算

这种是从后往前计算,通过剪枝,减少运算

public int[] nextLargerNodes(ListNode head) {
    List<Integer> list = new ArrayList<>();
    //链表元素存储到集合中
    while (head != null) {
        list.add(head.val);
        head = head.next;
    }
    int[] res = new int[list.size()];
    for (int i = res.length - 1; i >= 0; i--) {
        int j = i + 1;
        int num = 0;
        if (j < res.length)
            num = list.get(j);
        while (j < res.length) {
            if (num > list.get(i)) {
                //如果找到就停止while循环
                res[i] = num;
                break;
            } else if (num == 0) {
                break;
            } else {
                num = res[j++];
            }
        }
    }
    return res;
}

不转化为list集合求解

上面3种解法我们完全是参照第382题的答案写的,并且在计算之前都首先把链表转化为list集合,我们能不能直接使用链表,不把他转化为list集合呢,其实也是可以的,我们只需要使用一个栈来存储链表中每个节点在list中的位置即可,并且栈中元素在集合list中对应的值从栈底到栈顶是递减的,原理还是和上面类似,我们就以上面例二的数据[2,7,4,3,5]来画个图看一下是怎么样的一个实现过程

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

我们来看下代码

public int[] nextLargerNodes(ListNode head) {
    List<Integer> list = new ArrayList<>();
    Stack<Integer> stack = new Stack();
    for (ListNode node = head; node != null; node = node.next) {
        while (!stack.isEmpty() && node.val > list.get(stack.peek())) {
            list.set(stack.pop(), node.val);
        }
        stack.push(list.size());
        list.add(node.val);
    }
    for (int i : stack) {
        list.set(i, 0);
    }
    int[] res = new int[list.size()];
    for (int i = 0; i < res.length; i++) {
        res[i] = list.get(i);
    }
    return res;
}

上面代码核心部分在4-10行,原理和前几种写法类似,14-17行是把list转化为数组,第11-13行遍历这个栈,这个时候栈中的每个元素在list中对应的值在后面没有比他更大的,我们直接让他等于0即可。


递归方式

当然我们还可以改为递归的方式,有兴趣的可以自己看下

int[] res;

public int[] nextLargerNodes(ListNode head) {
    calNode(head, 0, new Stack<>());
    return res;
}

public void calNode(ListNode node, int index, Stack<Integer> stack) {
    if (node == null) {
        res = new int[index];//初始化数组的大小
        return;
    }
    calNode(node.next, index + 1, stack);
    while (!stack.empty() && stack.peek() <= node.val)
        stack.pop();
    res[index] = stack.empty() ? 0 : stack.peek();
    stack.push(node.val);
}

注意这里的第14行如果栈顶元素不小于当前值就要出栈,一直到栈顶元素大于当前值为止,为什么要这样写,因为这里的递归对链表的访问实际上相当于从链表的尾到头开始的,也就是逆序。这个可以参照前面的剪枝计算,因为他也在集合中从后往前开始计算的。

在这里插入图片描述

posted @ 2020-09-24 00:04  数据结构和算法  阅读(93)  评论(0编辑  收藏  举报