常见面试笔试算法总结

本人博客
https://www.liyunxu.work/

二分专题

sort函数的使用

包含在算法头文件中(#include )

  1. 对于内置数据类型 可以直接排序 比如
int num[100];
sort(num, num + 100);
  1. 对于自定义数据类型 需要提供比较的方法 比如
struct node {
    int x, y;
};

bool cmp(const node &a, const node &b) {
    return a.x > b.x;
}

int main() {
    node a[100];
    sort(a, a + 100, cmp)
    return 0;
}

二分答案

如果范围过大(超过int)在计算中间值的时候可以用l + (r - l ) / 2

对应OJ题目(#387,#390,#389,#393,#82,#391,#394,#392)

二分的两种特殊情况

  1. 前面一堆1后面一堆0(111111111000000000) 我们找最后一个1

C++版

while (l != r)
mid  = (l + r + 1) / 2;
l = mid;
r = mid - 1;

Java版

        while (left != right) {
            mid = (right + left + 1) >> 1;
            if (nums[mid] <= target) {
                left = mid;
            } else {
                right = mid - 1;
            }
        }
  1. 前面一堆0后面一堆1(000000001111111111)我们找第一个1

C++版

while (l != r) 
mid = (l + r) / 2;
l = mid + 1;
r = mid;

Java版

  while (left != right) {
            mid = left + ((right - left) >> 1);
            if (nums[mid] >= target) {
                right = mid;
            } else {
                left = mid + 1;
            }
  }

双指针

LeetCode977题<有序数组的平方>
因为给定数组是升序排列的,但是有负数,负数的平方会使其变大。所以不能直接平方返回。
用一根指针i只想数组头,一根指针j指向数组尾,然后比较nums[i]和nums[j]平方后的结果,逆序存储两者较大的值。

class Solution {
    public int[] sortedSquares(int[] nums) {
        int n = nums.length;
        int ans[] = new int[n];
        for (int i = 0, j = n - 1, pos = n - 1; i <= j; pos--) {
            int ii = nums[i] * nums[i];
            int jj = nums[j] * nums[j];
            if (ii > jj) {
                ans[pos] = ii;
                i++;
            } else {
                ans[pos] = jj;
                j--;
            }
        }
        return ans;
    }
}

快慢指针

LeetCode19. 删除链表的倒数第 N 个结点
给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
题解:

由于我们需要找到倒数第 n 个节点,因此我们可以使用两个指针 first 和 second 同时对链表进行遍历,并且 first 比 second 超前 n 个节点。当 first 遍历到链表的末尾时,second 就恰好处于倒数第 n 个节点。
如果我们能够得到的是倒数第 n 个节点的前驱节点而不是倒数第 n 个节点的话,删除操作会更加方便。因此我们可以考虑在初始时将 second 指向哑节点,其余的操作步骤不变。这样一来,当 first 遍历到链表的末尾时,second 的下一个节点就是我们需要删除的节点。
作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/remove-nth-node-from-end-of-list/solution/shan-chu-lian-biao-de-dao-shu-di-nge-jie-dian-b-61/
来源:力扣(LeetCode)

示例1:

输入:head = [1,2,3,4,5], n = 2
输出:[1,2,3,5]

示例2:

输入:head = [1], n = 1
输出:[]

示例3:

输入:head = [1,2], n = 1
输出:[1]
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        ListNode dummy = new ListNode(0, head);
        ListNode first = head;
        ListNode second = dummy;
        for (int i = 0; i < n; ++i) {
            first = first.next;
        }
        while (first != null) {
            first = first.next;
            second = second.next;
        }
        second.next = second.next.next;
        ListNode ans = dummy.next;
        return ans;
    }
}

STL容器

string

头文件:#include <string>

方法功能返回值
empty检查字符串是否为空如果为空返回true
size/length返回字符数字符数
clear清除内容
insert插入字符指向首个被插入字符的迭代器
erase移除指定字符指向擦除的后一个字符的迭代器,若不存在则返回end()
substr返回从pos到pos+count的字符串字符串
copy拷贝字符串拷贝的字符个数
swap交换字符串
find寻找首个等于给定字符串的子串找到的子串的首字符位置,或若找不到这种子串则为 npos

vector

头文件#include <vector>

