刷题打卡第六天

2022-11-22

牛客-面试高频榜单

2 简单 3 中等 1困难

66 两个链表的第一个公共结点

输入两个无环的单向链表,找出它们的第一个公共结点,如果没有公共节点则返回空。(注意因为传入数据是链表,所以错误测试数据的提示是用其他方式显示的,保证传入数据是正确的)

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

    ListNode(int val) {
        this.val = val;
    }
}*/
public class Solution {
    public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
        ListNode l1 = pHead1, l2 = pHead2;
        while(l1 != l2){
            l1 = (l1==null)?pHead2:l1.next;
            l2 = (l2==null)?pHead1:l2.next;
        }
        return l1;
    }
}

38 螺旋矩阵

给定一个m x n大小的矩阵(m行,n列),按螺旋的顺序返回矩阵中的所有元素。

1 2 3

4 5 6

7 8 9

返回[1,2,3,6,9,8,7,4,5]

import java.util.*;
public class Solution {
    public ArrayList<Integer> spiralOrder(int[][] matrix) {
        ArrayList<Integer> res = new ArrayList<>();
        if(matrix.length == 0)
            return res;
        // 定义四个指针,并且充当边界限制的作用
        int top = 0, bottom = matrix.length-1;
        int left = 0, right = matrix[0].length-1;
 
        while( top < (matrix.length+1)/2 && left < (matrix[0].length+1)/2 ){
            //上面  左到右
            for(int i = left; i <= right; i++){
                res.add(matrix[top][i]);
            }
 
            //右边 上到下
            for(int i = top+1; i <= bottom; i++){
                res.add(matrix[i][right]);
            }
 
            //下面  右到左
            for(int i = right-1; top!=bottom && i>=left; i--){
                res.add(matrix[bottom][i]);
            }
 
            //左边 下到上
            for(int i = bottom-1; left!=right && i>=top+1; i--){
                res.add(matrix[i][left]);
            }
            // 遍历完一圈之后,所有往里面靠
            ++top;
            --bottom;
            ++left;
            --right;
        }
        return res;
    }
}

12 重建二叉树

给定节点数为 n 的二叉树的前序遍历和中序遍历结果,请重建出该二叉树并返回它的头结点。

import java.util.*;
public class Solution {
    public TreeNode reConstructBinaryTree(int [] pre,int [] vin) {
        int n = pre.length;
        int m = vin.length;
        //每个遍历都不能为0
        if(n == 0 || m == 0) 
            return null;
        //构建根节点
        TreeNode root = new TreeNode(pre[0]);
        for(int i = 0; i < vin.length; i++){
            //找到中序遍历中的前序第一个元素
            if(pre[0] == vin[i]){ 
                //构建左子树
                root.left = reConstructBinaryTree(Arrays.copyOfRange(pre, 1, i + 1), Arrays.copyOfRange(vin, 0, i)); 
                //构建右子树
                root.right = reConstructBinaryTree(Arrays.copyOfRange(pre, i + 1, pre.length), Arrays.copyOfRange(vin, i + 1, vin.length));
                break;
            }
        }
        return root;
    }
}

91 最长上升子序列(三)

给定数组 arr ,设长度为 n ,输出 arr 的最长上升子序列。(如果有多个答案,请输出其中 按数值(注:区别于按单个字符的ASCII码值)进行比较的 字典序最小的那个)

import java.util.*;


public class Solution {
    /**
     * retrun the longest increasing subsequence
     * @param arr int整型一维数组 the array
     * @return int整型一维数组
     */
    public int[] LIS (int[] arr) {
        int n=arr.length;
        //维护一个单调递增tail数组
        int[] tail=new int[n];
        //dp[i]表示以i位置结尾的最长上升子序列长度
        int[] dp=new int[n];
        //最长上升子序列长度
        int len=0;
        for(int i=0;i<n;i++){
            //如果tail数组为空,或者tail数组最后一个元素小于arr[i]
            if(i==0||tail[len-1]<arr[i]){
                //直接加在最后面
                tail[len++]=arr[i];
                //记录当前长度
                dp[i]=len;
            }
            else{
                //二分法找到tail数组中第一个大于等于arr[i]的元素位置
                int index=search(tail,len,arr[i]);
                //跟新index处的值
                tail[index]=arr[i];
                //记录当前长度
                dp[i]=index+1;
            }
        }
        int[] res=new int[len];
        //从后向前依次给子序列赋值
        for(int i=n-1;i>=0;i--){
            if(dp[i]==len){
                res[--len]=arr[i];
            }
        }
        return res;
    }
    
    //二分法找tail数组中第一个大于等于arr[i]的元素位置
    private int search(int[] nums,int len,int k){
        int low=0,high=len-1;
        while(low<high){
            int mid=(high+low)/2;
            //如果大于等于k,排除mid往右的所有元素
            if(nums[mid]>=k){
                high=mid;
            }
            //否则排除mid以及mid往左的所有元素
            else{
                low=mid+1;
            }
        }
        return low;
    }
}

121 字符串的排列

输入一个长度为 n 字符串,打印出该字符串中字符的所有排列,你可以以任意顺序返回这个字符串数组。

