NC01--股票(一次交易)、合并k个有序链表、字符串的排列、接雨水问题、输出二叉树的右视图

本文介绍了五个经典的算法问题:股票一次交易求最大利润,合并k个有序链表,字符串排列,接雨水问题以及输出二叉树的右视图。针对每个问题,提供了思路解析和解题链接,帮助读者理解和掌握这些算法的解决方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、股票(一次交易)

https://www.nowcoder.com/practice/64b4262d4e6d4f6181cd45446a5821ec?tpId=117&&tqId=37717&rp=1&ru=/activity/oj&qru=/ta/job-code-high/question-ranking

根本用不上什么动态规划,直接遍历数组,然后找到最低价格,然后在后面减去最低价格,得到利润和最大利润进行比较,实现对最大的利润的更新,总之就是在遍历的过程中不断更新最小价格和最大利润。

import java.util.*;


public class Solution {
    /**
     * 
     * @param prices int整型一维数组 
     * @return int整型
     */
    public int maxProfit (int[] prices) {
        // write code here
        int minPrice = Integer.MAX_VALUE;
        int maxProfit = 0;
        for(int i = 0; i < prices.length; i++){
            if(minPrice > prices[i]){
                minPrice = prices[i];
            }else{
                maxProfit = Math.max(maxProfit, prices[i] - minPrice);
            }
        }
        return maxProfit;
    }
}

2、合并k个有序链表

https://www.nowcoder.com/practice/65cfde9e5b9b4cf2b6bafa5f3ef33fa6?tpId=117&&tqId=37747&rp=1&ru=/activity/oj&qru=/ta/job-code-high/question-ranking

合并k个有序链表,还是一步步的实现,先写出两个链表合并的函数,然后在遍历k个有序链表,依次进行合并。两个有序链表的合并一定要熟练:有几个关键点:

  1. 要设置一个伪头结点
  2. 在实现一个节点的连接之后要更新当前节点
  3. 在跳出循环之后,需要判断还有那个链表没有遍历完,需要将这个没有遍历完的链表添加到尾部。
import java.util.*;
public class Solution {
    public ListNode mergeKLists(ArrayList<ListNode> lists) {
        ListNode ans = null;
        for (int i = 0; i < lists.size(); ++i) {
            ans = mergeTwo(ans, lists.get(i));
        }
        return ans;
    }
    private ListNode mergeTwo(ListNode l1, ListNode l2){
        ListNode headNode = new ListNode(-1);
        ListNode prev = headNode;
        while(l1 != null && l2 != null){
            if(l1.val >= l2.val){
                prev.next = l2;
                l2 = l2.next;
            }else{
                prev.next = l1;
                l1 = l1.next;
            }
            prev = prev.next;
        }
        prev.next = l1 == null ? l2 : l1;
        return headNode.next;
    }
}

3、字符串的排列

https://www.nowcoder.com/practice/31c1aed01b394f0b8b7734de0324e00f?tpId=117&&tqId=37802&rp=1&ru=/activity/oj&qru=/ta/job-code-high/question-ranking

字符串的排列是最简单的回溯算法,这个包括数字的全排列(LC46)是一类题。
首先这类题一般都是需要有一个布尔类型的数组来对那些字符被用过进行记录,用过之后都不会在被用了,而且一般和结果都被定义为成员变量。
这个由于组成的字符串不能有重复,所以多了几个其他的步骤,就是将字符串转成字符数组之后进行排序,然后处理两个相邻的字符,这就处理了有重复字符的情况,保证重复字符出现的顺序是一致的,比如两个a挨在一起,如果第一个a没有被使用,那就不允许使用第二个a。

import java.util.*;
public class Solution {
    ArrayList<String> res;
    boolean[] vis;
    public ArrayList<String> Permutation(String str) {
        int n = str.length();
        res = new ArrayList<>();
        vis = new boolean[n];
        //采用回溯法
        char[] arr = str.toCharArray();
        Arrays.sort(arr);
        StringBuffer sb = new StringBuffer();
        dfs(sb, 0, arr, n);
        return res;
    }
    private void dfs(StringBuffer sb, int length, char[] arr, int n){
        if(length == n){
            res.add(sb.toString());
            return;
        }
        for(int i = 0; i < arr.length; i++){
            //如果这个点被使用过了
            //或者这个点没有使用过,但存在重复的现象,就必须保证前面和他重复的点一定要被被使用
            //那个点使用之后才能用后面的点,这就保证了重复元素的顺序总是一定的
            //就保证了比如[a,b,c,c]中已经填到了abc,这个时候如果再去填就不会将第二个c给填进去
            if(vis[i] || (i > 0 && !vis[i-1] && arr[i] == arr[i-1])){
                continue;
            }
            vis[i] = true;
            sb.append(arr[i]);
            dfs(sb, length+1, arr, n);
            sb.deleteCharAt(sb.length() - 1);
            vis[i] = false;
        }
    }
}

