【剑指Offer速刷与技巧笔记】剑指Offer重点&面试刷题

刷题过程中对于一系列技巧知晓了,但是拿过来题目 却不知道用什么对了……
或者说 对应的题目看过、刷过、实现过 却……具体细节又不会了……(特别是 魔鬼细节二分搜索哇!)
再者说 有些题目的“奇技淫巧”不是那么容易在短时间内想到的,所以想到了要针对面试厂家进行速刷……并相应的梳理刷题过程中 自己知识框架/思维 中短缺、不熟练的部分再进行二刷、三刷……
另外很重要的一点: 对刷过的题目 要尝试进行分类、可以快速索引到……

以上就是本篇的核心思想和目前刷题中遇到的问题。针对加粗重点部分进行重点突破……

更新:剑指Offer需要需要进一步由重点到细节——逐渐刷完!

零、引言

当遇到一个比较复杂的问题时,可以通过画图、举例或者分解来考虑,从具体的实例中总结普遍规律。

注重效率的考量,这66道题用蛮力法解决可能都不是很难,但是我们需要考虑的是时间效率和空间效率的平衡,以空间换时间有时候是一个不错的选择。

常用数据结构和数据操作是基础,要重点掌握,如树的遍历,排序,查找,递归等操作,在题目中反复用到,要深刻理解算法思想。

算法题主要分成数据结构和具体算法部分,简单归类如下。

零.1 数据结构类题目

LinkedList
003-从尾到头打印链表
014-链表中倒数第k个结点
015-反转链表
016-合并两个或k个有序链表
025-复杂链表的复制
036-两个链表的第一个公共结点
055-链表中环的入口结点
056-删除链表中重复的结点
Tree
004-重建二叉树
017-树的子结构
018-二叉树的镜像
022-从上往下打印二叉树
023-二叉搜索树的后序遍历序列
024-二叉树中和为某一值的路径
026-二叉搜索树与双向链表
038-二叉树的深度
039-平衡二叉树
057-二叉树的下一个结点
058-对称的二叉树
059-按之字形顺序打印二叉树
060-把二叉树打印成多行
061-序列化二叉树
062-二叉搜索树的第k个结点
Stack & Queue
005-用两个栈实现队列
020-包含min函数的栈
021-栈的压入、弹出序列
044-翻转单词顺序列(栈)
064-滑动窗口的最大值(双端队列)
Heap
029-最小的K个数
Hash Table
034-第一个只出现一次的字符

065-矩阵中的路径(BFS)
066-机器人的运动范围(DFS)

零.2 具体算法类题目

斐波那契数列
007-斐波拉契数列
008-跳台阶
009-变态跳台阶
010-矩形覆盖
搜索算法
001-二维数组查找
006-旋转数组的最小数字(二分查找)
037-数字在排序数组中出现的次数(二分查找)
全排列
027-字符串的排列
动态规划
030-连续子数组的最大和
052-正则表达式匹配(我用的暴力)
回溯
065-矩阵中的路径(BFS)
066-机器人的运动范围(DFS)
排序
035-数组中的逆序对(归并排序)
029-最小的K个数(堆排序)
029-最小的K个数(快速排序)
位运算
011-二进制中1的个数
012-数值的整数次方
040-数组中只出现一次的数字
其他算法
002-替换空格
013-调整数组顺序使奇数位于偶数前面
028-数组中出现次数超过一半的数字
031-整数中1出现的次数(从1到n整数中1出现的次数)
032-把数组排成最小的数
033-丑数
041-和为S的连续正数序列(滑动窗口思想)
042-和为S的两个数字(双指针思想)
043-左旋转字符串(矩阵翻转)
046-孩子们的游戏-圆圈中最后剩下的数(约瑟夫环)
051-构建乘积数组

零.3 节奏与方法

我个人觉得数据结构和DP在面试中手写代码的几率比较高,因此笔者目前的刷题节奏主要是:
剑指offer->Leetcode动态规划->面试前再过一遍剑指offer
有个重要的点是:每道题做完一定要去讨论区!

讨论区有非常精简的大神级代码,你好不容易AC了一道题准备去讨论区吹(装)水(逼),点开一看,“握草,还可以这样”。

思考为什么他可以写出这么好的代码,把每道题的思路理解后用笔记本记录下来,争取刷到融会贯通,即看见有个题能自动归类到某个方面,这样有一定好处。面试最重要的是让面试官日后能愿意与你以后一起工作,因此沟通交流非常重要。比如有时候面试需要交流,看着像是一道排序的题做不出来,就可以跟面试官交流:“我有几个不成熟的想法,一排序,二动态规划,三是直接搜索算法”,面试官可能就给个提示:“你先用排序试试吧“。

一、ZJ算法TOP题目

1.1 重排链表[143]

nb的,考察基础的 快慢指针、分割游离指针、旋转链表、指针引用……
【剑指Offer速刷与技巧笔记】剑指Offer重点&面试刷题_第1张图片

我的题解:


#define For(x,y,z) for(int x=y; x