例如输入字符串ABC,则输出由字符A,B,C所能排列出来的所有字符串ABC,ACB,BAC,BCA,CBA和CAB。

import java.util.*;
public class Solution {
    Set<String> res = new HashSet<>();
    public ArrayList<String> Permutation(String str) {
         boolean[] visited = new boolean[str.length()];
         recursion(str,"",visited);
         ArrayList<String> list = new ArrayList<>(res);
         Collections.sort(list);
        return list;
    }
    public void recursion(String str,String current,boolean[] visited){
        if(str.length()==current.length()){
            res.add(current);
            return;
        }
        for(int i = 0;i<str.length();i++)
        {
          if(!visited[i]){
              current+=str.charAt(i);
              visited[i] = true;
              recursion(str,current,visited);
              current = current.substring(0,current.length()-1);
              visited[i] = false;
            }
        } 
    }
}

94 设计LFU缓存结构

一个缓存结构需要实现如下功能。

  • set(key, value):将记录(key, value)插入该结构
  • get(key):返回key对应的value值

但是缓存结构中最多放K条记录,如果新的第K+1条记录要加入,就需要根据策略删掉一条记录,然后才能把新记录加入。这个策略为:在缓存结构的K条记录中,哪一个key从进入缓存结构的时刻开始,被调用set或者get的次数最少,就删掉这个key的记录;

如果调用次数最少的key有多个,上次调用发生最早的key被删除

这就是LFU缓存替换算法。实现这个结构,K作为参数给出

若opt=1,接下来两个整数x, y,表示set(x, y)
若opt=2,接下来一个整数x,表示get(x),若x未出现过或已被移除,则返回-1

对于每个操作2,返回一个答案

import java.util.*;
public class Solution {
    //设置节点结构
    static class Node{ 
        int freq;
        int key;
        int val;
        //初始化
        public Node(int freq, int key, int val) {
            this.freq = freq;
            this.key = key;
            this.val = val;
        }
    }
    //频率到双向链表的哈希表
    private Map<Integer, LinkedList<Node> > freq_mp = new HashMap<>();
    //key到节点的哈希表
    private Map<Integer, Node> mp = new HashMap<>();
    //记录缓存剩余容量
    private int size = 0; 
    //记录当前最小频次
    private int min_freq = 0;
    
    public int[] LFU (int[][] operators, int k) {
        //构建初始化连接
        //链表剩余大小
        this.size = k;
        //获取操作数
        int len = (int)Arrays.stream(operators).filter(x -> x[0] == 2).count();
        int[] res = new int[len];
        //遍历所有操作
        for(int i = 0, j = 0; i < operators.length; i++){
            if(operators[i][0] == 1)
                //set操作
                set(operators[i][1], operators[i][2]);
            else
                //get操作
                res[j++] = get(operators[i][1]);
        }
        return res;
    }
    
    //调用函数时更新频率或者val值
    private void update(Node node, int key, int value) { 
        //找到频率
        int freq = node.freq;
        //原频率中删除该节点
        freq_mp.get(freq).remove(node); 
        //哈希表中该频率已无节点,直接删除
        if(freq_mp.get(freq).isEmpty()){ 
            freq_mp.remove(freq);
            //若当前频率为最小,最小频率加1
            if(min_freq == freq) 
                min_freq++;
        }
        if(!freq_mp.containsKey(freq + 1))
            freq_mp.put(freq + 1, new LinkedList<Node>());
        //插入频率加一的双向链表表头,链表中对应:freq key value
        freq_mp.get(freq + 1).addFirst(new Node(freq + 1, key, value)); 
        mp.put(key, freq_mp.get(freq + 1).getFirst());
    }
    
    //set操作函数
    private void set(int key, int value) {
        //在哈希表中找到key值
        if(mp.containsKey(key)) 
            //若是哈希表中有,则更新值与频率
            update(mp.get(key), key, value);
        else{ 
            //哈希表中没有,即链表中没有
            if(size == 0){
                //满容量取频率最低且最早的删掉
                int oldkey = freq_mp.get(min_freq).getLast().key; 
                //频率哈希表的链表中删除
                freq_mp.get(min_freq).removeLast(); 
                if(freq_mp.get(min_freq).isEmpty()) 
                    freq_mp.remove(min_freq); 
                //链表哈希表中删除
                mp.remove(oldkey); 
            }
            //若有空闲则直接加入,容量减1
            else 
                size--; 
            //最小频率置为1
            min_freq = 1; 
            //在频率为1的双向链表表头插入该键
            if(!freq_mp.containsKey(1))
                freq_mp.put(1, new LinkedList<Node>());
            freq_mp.get(1).addFirst(new Node(1, key, value)); 
            //哈希表key值指向链表中该位置
            mp.put(key, freq_mp.get(1).getFirst()); 
        }
    }
    
    //get操作函数
    private int get(int key) {
        int res = -1;
        //查找哈希表
        if(mp.containsKey(key)){ 
            Node node = mp.get(key);
            //根据哈希表直接获取值
            res = node.val;
            //更新频率 
            update(node, key, res); 
        }
        return res;
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值