Android复习资料——常见面试算法题汇总(一)

// 层次遍历(DFS)
public static List<List> levelOrder(TreeNode root) {
List<List> res = new ArrayList<>();
if (root == null) {
return res;
}

dfs(root, res, 0);
return res;
}

private void dfs(TreeNode root, List<List> res, int level) {
if (root == null) {
return;
}
if (level == res.size()) {
res.add(new ArrayList<>());
}
res.get(level).add(root.val);

dfs(root.left, res, level + 1);
dfs(root.right, res, level + 1);
}

// 层次遍历(BFS)
public List<List> levelOrder(TreeNode root) {
List result = new ArrayList();

if (root == null) {
return result;
}

Queue queue = new LinkedList();
queue.offer(root);

while (!queue.isEmpty()) {
ArrayList level = new ArrayList();
int size = queue.size();
for (int i = 0; i < size; i++) {
TreeNode head = queue.poll();
level.add(head.val);
if (head.left != null) {
queue.offer(head.left);
}
if (head.right != null) {
queue.offer(head.right);
}
}
result.add(level);
}

return result;
}

// "Z"字遍历
public List<List> zigzagLevelOrder(TreeNode root) {
List<List> result = new ArrayList<>();

if (root == null){
return result;
}

Queue queue = new LinkedList<>();
queue.offer(root);
boolean isFromLeft = false;
while(!queue.isEmpty()){
int size = queue.size();
isFromLeft = !isFromLeft;
List list = new ArrayList<>();
for(int i = 0; i < size; i++){
TreeNode node;
if (isFromLeft){
node = queue.pollFirst();
}else{
node = queue.pollLast();
}
list.add(node.val);

if (isFromLeft){
if (node.left != null){
queue.offerLast(node.left);
}
if (node.right != null){
queue.offerLast(node.right);
}
}else{
if (node.right != null){
queue.offerFirst(node.right);
}
if (node.left != null){
queue.offerFirst(node.left);
}
}
}
result.add(list);
}

return result;
}

左右翻转

public void invert(TreeNode root) {
if (root == null) {
return;
}
TreeNode temp = root.left;
root.left = root.right;
root.right = temp;

invert(root.left);
invert(root.right);
}

最大值

public int getMax(TreeNode root) {
if (root == null) {
return Integer.MIN_VALUE;
} else {
int left = getMax(root.left);
int right = getMax(root.right);
return Math.max(Math.max(left, rigth), root.val);
}
}

最大深度

public int maxDepth(TreeNode root) {
if (root == null) {
return 0;
}

int left = maxDepth(root.left);
int right = maxDepth(root.right);
return Math.max(left, right) + 1;
}

最小深度

public int minDepth(TreeNode root) {
if (root == null) {
return 0;
}

int left = minDepth(root.left);
int right = minDepth(root.right);

if (left == 0) {
return right + 1;
} else if (right == 0) {
return left + 1;
} else {
return Math.min(left, right) + 1;
}
}

平衡二叉树

平衡二叉树每一个节点的左右两个子树的高度差不超过1

public boolean isBalanced(TreeNode root) {
return maxDepth(root) != -1;
}

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

int left = maxDepth(root.left);
int right = maxDepth(root.right);
if (left == -1 || right == -1 || Math.abs(left - right) > 1) {
return -1;
}
return Math.max(left, right) + 1;
}

链表

public class ListNode {
int val;
ListNode next;
ListNode(int x) {
val = x;
next = null;
}
}

删除节点

public void deleteNode(ListNode node) {
if (node.next == null){
node = null;
return;
}
// 取缔下一节点
node.val = node.next.val
node.next = node.next.next
}

翻转链表

public ListNode reverse(ListNode head) {
//prev表示前继节点
ListNode prev = null;
while (head != null) {
//temp记录下一个节点,head是当前节点
ListNode temp = head.next;
head.next = prev;
prev = head;
head = temp;
}
return prev;
}

中间元素

public ListNode findMiddle(ListNode head){
if(head == null){
return null;
}

ListNode slow = head;
ListNode fast = head;

// fast.next = null 表示 fast 是链表的尾节点
while(fast != null && fast.next != null){
fast = fast.next.next;
slow = slow.next;
}
return slow;
}

判断是否为循环链表

public Boolean hasCycle(ListNode head) {
if (head == null || head.next == null) {
return false;
}

ListNode slow = head;
ListNode fast = head.next;

while (fast != slow) {
if(fast == null || fast.next == null) {
return false;
}
fast = fast.next.next;
slow = slow.next;
}
return true;
}

