刷题笔记--牛客在线算法题📝

470 阅读3分钟

算法题笔记

题源: 牛客网 🔗 www.nowcoder.com/activity/oj…

Q1: 重建二叉树

Q1

思路:

重建二叉树必须要有中序+前序中序+后序才能重建,前序+后序是不能的。且一般是递归进行的。

根据前序+中序重建二叉树,首先需要确定根节点的值。由前序遍历定义可知,前序遍历结果的第一个值就是根节点的值pre[0] == root.val。然后通过根节点的值在中序中的位置idx,可以将中序遍历分成两部分,一部分是左子树的中序遍历结果(in[0]~in[idx-1]),另一部分是右子树的中序遍历结果(in[idx+1]~in[len-1])。然后在左右子树递归此过程即可,Java代码实现参考如下:

import java.util.*;
/**
 * Definition for binary tree
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Solution {
    public TreeNode reConstructBinaryTree(int [] pre,int [] in) {
        int len = pre.length;
        if(len == 0) {
            return null;
        }
        int rootNodeVal = pre[0];
        TreeNode res = new TreeNode(rootNodeVal);
        int idx = 0;
        for(;idx<len;idx++) {
            if(in[idx] == rootNodeVal) {
                break;
            }
        }
        res.left = reConstructBinaryTree(Arrays.copyOfRange(pre,1,1+idx),
                                         Arrays.copyOfRange(in,0,idx));
        res.right = reConstructBinaryTree(Arrays.copyOfRange(pre,1+idx,len),
                                         Arrays.copyOfRange(in,idx+1,len));
        return res;
    }
}

Q2:链表翻转

本题为链表翻转的进阶版,每K个一组进行翻转。同lc25提(https://leetcode-cn.com/problems/reverse-nodes-in-k-group/) q 解题思路:

  1. 遍历链表得到长度,然后计算一共会分多少组
  2. 对每组的节点都存到一个list变量中,然后从后往前遍历,进行翻转操作
  3. 然后织带前一组的最后一个节点的变量pre指向该list的最后一个节点,然后pre重新复制该list的最后一个节点。
  4. 循环step2 直到所有组分完,返回即可 code1
import java.util.*;

/*
 * public class ListNode {
 *   int val;
 *   ListNode next = null;
 * }
 */

public class Solution {
    public ListNode reverseKGroup (ListNode head, int k) {
        // write code here
        if (k == 1 || head == null || head.next == null) {
            return head;
        }
        ListNode dummy = new ListNode(-1);
        dummy.next = head;
        ListNode pre = dummy;
        // 计算一共需要旋转多少组
        int cnt = 0;
        ListNode cur = head;
        while (cur!=null) {
            cur = cur.next;
            cnt++;
        }
        cur = head;
        int group = cnt/k;
        //
        while (group-->0) {
            List<ListNode> l = new ArrayList<>();
            for (int i=0;i<k;i++){
                l.add(cur);
                cur = cur.next;
            }
            for (int i=l.size()-1;i>0;i--){
                l.get(i).next = l.get(i-1);
            }
            pre.next = l.get(l.size()-1);
            l.get(0).next = cur;
            pre = l.get(0);
        }
        return dummy.next;
    }
}

code2 并不需要保存list,直接按位翻转即可

class Solution {
    public ListNode reverseKGroup(ListNode head, int k) {
        ListNode dummy = new ListNode(0), prev = dummy, curr = head, next;
        dummy.next = head;
        int length = 0;
        while(head != null) {
            length++;
            head = head.next;
        }
        head = dummy.next;
        for(int i = 0; i < length / k; i++) {
            for(int j = 0; j < k - 1; j++) {
                next = curr.next;
                curr.next = next.next;
                next.next = prev.next;
                prev.next = next;
            }
            prev = curr;
            curr = prev.next;
        }
        return dummy.next;
    }
}

Q3: 判断链表是否有环

Q 解题思路:

典型的快慢指针问题

/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public boolean hasCycle(ListNode head) {
        if (head == null || head.next == null){
            return false;
        }
        ListNode slow=head,quick=head.next;
        while (quick != null) {
            if (slow == quick) return true;
            slow = slow.next;
            quick = quick.next;
            if (quick != null) {
                quick = quick.next;
            }else return false;
        }
        return false;
    }
}

