Android复习资料——常见面试算法题汇总,vivoandroid面试

currA = currA.next;
lengthA++;
}
while (currB != null) {
currB = currB.next;
lengthB++;
}

currA = headA;
currB = headB;
while (lengthA > lengthB) {
currA = currA.next;
lengthA–;
}
while (lengthB > lengthA) {
currB = currB.next;
lengthB–;
}

// 然后同时走到第一个相同的地方
while (currA != currB) {
currA = currA.next;
currB = currB.next;
}
// 返回交叉开始的节点
return currA;
}

栈 / 队列

带最小值操作的栈

实现一个栈, 额外支持一个操作:min() 返回栈中元素的最小值

public class MinStack {
private Stack stack;
private Stack minStack; // 维护一个辅助栈,传入当前栈的最小值

public MinStack() {
stack = new Stack();
minStack = new Stack();
}

public void push(int number) {
stack.push(number);
if (minStack.isEmpty()) {
minStack.push(number);
} else {
minStack.push(Math.min(number, minStack.peek()));
}
}

public int pop() {
minStack.pop();
return stack.pop();
}

public int min() {
return minStack.peek();
}
}

有效括号

给定一个字符串所表示的括号序列,包含以下字符: ‘(’, ‘)’, ‘{’, ‘}’, ‘[’ and ‘]’, 判定是否是有效的括号序列。括号必须依照 “()” 顺序表示, “()[]{}” 是有效的括号,但 “([)]” 则是无效的括号。

