自己挑的十个看起来高频或重要的算法题,仅供个人学习使用
题目和答案来自力扣题库
目录
121. 买卖股票的最佳时机
计算最大利润,后的股价-当天的股价,或者立刻卖出
思路:贪心
class Solution {
public int maxProfit(int[] prices) {
int max = 0;
int pre = prices[0];
for(int cur : prices){
if((cur - pre) > 0){
max = Math.max(cur-pre, max);
}
else{
pre = cur;
}
}
return max;
}
}
206. 反转链表
给你单链表的头节点 head
,请你反转链表,并返回反转后的链表。
方法一:迭代
class Solution {
public ListNode reverseList(ListNode head) {
ListNode node = null;
while(head != null){
ListNode tmp = head.next;
head.next = node;
node = head;
head = tmp;
}
return node;
}
}
方法二:递归
class Solution {
public ListNode reverseList(ListNode head) {
if(head == null || head.next == null){
return head;
}
ListNode node = reverseList(head.next);
head.next.next = head;
head.next = null;
return node;
}
}
给定一个未经排序的整数数组,找到最长且 连续递增的子序列,并返回该序列的长度。
方法一:贪心
class Solution {
public int findLengthOfLCIS(int[] nums) {
int ans = 0;
int n = nums.length;
int start = 0;
for(int i = 0; i < n; i++){
if( i>0 && nums[i-1] >= nums[i] ){
start = i; //更新开始值
}
ans = Math.max(ans, i - start + 1);
}
return ans;
}
}
方法二:动态规划(原题解)
- 状态定义dp[i]:表示必须以nums[i]结尾的连续递增序列长度
- 状态转移:dp[i] = nums[i] > nums[i-1] ? dp[i-1] + 1 : 1;
- ans = max { dp[i] }
class Solution {
public int findLengthOfLCIS(int[] nums) {
int pre = 1; // pre表示dp[i-1]: 必须以i-1位置结尾的递增子数组长度
int ans = pre;
for (int i = 1; i < nums.length; i++) {
int cur = nums[i] > nums[i-1] ? pre + 1 : 1; //更新当前值
ans = Math.max(ans, cur);
pre = cur;
}
return ans;
}
}
92. 反转链表 II
给你单链表的头指针 head
和两个整数 left
和 right
,其中 left <= right
。请你反转从位置 left
到位置 right
的链表节点,返回 反转后的链表 。
方法一:截取中间的子链表,翻转再连接。
方法二:头插法,遍历一次
class Solution {
public ListNode reverseBetween(ListNode head, int left, int right) {
ListNode dummyNode = new ListNode(-1,head);
// 找到前驱结点
ListNode pre = dummyNode;
for(int i = 0; i < left-1; i++) pre = pre.next;
// 从前驱结点后一个开始
ListNode cur = pre.next;
ListNode tmp;
for(int i = 0; i < right-left; i++){
tmp = cur.next;
cur.next = tmp.next;
tmp.next = pre.next;
pre.next = tmp;
}
return dummyNode.next;
}
}
145. 二叉树的后序遍历
方法一:
一定要注意一下条件
先处理左孩子,再处理右孩子,最后处理自己
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<Integer>();
if(root == null){
return res;
}
Deque<TreeNode> stack = new LinkedList<TreeNode>();
stack.push(root);
TreeNode pre = root;
while(!stack.isEmpty()){
TreeNode cur = stack.peek();
if(cur.left!= null && pre != cur.left && pre != cur.right){
stack.push(cur.left);
}else if(cur.right != null && pre != cur.right){
stack.push(cur.right);
}else{
res.add(cur.val);
stack.pop();
pre = cur;
}
}
return res;
}
方法二:
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
Deque<TreeNode> stack = new linkedList<>();
while(root != null || !stack.isEmpty()){
while(root != null){
res.add(root.val); //处理节点
stack.push(root);
root = root.right; //先把右边界入栈
}
TreeNode cur = stack.pop();
root = cur.left;
}
Collections.reverse(res); //反转数组
return res;
}
695. 岛屿的最大面积
给定一个由 0 和 1 组成的二维矩阵,求岛屿的最大面积。
方法一:递归,深度优先
class Solution {
int[] x0={1,-1,0,0};
int[] y0={0,0,1,-1};
public int maxAreaOfIsland(int[][] grid) {
int area = 0;
int m = grid.length;
int n = grid[0].length;
for(int i = 0; i < m; i++){
for(int j =0; j <n; j++){
if(grid[i][j] == 1){ //这个判断可以不要
area = Math.max(DFS(grid, i,j), area);
}
}
}
return area;
}
public int DFS(int[][] grid, int x, int y){
if(x < 0 || x == grid.length || y < 0 || y == grid[0].length || grid[x][y] == 0){
return 0;
}
grid[x][y] = 0;
int ans = 1;
for(int i = 0; i< 4; i++){
ans +=DFS(grid, x+x0[i],y+y0[i]); //这个面积累加就跟巧妙
}
return ans;
}
}
方法二:迭代(栈),深度优先
因为每一个顶点有x,y两个信息量,所以用两个栈分别保存x坐标和y坐标
可以类比树的深度优先,左神的先序遍历,先右孩子再左孩子,弹出再右孩子左孩子
这里,相当于一个顶点有四个孩子,弹出将其孩子添加到栈中
public int DFS(int[][] grid, int x, int y){
int ans = 0;
Deque<Integer> stackX = new LinkedList<>();
Deque<Integer> stackY = new LinkedList<>();
stackX.push(x);
stackY.push(y);
while (!stackX.isEmpty()){
int cur_x = stackX.pop();
int cur_y = stackY.pop();
if (cur_x < 0 || cur_y < 0 || cur_x >= grid.length || cur_y >= grid[0].length || grid[cur_x][cur_y] != 1) {
continue;
}
grid[cur_x][cur_y] = 0;
ans++;
for (int i = 0; i < 4; i++) {
int dx = cur_x + x0[i];
int dy = cur_y + y0[i];
stackX.push(dx);
stackY.push(dy);
}
}
return ans;
}
方法三:广度优先搜索
使用队列,出队添加所有孩子
98. 验证二叉搜索树
给你一个二叉树的根节点 root
,判断其是否是一个有效的二叉搜索树。
要注意的是测试用例的边界,精度都要大于INT才可测试通过
官方递归:
class Solution {
public boolean isValidBST(TreeNode root) {
return isValidBST(root, Long.MIN_VALUE, Long.MAX_VALUE);
}
public boolean isValidBST(TreeNode node, long lower, long upper) {
if (node == null) {
return true;
}
if (node.val <= lower || node.val >= upper) {
return false;
}
return isValidBST(node.left, lower, node.val) && isValidBST(node.right, node.val, upper);
}
}
77. 组合
给定两个整数 n
和 k
,返回范围 [1, n]
中所有可能的 k
个数的组合。
回溯
参考题解
class Solution {
List<List<Integer>> res = new ArrayList<>();
List<Integer> subset = new ArrayList<>();
public List<List<Integer>> combine(int n, int k) {
dfs(1,n,k);
return res;
}
public void dfs(int start, int n, int k){
if(subset.size() == k){
res.add(new ArrayList<>(subset));
return;
}
for (int i = start; i <= n ; i++) {
subset.add(i);
dfs(i+1,n,k);
subset.remove(subset.size()-1);
}
return;
}
}
剪枝优化
private void dfs(int begin, int n, int k, Deque<Integer> path, List<List<Integer>> res) {
if (k == 0) {
res.add(new ArrayList<>(path));
return;
}
// 基础版本的递归终止条件:if (begin == n + 1) {
if (begin > n - k + 1) {
return;
}
// 不选当前考虑的数 begin,直接递归到下一层
dfs(begin + 1, n, k, path, res);
// 不选当前考虑的数 begin,递归到下一层的时候 k - 1,这里 k 表示还需要选多少个数
path.addLast(begin);
dfs(begin + 1, n, k - 1, path, res);
// 深度优先遍历有回头的过程,因此需要撤销选择
path.removeLast();
}
面试题 16.01. 交换数字
编写一个函数,不用临时变量,直接交换numbers = [a, b]
中a
与b
的值。
思路:^
相同的数异或为0,任何数与0异或得它本身,满足交换律和结合律
tmp = a ^ b,这里tmp表示a和b之间的差异,ab任何与tmp进行异或都可以将另一个数还原
那么,只需要tmp和a,b中的一个,就可以还原另一个数了
这样就只需两个变量保存了
// a = a ^ b 此时a为tmp
// b = a ^ b 此时b = tmp ^ b = a
// a = a ^ b 此时a = tmp ^ a = b
198. 打家劫舍
如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。 计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。
动态规划
方法一:
class Solution {
public int rob(int[] nums) {
int[] dp = new int[nums.length];
dp[0] = nums[0];
int max = nums[0];
for(int i = 1; i < nums.length; i++ ){
dp[i] = i!= 1 ? Math.max(dp[i-1], dp[i-2] + nums[i]) : Math.max(nums[0], nums[1]) ;
max = Math.max(max, dp[i]);
}
return max;
}
}
方法二:
class Solution {
public int rob(int[] nums) {
int pre = 0, cur = 0, tmp;
for(int num : nums) {
tmp = cur;
cur = Math.max(pre + num, cur);
pre = tmp;
}
return cur;
}
}