Q4 判断二叉树是否对称

Q 解题思路:

典型的树🌲递归问题,完全对称的充要条件就是当前两节点值相同,且左右孩子也分别对称。

import java.util.*;

/*
 * public class TreeNode {
 *   int val = 0;
 *   TreeNode left = null;
 *   TreeNode right = null;
 * }
 */

public class Solution {
    public boolean isSymmetric (TreeNode root) {
        // write code here
        if (root == null) {
            return true;
        }
        return helper(root.left,root.right);
    }

    private boolean helper(TreeNode p1,TreeNode p2) {
        if (p1 == null || p2 == null) {
            return p1 == null && p2 == null;
        }
        if (p1.val == p2.val) {
            return helper(p1.left,p2.right)&&helper(p1.right,p2.left);
        }else {
            return false;
        }
    }

}

Q5 青蛙🐸跳台阶问题

Q 典型的dp问题,当前位置只能从当前-1的台阶,或者-2两个台阶跳上来,因此可的状态转移方程:

dp[n] = dp[n-1]+dp[n-2]

进而代码如下:

public class Solution {
  public int JumpFloor(int target) {
        if (target <= 2) return target;
        int[] dp = new int[target+1];
        dp[1] = 1;
        dp[2] = 2;
        for (int i=3;i<=target;i++) {
            dp[i] = dp[i-1]+dp[i-2];
        }
        return dp[target];
    }
}

Q6 LRU cache

缓存作为帮助快速访问热点的数据的结构当然存越多的数据越好,但是缓存是在内存中,需要占据宝贵的存储资源。因此需要采用合理的缓存失效策略,LRU cache就是一种常用缓存失效的策略(Redis中就可以使用LRU作为缓存失效的策略)。 Q

import java.util.*;


public class Solution {
/**
     * lru design
     * @param operators int整型二维数组 the ops
     * @param k int整型 the k
     * @return int整型一维数组
     */
     /**
     * lru design
     * @param operators int整型二维数组 the ops
     * @param k int整型 the k
     * @return int整型一维数组
     */
    public int[] LRU (int[][] operators, int k) {
        // write code here
        LRUADT lru = new LRUADT(k);
        List<Integer> res = new ArrayList<>();
        for (int[] op : operators) {
            if (op[0] == 1) {
                lru.set(op[1],op[2]);
            }else {
                res.add(lru.get(op[1]));
            }
        }
        int[] r = new int[res.size()];
        for (int i=0;i<r.length;i++) {
            r[i] = res.get(i);
        }
        return r;
    }

    class LRUADT {

        Map<Integer,Node> cache;
        Node head,tail;
        int capacity;
        class Node  {
            int key;
            int val;
            Node next,prev;
            Node(int key,int val) {
                this.key = key;
                this.val = val;
            }
        }

        LRUADT(int capacity) {
            cache = new HashMap<>();
            head = new Node(-1,-1);
            tail = new Node(-1,-1);
            head.next = tail;
            tail.prev = head;
            this.capacity = capacity;
        }
        int get(int key) {
            if (cache.containsKey(key)) {
                // 设置为最早的
                rotateFirst(key);
                return cache.get(key).val;
            }else {
                return -1;
            }
        }

        void set(int key,int val) {
            if (cache.containsKey(key)){
                Node n = cache.get(key);
                delNode(n);
                n.val = val;
                addFirst(n);
            }else if (cache.size() < capacity) {
                Node n = new Node(key,val);
                addFirst(n);
                cache.put(key,n);
            }else {
                // 删去最后一个节点
                Node del = tail.prev;
                delNode(del);
                cache.remove(del.key);
                Node n = new Node(key,val);
                addFirst(n);
                cache.put(key,n);
            }
        }

        void rotateFirst(int key) {
            Node cur = cache.get(key);
            if (cur.prev == head) {
                return;
            }
            // 链表中删去 Node
            delNode(cur);
            // Node 加到最前面
            addFirst(cur);
        }

        void addFirst(Node n){
            Node first = head.next;
            head.next = n;
            n.prev = head;
            n.next = first;
            first.prev = n;
        }