public boolean isValidParentheses(String s) {
Stack stack = new Stack();
for (Character c : s.toCharArray()) {
if (“({[”.contains(String.valueOf©)) {
stack.push©;
} else {
if (!stack.isEmpty() && isValid(stack.peek(), c)) {
stack.pop();
} else {
return false;
}
}
}
return stack.isEmpty();
}

private boolean isValid(char c1, char c2) {
return (c1 == ‘(’ && c2 == ‘)’) || (c1 == ‘{’ && c2 == ‘}’)
|| (c1 == ‘[’ && c2 == ‘]’);
}

用栈实现队列

public class MyQueue {
private Stack outStack;
private Stack inStack;

public MyQueue() {
outStack = new Stack();
inStack = new Stack();
}

private void in2OutStack(){
while(!inStack.isEmpty()){
outStack.push(inStack.pop());
}
}

public void push(int element) {
inStack.push(element);
}

public int pop() {
if(outStack.isEmpty()){
this.in2OutStack();
}
return outStack.pop();
}

public int top() {
if(outStack.isEmpty()){
this.in2OutStack();
}
return outStack.peek();
}
}

逆波兰表达式求值

在反向波兰表示法中计算算术表达式的值, [“2”, “1”, “+”, “3”, “*”] -> (2 + 1) * 3 -> 9

public int evalRPN(String[] tokens) {
Stack s = new Stack();
String operators = “±*/”;
for (String token : tokens) {
if (!operators.contains(token)) {
s.push(Integer.valueOf(token));
continue;
}

int a = s.pop();
int b = s.pop();
if (token.equals(“+”)) {
s.push(b + a);
} else if(token.equals(“-”)) {
s.push(b - a);
} else if(token.equals(“*”)) {
s.push(b * a);
} else {
s.push(b / a);
}
}
return s.pop();
}

二分

二分搜索

public int binarySearch(int[] arr, int start, int end, int hkey){
if (start > end) {
return -1;
}

int mid = start + (end - start) / 2; //防止溢位
if (arr[mid] > hkey) {
return binarySearch(arr, start, mid - 1, hkey);
}
if (arr[mid] < hkey) {
return binarySearch(arr, mid + 1, end, hkey);
}
return mid;
}

X的平方根

public int sqrt(int x) {
if (x < 0) {
throw new IllegalArgumentException();
} else if (x <= 1) {
return x;
}

int start = 1, end = x;
// 直接对答案可能存在的区间进行二分 => 二分答案
while (start + 1 < end) {
int mid = start + (end - start) / 2;
if (mid == x / mid) {
return mid;
} else if (mid < x / mid) {
start = mid;
} else {
end = mid;
}
}
if (end > x / end) {
return start;
}
return end;
}

哈希表

两数之和

给一个整数数组,找到两个数使得他们的和等于一个给定的数 target。需要实现的函数twoSum需要返回这两个数的下标。

用一个hashmap来记录,key记录target-numbers[i]的值,value记录numbers[i]的i的值,如果碰到一个 numbers[j]在hashmap中存在,那么说明前面的某个numbers[i]和numbers[j]的和为target,i和j即为答案

public int[] twoSum(int[] numbers, int target) {

HashMap<Integer,Integer> map = new HashMap<>();

for (int i = 0; i < numbers.length; i++) {
if (map.containsKey(numbers[i])) {
return new int[]{map.get(numbers[i]), i};
}
map.put(target - numbers[i], i);
}

return new int[]{};
}

连续数组

给一个二进制数组,找到 0 和 1 数量相等的子数组的最大长度

使用一个数字sum维护到i为止1的数量与0的数量的差值。在loop i的同时维护sum并将其插入hashmap中。对于某一个sum值,若hashmap中已有这个值,则当前的i与sum上一次出现的位置之间的序列0的数量与1的数量相同。

public int findMaxLength(int[] nums) {
Map<Integer, Integer> prefix = new HashMap<>();
int sum = 0;
int max = 0;
prefix.put(0, -1); // 当第一个0 1数量相等的情况出现时,数组下标减去-1得到正确的长度
for (int i = 0; i < nums.length; i++) {
int num = nums[i];
if (num == 0) {
sum–;
} else {
sum++;
}

if (prefix.containsKey(sum)) {
max = Math.max(max, i - prefix.get(sum));
} else {
prefix.put(sum, i);
}
}

return max;
}

最长无重复字符的子串

用HashMap记录每一个字母出现的位置。设定一个左边界, 到当前枚举到的位置之间的字符串为不含重复字符的子串。若新碰到的字符的上一次的位置在左边界右边, 则需要向右移动左边界

public int lengthOfLongestSubstring(String s) {
if (s == null || s.length() == 0) {
return 0;
}
HashMap<Character, Integer> map = new HashMap<>();
int max = Integer.MIN_VALUE;
int start = -1; // 计算无重复字符子串开始的位置
int current = 0;
for (int i = 0; i < s.length(); i++) {
if (map.containsKey(s.charAt(i))) {
int tmp = map.get(s.charAt(i));
if (tmp >= start) { // 上一次的位置在左边界右边, 则需要向右移动左边界
start = tmp;
}
}

map.put(s.charAt(i), i);
max = Math.max(max, i - start);
}
return max;
}

最多点在一条直线上

给出二维平面上的n个点,求最多有多少点在同一条直线上

class Point {
int x;
int y;
Point() {
x = 0; y = 0;
}
Point(int a, int b) {
x = a; y = b;
}
}

通过HashMap记录下两个点之间的斜率相同出现的次数,注意考虑点重合的情况

public int maxPoints(Point[] points) {
if (points == null) {
return 0;
}

int max = 0;
for (int i = 0; i < points.length; i++) {
Map<Double, Integer> map = new HashMap<>();
int maxPoints = 0;
int overlap = 0;
for (int j = i + 1; j < points.length; j++) {
if (points[i].x == points[j].x && points[i].y == points[j].y) {
overlap++; // 两个点重合的情况记录下来
continue;
}
double rate = (double)(points[i].y - points[j].y) / (points[i].x - points[j].x);
if (map.containsKey(rate)) {
map.put(rate, map.get(rate) + 1);
} else {
map.put(rate, 2);
}
maxPoints = Math.max(maxPoints, map.get(rate));
}
if (maxPoints == 0) maxPoints = 1;
max = Math.max(max, maxPoints + overlap);
}
return max;
}

堆 / 优先队列

前K大的数

// 维护一个 PriorityQueue,以返回前K的数
public int[] topk(int[] nums, int k) {
int[] result = new int[k];
if (nums == null || nums.length < k) {
return result;
}

Queue pq = new PriorityQueue<>();
for (int num : nums) {
pq.add(num);
if (pq.size() > k) {
pq.poll();
}
}

for (int i = k - 1; i >= 0; i–) {
result[i] = pq.poll();
}

return result;
}

前K大的数II

实现一个数据结构,提供下面两个接口:1.add(number) 添加一个元素 2.topk() 返回前K大的数

public class Solution {
private int maxSize;
private Queue minheap;
public Solution(int k) {
minheap = new PriorityQueue<>();
maxSize = k;
}

public void add(int num) {
if (minheap.size() < maxSize) {
minheap.offer(num);
return;
}

if (num > minheap.peek()) {
minheap.poll();
minheap.offer(num);
}
}

public List topk() {
Iterator it = minheap.iterator();
List result = new ArrayList();
while (it.hasNext()) {
result.add((Integer) it.next());
}
Collections.sort(result, Collections.reverseOrder());
return result;
}
}

第K大的数

public int kthLargestElement(int k, int[] nums) {
if (nums == null || nums.length == 0 || k < 1 || k > nums.length){
return -1;
}
return partition(nums, 0, nums.length - 1, nums.length - k);
}

private int partition(int[] nums, int start, int end, int k) {
if (start >= end) {
return nums[k];
}

int left = start, right = end;
int pivot = nums[(start + end) / 2];

while (left <= right) {
while (left <= right && nums[left] < pivot) {
left++;
}
while (left <= right && nums[right] > pivot) {
right–;
}
if (left <= right) {
swap(nums, left, right);
left++;
right–;
}
}

if (k <= right) {
return partition(nums, start, right, k);
}
if (k >= left) {
return partition(nums, left, end, k);
}
return nums[k];
}

private void swap(int[] nums, int i, int j) {
int tmp = nums[i];
nums[i] = nums[j];
nums[j] = tmp;
}

二叉搜索树

验证二叉搜索树

public boolean isValidBST(TreeNode root) {
return isValidBST(root, Long.MIN_VALUE, Long.MAX_VALUE);
}

private boolean isValidBST(TreeNode root, long min, long max){
if (root == null) {
return true;
}

if (root.val <= min || root.val >= max){
return false;
}

return isValidBST(root.left, min, root.val) && isValidBST(root.right, root.val, max);
}

第K小的元素

增加getCount方法来获取传入节点的子节点数(包括自己),从root节点开始判断k值和子节点数的大小决定递归路径是往左还是往右。

public int kthSmallest(TreeNode root, int k) {
if (root == null) {
return 0;
}

int leftCount = getCount(root.left);
if (leftCount >= k) {
return kthSmallest(root.left, k);
} else if (leftCount + 1 == k) {
return root.val;
} else {
return kthSmallest(root.right, k - leftCount - 1);
}
}

private int getCount(TreeNode root) {
if (root == null) {
return 0;
}

return getCount(root.left) + getCount(root.right) + 1;
}

数组 / 双指针

加一

给定一个非负数,表示一个数字数组,在该数的基础上+1,返回一个新的数组。该数字按照数位高低进行排列,最高位的数在列表的最前面。

public int[] plusOne(int[] digits) {
int carries = 1;
for(int i = digits.length - 1; i >= 0 && carries > 0; i–){
int sum = digits[i] + carries;
digits[i] = sum % 10;
carries = sum / 10;
}
if(carries == 0) {
return digits;
}

int[] rst = new int[digits.length + 1];
rst[0] = 1;
for(int i = 1; i < rst.length; i++){
rst[i] = digits[i - 1];
}
return rst;
}

删除元素

给定一个数组和一个值,在原地删除与值相同的数字,返回新数组的长度。

public int removeElement(int[] A, int elem) {
if (A == null || A.length == 0) {
return 0;
}

int index = 0;
for (int i = 0; i < A.length; i++) {
if (A[i] != elem) {
A[index++] = A[i];
}
}

return index;
}

删除排序数组中的重复数字

在原数组中“删除”重复出现的数字,使得每个元素只出现一次,并且返回“新”数组的长度。

public int removeDuplicates(int[] A) {
if (A == null || A.length == 0) {
return 0;
}

int size = 0;
for (int i = 0; i < A.length; i++) {
if (A[i] != A[size]) {
A[++size] = A[i];
}
}
return size + 1;
}

我的日程安排表 I

实现MyCalendar类来存储活动。如果新添加的活动没有重复,则可以添加。类将有方法book(int start,int end)。这代表左闭右开的间隔[start,end)有了预定,范围内的实数x,都满足start <= x < end,返回true。 否则,返回false,并且事件不会添加到日历中。

TreeMap 是一个有序的key-value集合,它通过 红黑树 实现,继承于AbstractMap,所以它是一个Map,即一个key-value集合。TreeMap可以查询小于等于某个值的最大的key,也可查询大于等于某个值的最小的key。 元素的顺序可以改变,并且对新的数组不会有影响。

class MyCalendar {
TreeMap<Integer, Integer> calendar;

MyCalendar() {
calendar = new TreeMap();
}

public boolean book(int start, int end) {
Integer previous = calendar.floorKey(start), next = calendar.ceilingKey(start);
if ((previous == null || calendar.get(previous) <= start) && (next == null || end <= next)) {
calendar.put(start, end);
return true;
}
return false;
}
}

合并排序数组

合并两个排序的整数数组A和B变成一个新的数组。可以假设A具有足够的空间去添加B中的元素。

public void mergeSortedArray(int[] A, int m, int[] B, int n) {
int i = m - 1, j = n - 1, index = m + n - 1;
while (i >= 0 && j >= 0) {
if (A[i] > B[j]) {
A[index–] = A[i–];
} else {
A[index–] = B[j–];
}
}
while (i >= 0) {
A[index–] = A[i–];
}
while (j >= 0) {
A[index–] = B[j–];
}
}

贪心

买卖股票的最佳时机

假设有一个数组,它的第i个元素是一支给定的股票在第i天的价格。如果你最多只允许完成一次交易(例如,一次买卖股票),设计一个算法来找出最大利润。

public int maxProfit(int[] prices) {
if (prices == null || prices.length == 0) {
return 0;
}

int min = Integer.MAX_VALUE; //记录最低的价格
int profit = 0;
for (int price : prices) {
min = Math.min(price, min);
profit = Math.max(price - min, profit);
}

return profit;
}

买卖股票的最佳时机 II

给定一个数组 prices 表示一支股票每天的价格。可以完成任意次数的交易, 不过不能同时参与多个交易,设计一个算法求出最大的利润。

贪心:只要相邻的两天股票的价格是上升的, 我们就进行一次交易, 获得一定利润。

public int maxProfit(int[] prices) {
int profit = 0;
for (int i = 0; i < prices.length - 1; i++) {
int diff = prices[i + 1] - prices[i];
if (diff > 0) {
profit += diff;
}
}
return profit;
}

最大子数组

给定一个整数数组,找到一个具有最大和的子数组,返回其最大和。

public int maxSubArray(int[] A) {
if (A == null || A.length == 0){
return 0;
}
//max记录全局最大值,sum记录区间和,如果当前sum>0,那么可以继续和后面的数求和,否则就从0开始
int max = Integer.MIN_VALUE, sum = 0;
for (int i = 0; i < A.length; i++) {
sum += A[i];
max = Math.max(max, sum);
sum = Math.max(sum, 0);

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

img
img

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V:vip204888 备注Android获取(资料价值较高,非无偿)
img

写在最后

本次我的分享也接近尾声了,感谢你们在百忙中花上一下午来这里聆听我的宣讲,希望在接下来的日子,我们共同成长,一起进步!!!

最后放上一个大概的Android学习方向及思路(详细的内容太多了~),提供给大家:

对于程序员来说,要学习的知识内容、技术有太多太多,这里就先放上一部分,其他的内容有机会在后面的文章向大家呈现出来,不过我自己所有的学习资料都整理成了一个文档,一直在不断学习,希望能帮助到大家,也节省大家在网上搜索资料的时间来学习,也可以分享动态给身边好友一起学习!

为什么某些人会一直比你优秀,是因为他本身就很优秀还一直在持续努力变得更优秀,而你是不是还在满足于现状内心在窃喜!希望读到这的您能点个小赞和关注下我,以后还会更新技术干货,谢谢您的支持!

Android架构师之路很漫长,一起共勉吧!

如果你觉得文章写得不错就给个赞呗?如果你觉得那里值得改进的,请给我留言,一定会认真查询,修正不足,谢谢。

]
[外链图片转存中…(img-AJReBGef-1711548977353)]

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V:vip204888 备注Android获取(资料价值较高,非无偿)
[外链图片转存中…(img-drCntMAZ-1711548977354)]

写在最后

本次我的分享也接近尾声了,感谢你们在百忙中花上一下午来这里聆听我的宣讲,希望在接下来的日子,我们共同成长,一起进步!!!

最后放上一个大概的Android学习方向及思路(详细的内容太多了~),提供给大家:

[外链图片转存中…(img-5EqBWghn-1711548977355)]

对于程序员来说,要学习的知识内容、技术有太多太多,这里就先放上一部分,其他的内容有机会在后面的文章向大家呈现出来,不过我自己所有的学习资料都整理成了一个文档,一直在不断学习,希望能帮助到大家,也节省大家在网上搜索资料的时间来学习,也可以分享动态给身边好友一起学习!

为什么某些人会一直比你优秀,是因为他本身就很优秀还一直在持续努力变得更优秀,而你是不是还在满足于现状内心在窃喜!希望读到这的您能点个小赞和关注下我,以后还会更新技术干货,谢谢您的支持!

Android架构师之路很漫长,一起共勉吧!

如果你觉得文章写得不错就给个赞呗?如果你觉得那里值得改进的,请给我留言,一定会认真查询,修正不足,谢谢。

[外链图片转存中…(img-HZEGk09s-1711548977357)]

本文已被CODING开源项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》收录

  • 14
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值