面试手撕代码

  1. 判断链表是否有环
  2. 前k个大的数
  3. 给定一个字符串,返回后面k个字符(采用sunstring(int start, int end),或者利用StringBuilder来Copy)
  4. 计算1/2=0.5,1/3=0.(3) 输入两个数,计算最后的结果,以字符串输出。要考虑负数,除数不为0,其他循环小数的情况比如12(125),(125)
  5. 输出链表:①线性存储;②递归;③分为两半,翻转后面一半,再依次连接 输入:1-2-3-4-5-6-7-8 输出:1-8-2-7-3-6-4-5
  6. 反转单链表(两种方法),斐波那契数列(跳台阶)(动态规划)
  7. 快速排序
  8. String字符串统计abc,链表倒数第k个(双指针)
  9. 长度为m的数组放1-m的数字,有些数字存在一次,有些存在两次,怎么得到只存在一次的数字(讲思路)
    • 若只有一个,则采用异或运算
    • 多个结果,则采用数组或HashMap计数,或者HashSet来实现
  10. 二叉树中寻找两个节点的公共父节点?
  11. 二叉树深度
  12. 数组里的第2大元素(堆,冒泡思想,快排)
  13. 单向链表的倒数第n个节点(双指针)

1、判断链表是否有环

class ListNode{
    int val;
    ListNode next;
    public ListNode(int val){
        this.val = val;
        next = null;
    }
}
public class ListCycle {
    public ListNode detectCycle(ListNode head) {
        // 采用快慢指针
        ListNode fast = head, slow = head;
        while(fast != null && fast.next != null){
            fast = fast.next.next;
            slow = slow.next;
            if(fast == slow){
                fast = head;
                while(fast != slow){
                    fast = fast.next;
                    slow = slow.next;
                }
                break;
            }
        }
        return fast == null || fast.next == null ? null : fast;
    }
}

删除有序数组中的重复项

public int removeDuplicates(int[] nums) {
        // 采用快慢指针
        int n = nums.length;
        if(n == 0) return 0;
        int slow = 0, fast = 0;
        while(fast < n){
            if(nums[fast] != nums[slow]){
                slow++;
                nums[slow] = nums[fast];
            }
            fast++;
        }
        return slow + 1;
    }

删除排序链表中的重复元素

public ListNode deleteDuplicates(ListNode head) {
        if(head == null) return null;
        ListNode slow = head, fast = head.next;
        while(fast != null){
            if(fast.val != slow.val){
                slow.next = fast;
                slow = slow.next;
            }
            fast = fast.next;
        }
        slow.next = null;
        return head;
    }

快速排序/数组中前k个大的数(也可采用冒泡,k次循环)

import java.util.Comparator;
import java.util.PriorityQueue;
public class QuickSort {
    // 快速排序
    public static void sort(int[] arr, int start, int end){
        if(start >= end) return;
        int part = partition(arr, start, end);
        sort(arr, start, part - 1);
        sort(arr, part + 1, end);
    }
    // 获取第k大的元素/获取最大的k个元素(修改数组,无序)
    public static int findKthLargest(int[] nums, int k){
        int len = nums.length;
        int low = 0, high = len - 1;
        k = len - k;
        while(low < high){// 采用二分法+快速排序
            int p = partition(nums, low, high);
            if(p < k){
                low = p + 1;
            }else if(p > k){
                high = p - 1;
            }else{
                return nums[p];// 返回元素值
//                return p;// 返回索引
            }
        }
        return  -1;
    }
    // 获取最大的k个元素(不修改数组,有序)
    public static int[] firstK(int[] nums, int k){
        int[] res = new int[k];
        PriorityQueue<Integer> priorityQueue = new PriorityQueue<>(k, new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o2 - o1;
            }
        });// 大顶堆
        for (int num : nums) {
            priorityQueue.add(num);
        }
        for (int i = k - 1; i >= 0 ; i--) {
            res[i] = priorityQueue.poll();
        }
        return res;
    }

    public static int partition(int[] arr, int start, int end){
        if(start == end) return start;
        int left = start, right = end + 1;
        int mid = arr[start];
        while(true){
            while(arr[++left] < mid){// 注意!!!
                if (left == end) break;
            }
            while(arr[--right] > mid){
                if (right == start) break;
            }
            if(left >= right) break;
            swap(arr, left, right);
        }
        swap(arr, start, right);
        return right;
    }

    public static void swap(int[] arr, int i, int j){
        arr[i] = arr[i] ^ arr[j];
        arr[j] = arr[i] ^ arr[j];
        arr[i] = arr[i] ^ arr[j];
    }

    public static void main(String[] args) {
        int[] arr = new int[]{5,6,9,5,7,3,2,4,8};
        /*sort(arr, 0, arr.length - 1);
        for (int num : arr) {
            System.out.print(num);
        }*/
//        System.out.println(findKthLargest(arr, 3));
        int[] res = firstK(arr, 3);
        for (int num : res) {
            System.out.print(num + " ");
        }
    }
}