        void delNode(Node n) {
            Node prev = n.prev;
            Node nxt = n.next;
            n.prev = null;
            n.next = null;
            prev.next = nxt;
            nxt.prev = prev;
        }

    }

}

Q7 合并有序链表

遍历两链表,每次将小的节点加入结果中即可。 Q


import java.util.*;

/*
 * public class ListNode {
 *   int val;
 *   ListNode next = null;
 * }
 */

public class Solution {
/**
     *
     * @param l1 ListNode类
     * @param l2 ListNode类
     * @return ListNode类
     */
    public ListNode mergeTwoLists (ListNode l1, ListNode l2) {
        // write code here
        ListNode dummy = new ListNode(-1);
        ListNode cur = dummy;
       while (l1 != null && l2 != null) {
            if (l1.val <= l2.val) {
                cur.next = l1;
                cur = cur.next;
                l1 = l1.next;
            }else {
                cur.next = l2;
                cur = cur.next;
                l2 = l2.next;
            }
        }
        if (l1 != null) {
            cur.next = l1;
        }else if (l2 != null) {
            cur.next = l2;
        }
        return dummy.next;
    }
}

Q8 链表中环的入口节点

本题为lc经典算法题,目标是考察对floyd判圈算法的认知。 Q floyd判圈算法参考下文链接:

ref: www.cnblogs.com/xudong-bupt…

具体代码如下:

/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
/**
     * floyd 判环算法
     * @param head
     * @return
     */
    public ListNode detectCycle(ListNode head) {
        if (head == null || head.next == null) return null;
        ListNode slow = head;
        ListNode quick = head;
        while (true) {
            slow = slow.next;
            quick = quick.next;
            if (quick == null) return null;
            quick = quick.next;
            if (quick == null) return null;
            if (slow == quick) break;
        }
        // 相遇
        slow = head;
        while (slow != quick) {
            slow = slow.next;
            quick = quick.next;
        }
        return slow;
    }
}

Q9 大数加法

分析:

通过字符串进行超大数加减法,为经典算法题。只需通过按字符串从右往左依次进行相加即可,并保存进位值prev

    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     * 计算两个数之和
     * @param s string字符串 表示第一个整数
     * @param t string字符串 表示第二个整数
     * @return string字符串
     */
    public String solve (String s, String t) {
        // write code here
        int len = Math.max(s.length(),t.length());
        char[] res = new char[len+1];
        int prev = 0;
        for (int i=0;i<res.length;i++){
            int a1=0,a2=0;
            if (i < s.length()) {
                a1 = s.charAt(s.length()-1-i)-'0';
            }
            if (i < t.length()) {
                a2 = t.charAt(t.length()-1-i)-'0';
            }
            int v = a1+a2+prev;
            if (v >= 10) {
                res[res.length-1-i] = (char)((v%10)+'0');
                prev = v/10;
            }else {
                res[res.length-1-i] = (char)(v+'0');
                prev = 0;
            }
        }
        if (res[0] == '0'){
            return String.valueOf(Arrays.copyOfRange(res,1,res.length));
        }else {
            return String.valueOf(res);
        }
    }

Q10 合并K个已排序链表

该题与Q7 合并有序链表核心是相似的。本题只需遍历lists中的每个ListNode然后找到最小的续上答案里即可。 代码如下:

import java.util.*;
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
     public ListNode mergeKLists(ArrayList<ListNode> lists) {
        ListNode dummy = new ListNode(-1);
        ListNode cur = dummy;
        lists.removeIf(Objects::isNull);
        while (lists.size() > 1){
            // find min
            int idx = -1;
            int min = Integer.MAX_VALUE;
            for (int i=0;i<lists.size();i++) {
                ListNode c = lists.get(i);
                if (c.val < min) {
                    idx = i;
                    min = c.val;
                }
            }
            // min next
            cur.next = lists.get(idx);
            cur = cur.next;
            ListNode c = lists.get(idx);
            if (c.next == null) {
                lists.remove(idx);
            }else {
                lists.set(idx,c.next);
            }
        }
        if (lists.size() == 1) {
            cur.next = lists.get(0);
        }
        return dummy.next;
    }
}

总结

以上为牛客在线算法题 Q1~Q10,之后继续更新全部的题解~