小镇错题家刷题日常1

160 阅读4分钟

1. 反转链表

代码实现
非递归
思路:声明一个pre和cur变量,因为要反转,所以要让cur.next域指向pre,然后进行下一步迭代,因为下一步迭代前,cur断了,所以在迭代之前要用一个next变量来存储cur的下一个结点,直到迭代到cur为空.

class Solution {
    public ListNode reverseList(ListNode head) {
        ListNode pre = null;
        ListNode cur = head;
        while(cur != null){
            ListNode next = cur.next;
            cur.next = pre;
            pre = cur;
            cur = next;
        }
        return pre;
    }
}

递归
思路:其实跟上面非递归差不多

class Solution {
    public ListNode reverseList(ListNode head) {
        if(head == null) return null;
        //初始时pre为null, cur为head上面的题也是一样
        return reverse(null, head);   
    }

    public ListNode reverse(ListNode pre, ListNode cur){
        ListNode next = cur.next;
        cur.next = pre;
        if(next == null) return cur;
        return reverse(cur, next);
    }
}

2. 无重复字符的最长子串

思路:一眼丁真,鉴定为滑动窗口,为什么是滑动窗口嘞,因为要找最长无重复字符子串,让窗口内的字符子串不重复,重复了就收缩,而这个窗口也就是子串,用个变量存储结果,每次向右扩张完就判断是否最长。思路是先让滑动窗口向右扩张,如果遇到重复了就收缩到窗口内那个重复字符的右边第一个。

代码实现

class Solution {
    public int lengthOfLongestSubstring(String s) {
        int len = s.length();
        if(len <= 1) return len;
        Map<Character, Integer> map = new HashMap<>();
        int left = 0;
        int res = 1;
        for(int i = 0; i < s.length(); i++){
            if(map.containsKey(s.charAt(i))){
                left = Math.max(map.get(s.charAt(i)) + 1, left);
            }
            map.put(s.charAt(i), i);
            if(i - left + 1 > res) res = i - left + 1;
        }
        return res;
    }
}

3. LRU 缓存

做这道题前先来了解一下LRU。
LRU全称Least Recently Used,翻译过来也就是最近最少使用,是一种页面置换算法(操作系统的),是选择最近最久未使用的页面予以淘汰。拿个具体的例子,我们应该用过手机的任务管理器吧,每次点开新的程序或者一个正在运行中的程序,打开管理器时,这个总会放到第一位,而假设一个任务管理器你只能放有限个并且当前任务的槽位满了,那么我们就需要把最末端(也就是最近最久未使用)的给淘汰掉。

思路:从get和put来看,不难看出来要用一个哈希表存储吧,而当容量满了,插入的新的key-value要将旧的淘汰,说明有序,而哈希表并不是有序的,所以我们应该用一个有序的数据结构,链表或数组(建议不使用数组,因为在删除的时候需要很多次移动,需要O(n)的时间复杂度,而哈希表也能通过key快速定位到这个key-value,算是可随机访问到key-value,从而可以将这个key-value删除掉,所以没必要用数组)来存储这个序列,哈希表则负责将链表的值存储到哈希表的value里。其实在Java中也提供了一个LinkedListHashMap,可以通过重写里面的方法,来实现RLU(读源码,里面有一个方法有说)

代码实现

class LRUCache {
    Node head;
    Node tail;
    Map<Integer, Node> map = new HashMap<>();
    int capacity;
    int count;

    public LRUCache(int capacity) {
        head = new Node();
        tail = new Node();
        head.next = tail;
        tail.pre = head;
        this.capacity = capacity;
        int count = 0;
    }
    
    public int get(int key) {
        if(!map.containsKey(key)){
            return -1;
        }
        Node node = map.get(key);
        deleteNode(node);
        addHead(node);
        return node.value;
    }
    
