秋招接近尾声,我总结了 牛客
、WanAndroid
上,有关笔试面经的帖子中出现的算法题,结合往年考题写了这一系列文章,全部文章均与 LeetCode 进行核对、测试。欢迎食用css
本文将覆盖 「二进制」 + 「位运算」 和 Lru 方面的面试算法题,文中我将给出:java
解析
GitHub
创建了一个仓库仓库地址:超级干货!精心概括视频、归类、总结
,各位路过的老铁支持一下!给个 Star !
android
如今就让咱们开始吧!git
给定一个包含 m x n
个要素的矩阵,(m
行, n
列),按照螺旋顺序,返回该矩阵中的全部要素。github
示例 :面试
输入:
[
[1, 2, 3, 4],
[5, 6, 7, 8],
[9,10,11,12]
]
输出: [1,2,3,4,8,12,11,10,9,5,6,7]
复制代码
第 1 层
,次外层元素都是第 2 层
,而后是第 3 层
的。[[1, 1, 1, 1, 1, 1, 1],
[1, 2, 2, 2, 2, 2, 1],
[1, 2, 3, 3, 3, 2, 1],
[1, 2, 2, 2, 2, 2, 1],
[1, 1, 1, 1, 1, 1, 1]]
复制代码
(r1, c)
,按照 c = c1,...,c2
的顺序。(r, c2)
,按照 r = r1+1,...,r2
的顺序。r1 < r2
而且 c1 < c2
),咱们如下图所示的方式遍历下方的元素和左侧的元素。public List<Integer> spiralOrder(int[][] matrix) {
ArrayList<Integer> rst = new ArrayList<Integer>();
if(matrix == null || matrix.length == 0) {
return rst;
}
int rows = matrix.length;
int cols = matrix[0].length;
int count = 0;
while(count * 2 < rows && count * 2 < cols){
for (int i = count; i < cols - count; i++) {
rst.add(matrix[count][i]);
}
for (int i = count + 1; i < rows - count; i++) {
rst.add(matrix[i][cols - count - 1]);
}
if (rows - 2 * count == 1 || cols - 2 * count == 1) { // 若是只剩1行或1列
break;
}
for (int i = cols - count - 2; i >= count; i--) {
rst.add(matrix[rows - count - 1][i]);
}
for (int i = rows - count - 2; i >= count + 1; i--) {
rst.add(matrix[i][count]);
}
count++;
}
return rst;
}
复制代码
请断定一个数独
是否有效。该数独可能只填充了部分数字,其中缺乏的数字用 . 表示。算法
维护一个HashSet
用来记同一行
、同一列
、同一九宫格
是否存在相同数字数据库
示例 :编程
输入:
[
["8","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"]
]
输出: false
解释: 除了第一行的第一个数字从 5 改成 8 之外,空格内其余数字均与 示例1 相同。
但因为位于左上角的 3x3 宫内有两个 8 存在, 所以这个数独是无效的。
复制代码
不必定
是可解的。有效便可
。1-9
和字符 '.'
。9x9
形式的。`一次迭代缓存
首先,让咱们来讨论下面两个问题:
如何枚举子数独? 可使用 box_index = (row / 3) * 3 + columns / 3
,其中 / 是整数除法。
如何确保行 / 列 / 子数独中没有重复项? 能够利用 value -> count
哈希映射来跟踪全部已经遇到的值。
如今,咱们完成了这个算法的全部准备工做:
false
。true
。public boolean isValidSudoku(char[][] board) {
Set seen = new HashSet();
for (int i=0; i<9; ++i) {
for (int j=0; j<9; ++j) {
char number = board[i][j];
if (number != '.')
if (!seen.add(number + " in row " + i) ||
!seen.add(number + " in column " + j) ||
!seen.add(number + " in block " + i / 3 + "-" + j / 3))
return false;
}
}
return true;
}
复制代码
给定一个N×N
的二维矩阵表示图像,90度
顺时针旋转图像。
示例 :
输入: [[1,1,0,0],[1,0,0,1],[0,1,1,1],[1,0,1,0]]
输出: [[1,1,0,0],[0,1,1,0],[0,0,0,1],[1,0,1,0]]
解释: 首先翻转每一行: [[0,0,1,1],[1,0,0,1],[1,1,1,0],[0,1,0,1]];
而后反转图片: [[1,1,0,0],[0,1,1,0],[0,0,0,1],[1,0,1,0]]
复制代码
1 <= A.length = A[0].length <= 20
0 <= A[i][j] <= 1
复制代码
旋转这些矩形
的问题。移动
它们。public void rotate(int[][] matrix) {
if (matrix == null || matrix.length == 0 || matrix[0].length == 0) {
return;
}
int length = matrix.length;
for (int i = 0; i < length / 2; i++) {
for (int j = 0; j < (length + 1) / 2; j++){
int tmp = matrix[i][j];
matrix[i][j] = matrix[length - j - 1][i];
matrix[length -j - 1][i] = matrix[length - i - 1][length - j - 1];
matrix[length - i - 1][length - j - 1] = matrix[j][length - i - 1];
matrix[j][length - i - 1] = tmp;
}
}
}
复制代码
优势:
特定状况下,计算方便,速度快,被支持面广
若是用算数方法,速度慢,逻辑复杂
位运算不限于一种语言,它是计算机的基本运算方法
复制代码
2 * n + 1
个数字,除其中一个数字以外其余每一个数字均出现两次,找到这个数字。异或运算具备很好的性质,相同数字异或运算后为0,而且具备交换律和结合律,故将全部数字异或运算后便可获得只出现一次的数字。
示例 :
输入: [4,1,2,1,2]
输出: 4
复制代码
若是咱们对 0 和二进制位作 XOR 运算,获得的仍然是这个二进制位
若是咱们对相同的二进制位作 XOR 运算,返回的结果是 0
XOR 知足交换律和结合律
因此咱们只须要将全部
的数进行 XOR 操做,获得那个惟一的数字。
public int singleNumber(int[] A) {
if(A == null || A.length == 0) {
return -1;
}
int rst = 0;
for (int i = 0; i < A.length; i++) {
rst ^= A[i];
}
return rst;
}
复制代码
O(n)
。咱们只须要将 O(1)
。非负整数 n
,表示该代码中全部二进制的总数,请找出其格雷编码顺序。一个格雷编码顺序必须以 0
开始,并覆盖全部的 2n
个整数。例子——输入:2
;输出:[0, 1, 3, 2];解释: 0 - 00
,1 - 01
,3 - 11
,2 - 10
G(i) = i ^ (i >> 2)
public ArrayList<Integer> grayCode(int n) {
ArrayList<Integer> result = new ArrayList<Integer>();
for (int i = 0; i < (1 << n); i++) {
result.add(i ^ (i >> 1));
}
return result;
}
复制代码
将一个整数中的数字进行颠倒
,当颠倒后的整数溢出时
,返回 0 (标记为 32 位整数)。
示例 :
输入: -123
输出: -321
复制代码
除 10 取余
的方法,将最低位和最高倒序输出
便可public int reverseInteger(int n) {
int reversed_n = 0;
while (n != 0) {
int temp = reversed_n * 10 + n % 10;
n = n / 10;
if (temp / 10 != reversed_n) {
reversed_n = 0;
break;
}
reversed_n = temp;
}
return reversed_n;
}
复制代码
运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制。它应该支持如下操做: 获取数据 get 和 写入数据 put 。
获取数据 get(key)
- 若是密钥 (key) 存在
于缓存中,则获取密钥的值(老是正数),不然返回 -1。
写入数据 put(key, value)
- 若是密钥不存在
,则写入
其数据值。当缓存容量达到上限时,它应该在写入新数据以前删除
最近最少使用的数据值,从而为新的数据值留出空间。
示例:
LRUCache cache = new LRUCache( 2 /* 缓存容量 */ );
cache.put(1, 1);
cache.put(2, 2);
cache.get(1); // 返回 1
cache.put(3, 3); // 该操做会使得密钥 2 做废
cache.get(2); // 返回 -1 (未找到)
cache.put(4, 4); // 该操做会使得密钥 1 做废
cache.get(1); // 返回 -1 (未找到)
cache.get(3); // 返回 3
cache.get(4); // 返回 4
复制代码
解法一:
HashMap
用于记录缓存内容public class LRUCache {
private class Node{
Node prev;
Node next;
int key;
int value;
public Node(int key, int value) {
this.key = key;
this.value = value;
this.prev = null;
this.next = null;
}
}
private int capacity;
private HashMap<Integer, Node> hs = new HashMap<Integer, Node>();
private Node head = new Node(-1, -1);// 头
private Node tail = new Node(-1, -1);// 尾
public LRUCache(int capacity) {
this.capacity = capacity;
tail.prev = head;
head.next = tail;
}
public int get(int key) {
if( !hs.containsKey(key)) { //key找不到
return -1;
}
// remove current
Node current = hs.get(key);
current.prev.next = current.next;
current.next.prev = current.prev;
// move current to tail
move_to_tail(current); //每次get,使用次数+1,最近使用,放于尾部
return hs.get(key).value;
}
public void set(int key, int value) { //数据放入缓存
// get 这个方法会把key挪到最末端,所以,不须要再调用 move_to_tail
if (get(key) != -1) {
hs.get(key).value = value;
return;
}
if (hs.size() == capacity) { //超出缓存上限
hs.remove(head.next.key); //删除头部数据
head.next = head.next.next;
head.next.prev = head;
}
Node insert = new Node(key, value); //新建节点
hs.put(key, insert);
move_to_tail(insert); //放于尾部
}
private void move_to_tail(Node current) { //移动数据至尾部
current.prev = tail.prev;
tail.prev = current;
current.prev.next = current;
current.next = tail;
}
}
复制代码
解法二:
LRU
缓存机制,须要在 O(1)
时间内完成以下操做:O(1)
时间内完成。有一种叫作有序字典
的数据结构,综合了哈希表
和链表
,在 Java 中为 LinkedHashMap
。
下面用这个数据结构来实现。
class LRUCache extends LinkedHashMap<Integer, Integer>{
private int capacity;
public LRUCache(int capacity) {
super(capacity, 0.75F, true);
this.capacity = capacity;
}
public int get(int key) {
return super.getOrDefault(key, -1);
}
public void put(int key, int value) {
super.put(key, value);
}
@Override
protected boolean removeEldestEntry(Map.Entry<Integer, Integer> eldest) {
return size() > capacity;
}
}
复制代码
get/in/set/move_to_end/popitem(get/containsKey/put/remove)
均可以在常数时间内完成。 空间复杂度:本片文章篇幅总结越长。我一直以为,一片过长的文章,就像一堂超长的 会议/课堂,体验很很差,因此我打算再开一篇文章
在后续文章中,我将继续针对链表
栈
队列
堆
动态规划
矩阵
位运算
等近百种,面试高频算法题,及其图文解析 + 教学视频 + 范例代码
,进行深刻剖析有兴趣能够继续关注 _yuanhao 的编程世界
不求快,只求优质,每篇文章将以 2 ~ 3
天的周期进行更新,力求保持高质量输出
仓库地址:超级干货!精心概括视频、归类、总结
,各位路过的老铁支持一下!给个 Star !