数字的排列时需要注意一点:就是在添加一个list时,应该采用new ArrayList(list),而不是直接添加list,这样添加的只是引用

class Solution {
    List<List<Integer>> res;
    boolean[] vis;
    public List<List<Integer>> permute(int[] nums) {
        res = new ArrayList<>();
        int n = nums.length;
        vis = new boolean[n];
        List<Integer> list = new ArrayList<>();
        dfs(nums, list, 0, n);
        return res;
    }
    private void dfs(int[] nums, List<Integer> list, int length, int n){
        if(length == n){
            res.add(new ArrayList(list));
            return;
        }
        for(int i = 0; i < n; i++){
            if(vis[i]){
                continue;
            }
            vis[i] = true;
            list.add(nums[i]);
            dfs(nums, list, length + 1, n);
            list.remove(list.size() - 1);
            vis[i] = false;
        }
    }
}

4、接雨水问题

https://www.nowcoder.com/practice/31c1aed01b394f0b8b7734de0324e00f?tpId=117&&tqId=37802&rp=1&ru=/activity/oj&qru=/ta/job-code-high/question-ranking

采用栈来进行解决,一旦当前高度大于栈顶元素高度我们就知道了栈顶这单位长度上是由它在栈中的下面那个高度和当前高度共同确定的,然后写两个循环,第一个循环用来遍历,第二个循环用来出栈并计算雨水体积,不满足第二个循环条件的一律进栈:

while(current < arr.length)
	while(!stack.isEmpty() && arr[stack.peek()] < arr[current])

牛客上这道题比LC上有些不同,他的用例可能比较大,返回的是long,把结果和distance定义为long就行了

import java.util.*;


public class Solution {
    /**
     * max water
     * @param arr int整型一维数组 the array
     * @return long长整型
     */
    public long maxWater (int[] arr) {
        // write code here
        long ans = 0; 
        int current = 0;
        Stack<Integer> stack = new Stack();
        while(current < arr.length){
            while(!stack.isEmpty() && arr[current] > arr[stack.peek()]){
                int top = stack.pop();
                if(stack.isEmpty()){
                    break;
                }
                //横着的距离有多长,这里的距离确实是需要的,因为装水取决的是两边
                //不能只对它所在的一个单位进行计算,还是要看两边的距离
                //画一个图就能看的十分直观
                long distance = current - stack.peek()-1;
                ans += (Math.min(arr[current], arr[stack.peek()]) - arr[top]) * distance;
            }
            stack.push(current++);
        }
        return ans;
    }
}

5、输出二叉树的右视图

https://www.nowcoder.com/practice/c9480213597e45f4807880c763ddd5f0?tpId=117&&tqId=37848&rp=1&ru=/activity/oj&qru=/ta/job-code-high/question-ranking

LC上是直接给的一棵树,然后输出右视图,牛客网上还需要利用前序遍历和中序遍历来重建数之后再进行右视图的展示,重建树的内容在这里就不再这里提了,这里主要就说明一下如何输出右视图。
两种方法都比较直观:
方法一:采用BFS
直接将每层最后一个元素添加进结果集中。

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public List<Integer> rightSideView(TreeNode root) {
        Queue<TreeNode> queue = new LinkedList<>();
        List<Integer> list = new ArrayList<>();
        if(root == null){
            return list;
        }
        queue.offer(root);
        while(!queue.isEmpty()){
            int size = queue.size();
            for(int i = size; i > 0; i--){
                TreeNode node = queue.poll();
                if(node.left != null){
                    queue.offer(node.left);
                }
                if(node.right != null){
                    queue.offer(node.right);
                }
                if(i == 1){
                    list.add(node.val);
                }
            }
        }
        return list;
    }
}

方法二:采用DFS
采用头结点->右节点->左节点这种方式来进行遍历,这样就能得到右视图了,就是另一种前序遍历。定义一个depth的变量,当结果其与结果集中的元素个数相等的时候进行添加,保证添加进去的是这一层的第一个元素,结果集定义为成员变量:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    List<Integer> ans;
    public List<Integer> rightSideView(TreeNode root) {
        ans = new ArrayList<>();
        dfs(root, 0);
        return ans;
    }
    private void dfs(TreeNode node, int depth){
        if(node == null){
            return;
        }
        if(ans.size() == depth){
            ans.add(node.val);
        }
        dfs(node.right, depth+1);
        dfs(node.left, depth+1);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值