链表转换:将原链表的每个节点添加到新链表的头部和尾部

1->2->3->4->5 5->3->1->2->4

public ListNode formatList (ListNode head) {   
    if(head == null) return null;
    if(head.next == null) return head;
    ListNode cur = head.next;
    ListNode start = head, end = head;
    end.next = null;
    ListNode next = cur.next;
    while(cur != null){
        // 插尾节点
        end.next = cur;
        cur = next;
        if(cur == null) break;
        next = cur.next;
        end = end.next;
        end.next = null;
        
        // 先插头节点
        cur.next = start;
        start = cur;
        cur = next;
        if(next != null ) next = cur.next;
    }
    return start;
}

链表转换:

输入:1-2-3-4-5-6-7-8 输出:1-8-2-7-3-6-4-5

// 方式一:
public void reorderList(ListNode head) {
    if (head == null) return;
    //存到 list 中去
    List<ListNode> list = new ArrayList<>();
    while (head != null) {
        list.add(head);
        head = head.next;
    }
    //头尾指针依次取元素
    int i = 0, j = list.size() - 1;
    while (i < j) {
        list.get(i).next = list.get(j);
        i++;
        //偶数个节点的情况,会提前相遇
        if (i == j) break;
        list.get(j).next = list.get(i);
        j--;
    }
    list.get(i).next = null;
}

// 方式二:
public void reorderList(ListNode head) {
    if (head == null || head.next == null || head.next.next == null) {
        return;
    }
    int len = 0;
    ListNode h = head;
    //求出节点数
    while (h != null) {
        len++;
        h = h.next;
    }
    reorderListHelper(head, len);
}

private ListNode reorderListHelper(ListNode head, int len) {
    if (len == 1) {
        ListNode outTail = head.next;
        head.next = null;
        return outTail;
    }
    if (len == 2) {
        ListNode outTail = head.next.next;
        head.next.next = null;
        return outTail;
    }
    //得到对应的尾节点,并且将头结点和尾节点之间的链表通过递归处理
    ListNode tail = reorderListHelper(head.next, len - 2);
    ListNode subHead = head.next;//中间链表的头结点
    head.next = tail;
    ListNode outTail = tail.next;  //上一层 head 对应的 tail
    tail.next = subHead;
    return outTail;
}

// 方式三:
public void reorderList(ListNode head) {
    if (head == null || head.next == null || head.next.next == null) {
        return;
    }
    //找中点,链表分成两个
    ListNode slow = head;
    ListNode fast = head;
    while (fast.next != null && fast.next.next != null) {
        slow = slow.next;
        fast = fast.next.next;
    }
    ListNode newHead = slow.next;
    slow.next = null;
    //第二个链表倒置
    newHead = reverseList(newHead);
    //链表节点依次连接
    while (newHead != null) {
        ListNode temp = newHead.next;
        newHead.next = head.next;
        head.next = newHead;
        head = newHead.next;
        newHead = temp;
    }
}