struct ListNode {
    int val;
    ListNode *next;
    ListNode() : val(0), next(nullptr) {}
    ListNode(int x) : val(x), next(nullptr) {}
    ListNode(int x, ListNode *next) : val(x), next(next) {}
};
class Solution {
public:
    /**
     * @Description: 首先快慢指针 找中点。 
     * @param {*}
     * @return {*}
     * @notes: 一前一后往后插
     */
    void reorderList(ListNode* head) {
        ListNode *slow=head, *fast=head;
        while(fast->next!=nullptr && fast->next->next!=nullptr){
            fast = fast->next->next;
            slow = slow->next;
        }
        ListNode *l1=head, *l2=slow->next;
        slow->next = nullptr;  // 这个指针游离了! 就会错误!!!!
       
        insertTwoReveList(l1, l2);
    }
    // helper 后序递归插入
    void insertTwoReveList(ListNode* &l1, ListNode *l2){
        if(l2 == nullptr){
            return ;
        }
        insertTwoReveList(l1, l2->next);
        ListNode *tmp1 = l1;
        l1->next = l2;
        l2->next = tmp1;
        l1 = tmp1;
    }
};

1.2 旋转图像[48]

【剑指Offer速刷与技巧笔记】剑指Offer重点&面试刷题_第2张图片
我的题解


#define For(x,y,z) for(int x=y; x
class Solution {
public:
    /**
     * @Description: 技巧:先 上下翻转,然后再对角翻转即可
     * @param {*}
     * @return {*}
     * @notes: 
     */
    void rotate(vector<vector<int>>& matrix) {
        int m = matrix.size(), n = matrix[0].size();
        For(i,0,m/2){
            matrix[i].swap(matrix[m-i-1]);
        }
        // 对角元素 互换
        For(i, 0, m){
            For(j, i+1, n){
                swap(matrix[i][j], matrix[j][i]);
            }
        }
    }
};

1.3 长度最小子数组[209]

【剑指Offer速刷与技巧笔记】剑指Offer重点&面试刷题_第3张图片
题解——滑动窗口


#define For(x,y,z) for(ll x = y; x < z;++x)
typedef long long ll;

class Solution {
public:
    /**
     * @Description: 大于目标值 最小子数组和
     * @param {int} target
     * @return {*}
     * @notes: 
     */
    int minSubArrayLen(int target, vector<int>& nums) {
        int n = nums.size();
        int left=0, right=0;
        int minArr = INT_MAX;
        int sum=0;
        while(right<n){
            sum+=nums[right];
            right++;

            while( target >= sum ){
                // 查看是否最小
                int len = right-left + 1;
                minArr = minArr>len?len:minArr;

                // shrink windows
                sum -= nums[left];
                left++;
            }
        }
        return minArr;
    }
};

1.4 除自身以外数组乘积[238]

【剑指Offer速刷与技巧笔记】剑指Offer重点&面试刷题_第4张图片
解题思路
因为空间复杂度要求 O(1),不能使用 除法,因此一定需要在 乘法 过程中得到所有答案;
由于输出数组不算在空间复杂度内,那么我们可以将 L 或 R 数组用输出数组来计算。先把输出数组当作 L 数组来计算,然后再动态构造 R 数组得到结果。 让我们来看看基于这个思想的算法。

  • 初始化 res 数组,对于给定索引 i,res[i] 代表的是 i 左侧所有数字的乘积。
  • 构造方式与之前相同,只是我们试图节省空间,先把 res 作为方法一的 L 数组。
  • 这种方法的唯一变化就是我们没有构造 R 数组。而是用一个遍历来跟踪右边元素的乘积。并更新数组 res[i]=res[i]∗R。然后 R 更新为 R=R∗nums[i],其中变量 R表示的就是索引右侧数字的乘积。

【剑指Offer速刷与技巧笔记】剑指Offer重点&面试刷题_第5张图片

我的题解:

#define For(x,y,z) for(ll x = y; x < z;++x)
typedef long long ll;

class Solution {
public:
    /**
     * @Description: 关键是 不能用除法+还要原地或者用res 进行O(1)的算法题解。
     * @param {*}
     * @return {*}
     * @notes: 使用res 计算元素左侧的乘积; 再进行一次从右到左 计算元素右边的乘积,对应乘res 即可得到结果。
     */
    vector<int> productExceptSelf(vector<int>& nums) {
        int n = nums.size();
        vector<int> res(n, 1); // 首先代表 i左侧的元素乘积
        For(i, 1, n){
            res[i] = res[i-1]*nums[i-1];
        }
        // 右边有个tmp
        int tmp = 1;  //动态右边的累乘 除去自身
        for(int j = n-2; j >= 0;j--){
            tmp *= nums[j+1];
            res[j] *= tmp;
        }
        return res;
    }  
};

1.5 乘积最大子数组[152]

【剑指Offer速刷与技巧笔记】剑指Offer重点&面试刷题_第6张图片

我的题解
min_num 记录以 nums[i-1] 结尾的乘积最小值,max_num 记录以 nums[i-1] 结尾的乘积最大值。


#define For(x,y,z) for(ll x = y; x < z;++x)
typedef long long ll;


class Solution {
public:
    /**
     * @Description: 最大乘积 有负数,所以可以记录最大和最小值。
     * @param {*}
     * @return {*}
     * @notes: 
     */
    int maxProduct(vector<int>& nums) {
        int min_num = 1, max_num = 1;  // 代表 以 nums[i-1]为结尾的最小和最大的值
        int maxNow = INT_MIN;
        For(i, 0, nums.size()){
            if(nums[i] < 0) {
                // 最大最小互换
                int tmp = min_num;
                min_num = max_num;
                max_num = tmp;
            }
            
            min_num = min( min_num*nums[i], nums[i]);
            max_num = max( max_num*nums[i], nums[i]);  // 要么自己开始,要么和之前一样开始。

            maxNow = max_num>maxNow?max_num:maxNow;
        }
        return maxNow;
    }
};


你可能感兴趣的:(刷题,思维总结,算法,面试,剑指Offer)