剑指offer刷题心得

刷题套路总结


很多简单题,看似简单实则不简单,因为面试官会让你多种解法、做出时间复杂度最低的算法。
因此刷题过程一定要记录时间复杂度和空间复杂度!!!

数组专题

39.数组中次数超过数组个数一半的数字

分析:
最直接的想法,枚举数组中元素,再遍历数组,计算其枚举元素的个数,时间复杂度是O(n^2)。很明显在面试中不会这么考察。
1如何保存数组中的数字以及出现的个数呢?很容易想到哈希表k-v方法
2在数组中个数超过一半的数字,那么其出现位置必有一个在数组下标为 n/2 位置 (这里是向下取整),为什么?本质上是数学推导,这里就记住吧!
3摩尔投票法,这个算法目前不想学习,感觉普适性不高,后期有遇到再学习,先来记录一下

哈希表法:
要时刻掌握哈希表的常用方法,get(key)、put(key,value)、containsKey(key)、
entrySet()、keySet()、getKey()、getValue()
注:keySet()方法返回值是Map中key值的集合;entrySet()的返回值也是返回一个Set集合,此集合的类型为Map.Entry,Map是java中的接口,Map.Entry、HashMap是Map的一个内部接口。
所以我们遍历map的entry类型数据,使用entrySet(),并且返回的数据类型使用Map.Entry

时间复杂度:O(n),遍历数组
空间复杂度:O(n),构建了一个哈希表

class Solution {

    // 哈希表记录k-v
    public HashMap<Integer,Integer> counts(int[] nums){
        HashMap<Integer,Integer> res = new HashMap<>();
        for(Integer num:nums){
            // 类似打擂台,相同key就+1
            if(res.containsKey(num)){
                res.put(num,res.get(num) + 1);
            }else{
                res.put(num,1);
            }
        }
        return res;
    } 

    public int majorityElement(int[] nums) {
        int len = nums.length;
        // 如何记录多种元素及其个数?使用哈希表
        HashMap<Integer,Integer> map = counts(nums);
        // 遍历map中的数据
        Map.Entry<Integer,Integer> majorityEntry = null;
        for(Map.Entry<Integer,Integer> temp:map.entrySet()){
            if(majorityEntry == null || temp.getValue() > majorityEntry.getValue()){
                majorityEntry = temp;
            }
        }
        return majorityEntry.getKey();

    }
}

排序法:
使用java内置的Arrays.sort(nums),默认使用的是快速排序算法。
时间复杂度:O(nlogn),这是数组排序的复杂度
空间复杂度:O(n)

03. 数组中重复的数字
哈希表具有搜索功能。
时间复杂度:O(n)
空间复杂度:O(n)

class Solution {
    public int findRepeatNumber(int[] nums) {
        /*
        这道题题很容易想到使用哈希表来做,通过比较值是否存在于哈希表里
        这里我其实惯性思维使用了k-v方法,实际上使用HashSet即可
        */
        Map<Integer,Integer> map = new HashMap<>();
        int len = nums.length;
        for(int i = 0; i < len; i++){
            if(map.containsKey(nums[i])){
                return nums[i];
            }
            map.put(nums[i],i);
        }
        return -1;

    }
}

04. 二维数组中的查找

class Solution {
    public boolean findNumberIn2DArray(int[][] matrix, int target) {
        /**
        这道题很有意思,三个方面来考虑这个问题。
        1.直接暴力搜索,双重for循环来遍历寻找target,时间复杂度O(m*n),肯定不可取
        2.优化暴力搜索(一维数组的二分法),每一行的数据都是有序的,每一行可以进行二分法遍历o(mlogn)
        3.线性搜索,将整个二维数组逆时针旋转45度,会发现和一个二查搜索树相似,左边小右边大,最后遍历o(m+n)
         */
         // 优化暴力搜索
        //  int m = matrix.length;
        //  for(int i = 0; i < m; i++){
        //      int left = 0;
        //      int right = matrix[i].length - 1;
        //      while(left <= right){
        //          int mid = (left + right) / 2;
        //          if(target == matrix[i][mid]){
        //              return true;
        //          }
        //          else if(target > matrix[i][mid]){
        //              left = mid + 1;
        //          }else{
        //              right = mid - 1;
        //          }
        //      }
        //  }
        //  return false;

        // 线性查找
        // 特判!
        if(matrix == null || matrix.length == 0 || matrix[0].length == 0){
            return false;
        }
        int row = 0;
        int col = matrix[0].length - 1;
        while( row < matrix.length && col >= 0){
            if(target == matrix[row][col]){
                return true;
            }
            else if(target > matrix[row][col]){
                row++;
            }else{
                col--;
            }
        }
        return false;

    }
}

11. 旋转数组的最小数字

class Solution {
    public int minArray(int[] numbers) {
        // 这道题居然是一道简单题,难以置信!!!
        // 排序数组问题优先想到二分法
        // 输出最小值问题实际上就是在寻找旋转点
        // 时间复杂度o(logn)
        int len = numbers.length;
        int left = 0;
        int right = len - 1;
        // 为什么对number[right]比较而不是对left? 因为right必定是右排序数组,而left却无法确定
        while(left < right){
            int mid = (left + right) / 2;
            if(numbers[mid] > numbers[right]){
                // 说明旋转点在右侧
                left = mid + 1;
            }else if(numbers[mid] < numbers[right]){
                // 说明旋转点在左侧
                right = mid;
            }else{
                // 两者相等情况,无法判断旋转点在那一侧,那么就缩小right判断
                // 这个需要数学证明~~~
                right--;
            }
        }
        // 返回旋转点
        return numbers[left];
    }
}

35. 复杂链表的复制

/*
// Definition for a Node.
class Node {
    int val;
    Node next;
    Node random;

    public Node(int val) {
        this.val = val;
        this.next = null;
        this.random = null;
    }
}
*/
class Solution {
    public Node copyRandomList(Node head) {

        // 使用哈希表:构建原链表和新链表之间的联系
        // 特判
        if(head == null){
            return null;
        }
        HashMap<Node,Node> map = new HashMap<>();
        // 建立遍历指针
        Node cur = head;
        while(cur != null){
            Node node = new Node(cur.val);
            // 储存原节点 和 新节点
            map.put(cur,node);
            // 遍历指针下移
            cur = cur.next;
        }
        // 已经构造好新链表节点,开始建立next和random引用指向
        cur = head;
        while(cur != null){
            // next引用指向:新节点指向新节点
            map.get(cur).next = map.get(cur.next);
            // random引用指向
            map.get(cur).random = map.get(cur.random);
            // cur指针下移
            cur = cur.next;
        }
        // 返回新链表的head节点
        return map.get(head);
        
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值