private ListNode reverseList(ListNode head) {
    if (head == null) return null;
    ListNode tail = head;
    head = head.next;
    tail.next = null;
    while (head != null) {
        ListNode temp = head.next;
        head.next = tail;
        tail = head;
        head = temp;
    }
    return tail;
}

二叉树中寻找两个节点的公共父节点

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if(root == null || root == p || root == q) return root;
        TreeNode left = lowestCommonAncestor(root.left, p, q);
        TreeNode right = lowestCommonAncestor(root.right, p, q);
        if(left == null && right == null) return null; // 1.
        if(left == null) return right; // 3.
        if(right == null) return left; // 4.
        return root; // 2. if(left != null and right != null)
    }
}

二叉树深度

// 后序遍历
class Solution {
    public int maxDepth(TreeNode root) {
        if(root == null) return 0;
        return Math.max(maxDepth(root.left), maxDepth(root.right)) + 1;
    }
}
// 层序遍历
class Solution {
    public int maxDepth(TreeNode root) {
        if(root == null) return 0;
        List<TreeNode> queue = new LinkedList<>() {{ add(root); }}, tmp;
        int res = 0;
        while(!queue.isEmpty()) {
            tmp = new LinkedList<>();
            for(TreeNode node : queue) {
                if(node.left != null) tmp.add(node.left);
                if(node.right != null) tmp.add(node.right);
            }
            queue = tmp;
            res++;
        }
        return res;
    }
}
// 二叉树的最小深度
class Solution {
    public int minDepth(TreeNode root) {
        if (root == null) return 0;
        Queue<TreeNode> q = new LinkedList<>();
        q.offer(root);
        // root 本身就是一层,depth 初始化为 1
        int depth = 1;

        while (!q.isEmpty()) {
            int sz = q.size();
            /* 遍历当前层的节点 */
            for (int i = 0; i < sz; i++) {
                TreeNode cur = q.poll();
                /* 判断是否到达叶子结点 */
                if (cur.left == null && cur.right == null)
                    return depth;
                /* 将下一层节点加入队列 */
                if (cur.left != null)
                    q.offer(cur.left);
                if (cur.right != null)
                    q.offer(cur.right);
            }
            /* 这里增加步数 */
            depth++;
        }
        return depth;
    }
}

// 二叉树的最大深度
/***** 解法一,回溯算法思路 *****/
class Solution {
    int depth = 0;
    int res = 0;
    public int maxDepth(TreeNode root) {
        traverse(root);
        return res;
    }
    // 遍历二叉树
    void traverse(TreeNode root) {
        if (root == null) {
            return;
        }
        // 前序遍历位置
        depth++;
        // 遍历的过程中记录最大深度
        res = Math.max(res, depth);
        traverse(root.left);
        traverse(root.right);
        // 后序遍历位置
        depth--;
    }
}

/***** 解法二,动态规划思路 *****/
class Solution2 {
    // 定义:输入一个节点,返回以该节点为根的二叉树的最大深度
    public int maxDepth(TreeNode root) {
        if (root == null) {
            return 0;
        }
        int leftMax = maxDepth(root.left);
        int rightMax = maxDepth(root.right);
        // 根据左右子树的最大深度推出原二叉树的最大深度
        return 1 + Math.max(leftMax, rightMax);
    }
}

LRU缓存

LRU缓存

class LRUCache {
    int cap;
    LinkedHashMap<Integer, Integer> cache = new LinkedHashMap<>();
    public LRUCache(int capacity) {
        this.cap = capacity;
    }

    public int get(int key) {
        if (!cache.containsKey(key)) {
            return -1;
        }
        // 将 key 变为最近使用
        makeRecently(key);
        return cache.get(key);
    }

    public void put(int key, int val) {
        if (cache.containsKey(key)) {
            // 修改 key 的值
            cache.put(key, val);
            // 将 key 变为最近使用
            makeRecently(key);
            return;
        }

        if (cache.size() >= this.cap) {
            // 链表头部就是最久未使用的 key
            int oldestKey = cache.keySet().iterator().next();
            cache.remove(oldestKey);
        }
        // 将新的 key 添加链表尾部
        cache.put(key, val);
    }