方法功能返回值
empty检查容器是否为空为空返回true
size返回元素个数元素个数
clear清除内容
erase删除指定位置的元素返回移除的后一个元素的迭代器
push_back向末尾添加元素
pop_back移除末尾元素

stack

头文件#include <stact>
封装于双端队列

方法功能返回值
top访问栈顶元素栈顶元素
empty检查容器是否为空为空返回true
size返回元素个数元素个数
push向栈顶插入元素
pop删除栈顶元素

queue

头文件#include <queue>
封装于双端队列

方法功能返回值
front访问第一个元素到首元素的引用
back访问最后一个元素到末元素的引用
empty检查容器是否为空为空返回true
size返回元素个数元素个数
push向队列尾部插入元素
pop删除首个元素

deque

头文件#include <deque>

方法功能返回值
front访问第一个元素到首元素的引用
back访问最后一个元素到末元素的引用
empty检查容器是否为空为空返回true
size返回元素个数元素个数
clear清除内容
push_back将元素添加到容器末尾
pop_back移除末尾元素
push_front插入元素到容器起始
pop_front移除首元素

priority_queue

头文件#include <queue>
默认是大顶堆,首个元素是最大的

方法功能返回值
top访问栈顶元素到顶元素的引用
empty检查容器是否为空为空返回true
size返回元素个数元素个数
push插入元素,并对底层容器排序
pop删除队首元素

set

头文件#include <set>

方法功能返回值
empty检查容器是否为空为空返回true
size返回元素个数元素个数
clear清除内容
insert插入元素或结点返回由指向被插入元素
count返回匹配特定键的元素数量拥有比较等价于 key 或 x 的关键的元素数1或0

map

头文件#include <map>
重载了方括号运算符

方法功能返回值
empty检查容器是否为空为空返回true
size返回元素个数元素个数
clear清除内容
insert插入元素或结点返回由指向被插入元素
count返回匹配特定键的元素数量拥有比较等价于 key 或 x 的关键的元素数1或0

multiset

头文件#include <set>
不会去重可以有多个key,使用方法和set基本一致

multimap

头文件#include <map>
不会去重可以有多个key,使用方法和map基本一致,但是不可以使用方括号

unordered_set

头文件#include <unordered_set>
可以去重但是顺序是随机的

unordered_map

头文件#include <unordered_map>
可以去重但是顺序是随机的

unordered_multiset

头文件#include <unordered_set>
不去重顺序也是随机的

unordered_multimap

头文件#include <unordered_map>
不去重顺序也是是随机的


滑动窗口法

定长的滑动窗口法

eg : leetcode #143
给定 n 个整数,找出平均数最大且长度为 k 的连续子数组,并输出该最大平均数。
每次进来一个新的我们就把第一个删除

ass Solution {
public:
    double findMaxAverage(vector<int>& nums, int k) {
        double ans = INT_MIN, sum = 0;
        for (int i = 0; i < nums.size(); i++) {
            sum += nums[i];
            if (i - k >= 0) {
                sum -= nums[i - k];
            }
            if (i + 1 >= k) {
                ans = max(ans, sum / k);
            }
        }
        return ans;
    }
};

树状数组

我们先来看前缀和数组
如果有一个数组a[i]那么他的前缀和数组s[i]就是对原数组的前i项求和,前缀和数组主要是是用来解决区间求和问题的,那么他的初始化时间复杂度是O(n),计算区间和的时间复杂度O(1),对原数组单点修改的时间复杂度为O(n)

那么如何解决单点修改慢的问题呢?

树状数组

要看树状数组我们先看lowbit()函数,功能就是一个整数的二进制的最低位的1对应十进制的值就是lowbit()函数的结果
那么树状数组c[i]就是前lowbit(i)项的和

