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移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V:vip204888 备注Android获取(资料价值较高,非无偿)
写在最后
本次我的分享也接近尾声了,感谢你们在百忙中花上一下午来这里聆听我的宣讲,希望在接下来的日子,我们共同成长,一起进步!!!
最后放上一个大概的Android学习方向及思路(详细的内容太多了~),提供给大家:
对于程序员来说,要学习的知识内容、技术有太多太多,这里就先放上一部分,其他的内容有机会在后面的文章向大家呈现出来,不过我自己所有的学习资料都整理成了一个文档,一直在不断学习,希望能帮助到大家,也节省大家在网上搜索资料的时间来学习,也可以分享动态给身边好友一起学习!
为什么某些人会一直比你优秀,是因为他本身就很优秀还一直在持续努力变得更优秀,而你是不是还在满足于现状内心在窃喜!希望读到这的您能点个小赞和关注下我,以后还会更新技术干货,谢谢您的支持!
Android架构师之路很漫长,一起共勉吧!
如果你觉得文章写得不错就给个赞呗?如果你觉得那里值得改进的,请给我留言,一定会认真查询,修正不足,谢谢。
]
[外链图片转存中…(img-AJReBGef-1711548977353)]
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V:vip204888 备注Android获取(资料价值较高,非无偿)
[外链图片转存中…(img-drCntMAZ-1711548977354)]
写在最后
本次我的分享也接近尾声了,感谢你们在百忙中花上一下午来这里聆听我的宣讲,希望在接下来的日子,我们共同成长,一起进步!!!
最后放上一个大概的Android学习方向及思路(详细的内容太多了~),提供给大家:
[外链图片转存中…(img-5EqBWghn-1711548977355)]
对于程序员来说,要学习的知识内容、技术有太多太多,这里就先放上一部分,其他的内容有机会在后面的文章向大家呈现出来,不过我自己所有的学习资料都整理成了一个文档,一直在不断学习,希望能帮助到大家,也节省大家在网上搜索资料的时间来学习,也可以分享动态给身边好友一起学习!
为什么某些人会一直比你优秀,是因为他本身就很优秀还一直在持续努力变得更优秀,而你是不是还在满足于现状内心在窃喜!希望读到这的您能点个小赞和关注下我,以后还会更新技术干货,谢谢您的支持!
Android架构师之路很漫长,一起共勉吧!
如果你觉得文章写得不错就给个赞呗?如果你觉得那里值得改进的,请给我留言,一定会认真查询,修正不足,谢谢。
[外链图片转存中…(img-HZEGk09s-1711548977357)]