    private void makeRecently(int key) {
        int val = cache.get(key);
        // 删除 key,重新插入到队尾
        cache.remove(key);
        cache.put(key, val);
    }
}

LFU缓存

class LFUCache {

    // key 到 val 的映射,我们后文称为 KV 表
    HashMap<Integer, Integer> keyToVal;
    // key 到 freq 的映射,我们后文称为 KF 表
    HashMap<Integer, Integer> keyToFreq;
    // freq 到 key 列表的映射,我们后文称为 FK 表
    HashMap<Integer, LinkedHashSet<Integer>> freqToKeys;
    // 记录最小的频次
    int minFreq;
    // 记录 LFU 缓存的最大容量
    int cap;

    public LFUCache(int capacity) {
        keyToVal = new HashMap<>();
        keyToFreq = new HashMap<>();
        freqToKeys = new HashMap<>();
        this.cap = capacity;
        this.minFreq = 0;
    }

    public int get(int key) {
        if (!keyToVal.containsKey(key)) {
            return -1;
        }
        // 增加 key 对应的 freq
        increaseFreq(key);
        return keyToVal.get(key);
    }

    public void put(int key, int val) {
        if (this.cap <= 0) return;

        /* 若 key 已存在,修改对应的 val 即可 */
        if (keyToVal.containsKey(key)) {
            keyToVal.put(key, val);
            // key 对应的 freq 加一
            increaseFreq(key);
            return;
        }

        /* key 不存在,需要插入 */
        /* 容量已满的话需要淘汰一个 freq 最小的 key */
        if (this.cap <= keyToVal.size()) {
            removeMinFreqKey();
        }

        /* 插入 key 和 val,对应的 freq 为 1 */
        // 插入 KV 表
        keyToVal.put(key, val);
        // 插入 KF 表
        keyToFreq.put(key, 1);
        // 插入 FK 表
        freqToKeys.putIfAbsent(1, new LinkedHashSet<>());
        freqToKeys.get(1).add(key);
        // 插入新 key 后最小的 freq 肯定是 1
        this.minFreq = 1;
    }

    private void increaseFreq(int key) {
        int freq = keyToFreq.get(key);
        /* 更新 KF 表 */
        keyToFreq.put(key, freq + 1);
        /* 更新 FK 表 */
        // 将 key 从 freq 对应的列表中删除
        freqToKeys.get(freq).remove(key);
        // 将 key 加入 freq + 1 对应的列表中
        freqToKeys.putIfAbsent(freq + 1, new LinkedHashSet<>());
        freqToKeys.get(freq + 1).add(key);
        // 如果 freq 对应的列表空了,移除这个 freq
        if (freqToKeys.get(freq).isEmpty()) {
            freqToKeys.remove(freq);
            // 如果这个 freq 恰好是 minFreq,更新 minFreq
            if (freq == this.minFreq) {
                this.minFreq++;
            }
        }
    }

    private void removeMinFreqKey() {
        // freq 最小的 key 列表
        LinkedHashSet<Integer> keyList = freqToKeys.get(this.minFreq);
        // 其中最先被插入的那个 key 就是该被淘汰的 key
        int deletedKey = keyList.iterator().next();
        /* 更新 FK 表 */
        keyList.remove(deletedKey);
        if (keyList.isEmpty()) {
            freqToKeys.remove(this.minFreq);
            // 问:这里需要更新 minFreq 的值吗?
        }
        /* 更新 KV 表 */
        keyToVal.remove(deletedKey);
        /* 更新 KF 表 */
        keyToFreq.remove(deletedKey);
    }
}
全部评论

相关推荐

hso_:哈哈哈哈哈哈我没offer一样在同一道题开喷了
投递深圳同为数码等公司10个岗位
点赞 评论 收藏
分享
11-08 13:58
门头沟学院 Java
程序员小白条:竟然是蓝桥杯人才doge,还要花钱申领的offer,这么好的公司哪里去找
点赞 评论 收藏
分享
点赞 2 评论
分享
牛客网
牛客企业服务