    public void put(int key, int value) {
        //如果存在key,则先删掉
        if(map.containsKey(key)){
            Node node = map.get(key);
            deleteNode(node);
        }
        if(count >= capacity){
            //淘汰末尾
            deleteTail();
        }
        addHead(new Node(key, value));
    }

    private void addHead(Node node){
        Node next = head.next;
        head.next = node;
        node.pre = head;
        node.next = next;
        next.pre = node;
        map.put(node.key, node);
        count++;
    }

    private void deleteNode(Node node){
        Node pre = node.pre, next = node.next;
        //删除
        pre.next = next;
        next.pre = pre;
        node.pre = null;
        node.next = null;
        map.remove(node.key);
        count--;
    }

    private void deleteTail(){
        deleteNode(tail.pre);
    }
}

class Node{
    Node pre;
    Node next;
    int key;
    int value;
    public Node(){

    }

    public Node(int key,int value){
        this.key = key;
        this.value = value;
    }
}

4. 三数之和

思路:排序,三指针,去重

代码实现:

class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        Arrays.sort(nums);
        List<List<Integer>> res = new ArrayList<>();
        int len = nums.length;
        for(int i = 0; i < len; i++){
            //排序过的,只要大于0后面怎么加都是大于0
            if(nums[i] > 0) break;
            //去重
            while(i - 1 >= 0 && i < len && nums[i - 1] == nums[i]) i++;
            int left = i + 1, right = len - 1;
            while(left < right){
                if(nums[i] + nums[left] + nums[right] < 0) left++;
                else if(nums[i] + nums[left] + nums[right] > 0) right--;
                else {
                    res.add(Arrays.asList(nums[i],nums[left],nums[right]));
                    //去重
                    while(left < right && nums[left] == nums[left + 1]){
                        left++;
                    }
                    while(left < right && nums[right] == nums[right - 1]){
                        right--;
                    }
                    left++;
                    right--;
                }
                
            }
        }
        return res;
    }
}

5. 排序数组(后续补上其他算法)

快速排序做法(要求手撕):

思路:确定一个基准值,声明双指针,把一个区间划分为两个区间,左边都小于基准值,右边大于等于基准值,然后分而治之。

代码实现:

class Solution {
    public int[] sortArray(int[] nums) {
        quickSort(nums, 0, nums.length - 1);
        return nums;
    }

    private void quickSort(int[] nums, int left, int right){
        if(left < right){
            int i = left, j = right, x = nums[left];
            while(i < j){
                while(i < j && nums[j] >= x){
                    j--;
                }
                nums[i] = nums[j];
                while(i < j && nums[i] < x){
                    i++;
                }
                nums[j] = nums[i];
            }
            nums[i] = x;
            quickSort(nums, left, i - 1);
            quickSort(nums, i + 1, right);
        }
    }
}

6. 数组中的第K个最大元素

思路1,快排。进行正序的快速排序,然后取倒数第k个,时间复杂度O(nlogn),空间复杂度是调用栈的高度,即O(logn)
思路2,优先队列。因为要取最大的,所以建立一个大小为k的小根堆,遍历整个数组,当堆没满时,将元素入堆;当遇到比堆顶大的数(堆顶也就是此时小根堆里最小的数),将堆顶退出堆,然后将这个数入堆中,形成新的小根堆。最后,遍历完数组时,小根堆中的元素都是最大的k个,而堆顶(因为是小根堆,所以堆顶是堆里最小的)就是要找的第k大的元素。
代码实现:

class Solution {
    public int findKthLargest(int[] nums, int k) {
        PriorityQueue<Integer> minHeap = new PriorityQueue<>(k, Comparator.comparingInt(a -> a));
        for(int i = 0; i < k; i++){
            minHeap.offer(nums[i]);
        }
        for(int i = k; i < nums.length; i++){
            //堆顶
            int peek = minHeap.peek();
            if(nums[i] > peek){
                //退出堆顶,nums[i]入堆
                minHeap.poll();
                minHeap.offer(nums[i]);
            }
        }
        return minHeap.peek();
    }
}