例如 lowbit(10) = 2, C[10] = a[10]+a[9]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ymo03C10-1661763264440)(https://myblog2-0.oss-cn-beijing.aliyuncs.com/2022-08-29/3412f264-6e44-4794-8033-7a9df8b3a2d7_截图_选择区域_20210716193651.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Cmw8ePUa-1661763264441)(https://myblog2-0.oss-cn-beijing.aliyuncs.com/2022-08-29/bfe9b449-8637-48b1-898f-629ab709ee29_截图_选择区域_20210716193957.png)]
我们对比两张图可以看出树状数组假如我们修改了a[1]的值那么我们修改的效率就会提高

基本操作

  1. 前缀和查询
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-p2mWE8O4-1661763264442)(https://myblog2-0.oss-cn-beijing.aliyuncs.com/2022-08-29/b1aa39e6-a8dd-46a4-883d-2f96f4f8a9e2_截图_选择区域_20210716194545.png)]

s[i]可以拆解为几项c[i]取决与i的二进制1的个数 那么时间复杂度就是log(i)的

  1. 单点修改
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bknm4zmE-1661763264442)(https://myblog2-0.oss-cn-beijing.aliyuncs.com/2022-08-29/2bc1f519-a10d-477f-8faa-cde7c2033d78_截图_选择区域_20210716195919.png)]

那么如何取得c[5],c[6],c[8]呢?
我们只需要5 + lowbit(5) = 6. 6 + lowbit(6) = 8 就可以取得。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SjD0pa0P-1661763264443)(https://myblog2-0.oss-cn-beijing.aliyuncs.com/2022-08-29/cca98777-20a6-40b8-8358-e75e35942721_截图_选择区域_20210716200136.png)]

递推问题

LeetCode53最大子序和
给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
示例

输入:nums = [-2,1,-3,4,-1,2,1,-5,4]
输出:6
解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。
class Solution {
    public int maxSubArray(int[] nums) {
        int ans = nums[0];
        int sum = nums[0];
        for (int i = 1; i < nums.length; i++) {
            sum = Integer.max(nums[i], nums[i] + sum);
            ans = Integer.max(ans, sum);
        }
        return ans;
    }
}

解释:sum看成是一个到第i项为止最大的连续子序列和数组,因为只需要第i项的前一项,所以就用一个sum变量维护就可以,sum更新有两种情况

  1. 要么是本身nums[i]自己大。
  2. 要么是nums[i] + sum大也就是加上到前一项为止的最大连续子序列和。

之后更新ans。

数组判断重复值或着快速查值问题

LeetCode 36. 有效的数独
判断一个 9x9 的数独是否有效。只需要 根据以下规则 ,验证已经填入的数字是否有效即可。

数字 1-9 在每一行只能出现一次。
数字 1-9 在每一列只能出现一次。
数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。
数独部分空格内已填入了数字,空白格用 ‘.’ 表示。

示例1:

  输入:board = 
[["5","3",".",".","7",".",".",".","."]
,["6",".",".","1","9","5",".",".","."]
,[".","9","8",".",".",".",".","6","."]
,["8",".",".",".","6",".",".",".","3"]
,["4",".",".","8",".","3",".",".","1"]
,["7",".",".",".","2",".",".",".","6"]
,[".","6",".",".",".",".","2","8","."]
,[".",".",".","4","1","9",".",".","5"]
,[".",".",".",".","8",".",".","7","9"]]
输出:true

题目让我们找行不能重复,列不能重复,每一个33的表格不能重复,所以可以建立3个哈希表(row, columns, boxIndex),分别存储行,列,和每一个33的块区域。kay是出现的数字,value则是对应出现数字的次数。

class Solution {
    public boolean isValidSudoku(char[][] board) {
        HashMap<Character, Integer>[] row = new HashMap[9];
        HashMap<Character, Integer>[] columns = new HashMap[9];
        HashMap<Character, Integer>[] boxIndex = new HashMap[9];

        for (int i = 0; i < 9; i++) {
            row[i] = new HashMap<Character, Integer>();
            columns[i] = new HashMap<Character, Integer>();
            boxIndex[i] = new HashMap<Character, Integer>();
        }
        int count = 0;
        for (int i = 0; i < 9; i++) {
            for (int j = 0; j < 9; j++) {
                if (Character.compare(board[i][j], '.') == 0) continue;
                count = row[i].getOrDefault(board[i][j], 0) + 1;
                row[i].put(board[i][j], count);

                count = columns[j].getOrDefault(board[i][j], 0) + 1;
                columns[j].put(board[i][j], count);

                count = boxIndex[(i / 3) * 3 + j / 3].getOrDefault(board[i][j], 0) + 1;
                boxIndex[(i / 3) * 3 + j / 3].put(board[i][j], count);

                if (row[i].get(board[i][j]) > 1 || columns[j].get(board[i][j]) > 1 || boxIndex[(i / 3) * 3 + j / 3].get(board[i][j]) > 1) {
                    return false;
                }
            }
        }
        return true;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

时莫如初

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值