合并两个已排序链表

public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
ListNode dummy = new ListNode(0);
ListNode lastNode = dummy;

while (l1 != null && l2 != null) {
if (l1.val < l2.val) {
lastNode.next = l1;
l1 = l1.next;
} else {
lastNode.next = l2;
l2 = l2.next;
}
lastNode = lastNode.next;
}

if (l1 != null) {
lastNode.next = l1;
} else {
lastNode.next = l2;
}

return dummy.next;
}

链表排序

可利用归并、快排等算法实现

// 归并排序
public ListNode sortList(ListNode head) {
if (head == null || head.next == null) {
return head;
}

ListNode mid = findMiddle(head);

ListNode right = sortList(mid.next);
mid.next = null;
ListNode left = sortList(head);

return mergeTwoLists(left, right);
}

// 快速排序
public ListNode sortList(ListNode head) {
quickSort(head, null);
return head;
}

private void quickSort(ListNode start, ListNode end) {
if (start == end) {
return;
}

ListNode pt = partition(start, end);
quickSort(start, pt);
quickSort(pt.next, end);
}

private ListNode partition(ListNode start, ListNode end) {
int pivotKey = start.val;
ListNode p1 = start, p2 = start.next;
while (p2 != end) {
if (p2.val < pivotKey) {
p1 = p1.next;
swapValue(p1, p2);
}
p2 = p2.next;
}

swapValue(start, p1);
return p1;
}

private void swapValue(ListNode node1, ListNode node2) {
int tmp = node1.val;
node1.val = node2.val;
node2.val = tmp;
}

删除倒数第N个节点

public ListNode removeNthFromEnd(ListNode head, int n) {
if (n <= 0) {
return null;
}

ListNode dummy = new ListNode(0);
dummy.next = head;

ListNode preDelete = dummy;
for (int i = 0; i < n; i++) {
if (head == null) {
return null;
}
head = head.next;
}
// 此时head为正数第N个节点
while (head != null) {
head = head.next;
preDelete = preDelete.next;
}
preDelete.next = preDelete.next.next;
return dummy.next;
}

两个链表是否相交

public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
if (headA == null || headB == null) {
return null;
}

ListNode currA = headA;
ListNode currB = headB;
int lengthA = 0;
int lengthB = 0;

// 让长的先走到剩余长度和短的一样
while (currA != null) {
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;

总结

**其实上面说了这么多,钱是永远赚不完的,在这个知识付费的时代,知识技能提升才是是根本!我作为一名8年的高级工程师,知识技能已经学习的差不多。**在看这篇文章的可能有刚刚入门,刚刚开始工作,或者大佬级人物。

像刚刚开始学Android开发小白想要快速提升自己,最快捷的方式,就是有人可以带着你一起分析,这样学习起来最为高效,所以这里分享一套高手学习的源码和框架视频等精品Android架构师教程,保证你学了以后保证薪资上升一个台阶。

这么重要的事情说三遍啦!点赞+点赞+点赞!

【Android高级架构师系统学习资料】高级架构师进阶必备——设计思想解读开源框架

第一章、热修复设计
第二章、插件化框架设计
第三章、组件化框架设计
第四章、图片加载框架
第五章、网络访问框架设计
第六章、RXJava 响应式编程框架设计
第七章、IOC 架构设计
第八章、Android 架构组件 Jetpack


《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!

给定一个非负数,表示一个数字数组,在该数的基础上+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;

总结

**其实上面说了这么多,钱是永远赚不完的,在这个知识付费的时代,知识技能提升才是是根本!我作为一名8年的高级工程师,知识技能已经学习的差不多。**在看这篇文章的可能有刚刚入门,刚刚开始工作,或者大佬级人物。

像刚刚开始学Android开发小白想要快速提升自己,最快捷的方式,就是有人可以带着你一起分析,这样学习起来最为高效,所以这里分享一套高手学习的源码和框架视频等精品Android架构师教程,保证你学了以后保证薪资上升一个台阶。

这么重要的事情说三遍啦!点赞+点赞+点赞!
[外链图片转存中…(img-WU3xhPAo-1714503151834)]

【Android高级架构师系统学习资料】高级架构师进阶必备——设计思想解读开源框架

第一章、热修复设计
第二章、插件化框架设计
第三章、组件化框架设计
第四章、图片加载框架
第五章、网络访问框架设计
第六章、RXJava 响应式编程框架设计
第七章、IOC 架构设计
第八章、Android 架构组件 Jetpack

[外链图片转存中…(img-khg6ZPzK-1714503151837)]
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值