大家好,我是独沽一味的猪。对于LeetCode刷题,相信大家并不陌生,然而很多人在付出了大量的时间和精力之后,往往收效甚微。基于此种现象,我希望能将LeetCode中经典的算法加以总结,方便大家对很多常见的算法有一个清晰的认识,更是对自己刷题的心得做出一些总结。
常见的排序算法有:
public class Test {
public static void main(String[] args) {
int[] arr = {6,4,3,9,2};
bubbleSort(arr);
}
public static void bubbleSort(int[] arr) {
for(int i = 0;iarr[j+1]) {
int temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
for(int i = 0; i
题目分析:
import java.util.Arrays;
public class InsertSort {
public static void main(String[] args) {
int[] arr = {5,8,6,3,9,2,1,7};
for (int i = 1; i < arr.length; i++) {
//arr[i] 当前元素 arr[i-1] 他前面的一个元素
//如果当前元素小于我前一个元素,那就交换位置
for (int j = i; j > 0; j--) {
if (arr[j] < arr[j - 1]) {
int t = arr[j];
arr[j] = arr[j - 1];
arr[j - 1] = t;
}
}
}
System.out.println(Arrays.toString(arr));
}
}
题目分析:
package com.qf.sort;
import java.util.Arrays;
public class HeapSort {
public static void main(String[] args) {
int[] arr={4,6,8,5,9};
heapSort(arr);
System.out.println(Arrays.toString(arr));
}
public static void heapSort(int[] arr){
//给你一个数组 {4,6,8,5,9} , 要求使用堆排序法,将数组升序排序
/*
将待排序序列构造成一个大顶堆
此时,整个序列的最大值就是堆顶的根节点。*/
/*adjustHeap( arr,1,arr.length);
adjustHeap( arr,0,arr.length);*/
for (int i = arr.length/2-1; i >=0; i--) {
adjustHeap( arr,i,arr.length);
}
int temp=0;
/*
将其与末尾元素进行交换,此时末尾就为最大值。
然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值。如此反复执行,便能得到一个有序序列了。*/
for (int j=arr.length-1;j>0;j--){
temp=arr[j];
arr[j]=arr[0];
arr[0]=temp;
adjustHeap(arr,0,j);
}
}
public static void adjustHeap(int[] arr,int i,int length){
//第一步把数组 {4,6,8,5,9}调整排序为{4,9,8,5,6}
//k=i*2+1 是循环对应的左子树元素
//记录当前要调整的元素
int temp=arr[i];
for (int k=i*2+1;ktemp){
//交换位置
arr[i]=arr[k];
i=k;
}else{
break;
}
}
arr[i]=temp;
}
}
题目分析:
常见的查找算法有:
题目描述:给定一个数字,我们按照如下规则把它翻译为字符串:0 翻译成 “a” ,1 翻译成 “b”,……,11 翻译成 “l”,……,25 翻译成 “z”。一个数字可能有多个翻译。请编程实现一个函数,用来计算一个数字有多少种不同的翻译方法。
题目解析:
01.当src(i - 1)到src(i + 1)的值为10到25之间,那么f(i)可以包括f(i - 2)种可能,集合为:
f(i - 2)、src(i - 1)、src(i);以及包括f(i - 1)种可能,集合为:f(i - 1)、src(i)。举个例子就是122可以看作1的所有可能集合 + 12的所有可能集合,所以122的所有可能集合是3
注意:f(i - 2)、src(i - 1)、src(i)和f(i - 1)、src(i)是完全不同的,一个最后两个数字是src(i - 1)、src(i),一个最后一个数字是src(i)
02.当src(i - 1)到src(i + 1)的值不在10到25之间,那么src(i)是没有贡献的,所以f(i) = f(i - 1)。举个例子就是128的28是没有贡献的,所以128和12的可能集合是一样的,最后的结果都是2
class Solution {
public:
int translateNum(int num) {
string src = to_string(num);
int p = 0, q = 0, r = 1;
for (int i = 0; i < src.size(); ++i) {
p = q;
q = r;
r = 0;
r += q;
if (i == 0) {
continue;
}
//src.substr(i - 1, 2)表示src(i - 1)到src(i + 1),注意i + 1不包括
auto pre = src.substr(i - 1, 2);
if (pre <= "25" && pre >= "10") {
r += p;
}
}
return r;
}
};
题目分析:使用滑动窗口的思想,创建一个0、0、1的窗口进行滑动,核心思想是窗口元素之间的1关系
题目描述:假设把某股票的价格按照时间先后顺序存储在数组中,请问买卖该股票一次可能获得的最大利润是多少?
/**
* 设置初始值:maxprofit 为 0,minprice 是无穷大
* 遍历规则:
* 1.获取最小的股票价格:数组中的某个值
* 2.获取处于最小的股票价格时,所获取的 maxprofit (比较当前的 maxprofit 和之前的 maxprofit )
* 3.返回 maxprofit
*/
class Solution {
public:
int maxProfit(vector& prices) {
int inf = 1e9; //1e9 = 1 * 10^9 = 1000000000
int minprice = inf, maxprofit = 0;
/**
* 目标数组:7 、1 、5 、3 、6 、4
* 初始值:maxprofit = 1e9 、minprice = 0
* 第一次遍历:maxprofit = 7 、minprice = 7 - 1e9
* 第二次遍历:maxprofit = 1 、minprice = 1
* 第三次遍历:maxprofit = 4 、minprice = 1
* 第四次遍历:maxprofit = 1e9 、minprice = 0
* 第五次遍历:maxprofit = 1e9 、minprice = 0
* 第六次遍历:maxprofit = 1e9 、minprice = 0
*/
for (int price: prices) {
//获取历史最低价格minprice,比较第i天卖出股票的利润,获取最大利润price[i] - minprice
maxprofit = max(maxprofit, price - minprice);
minprice = min(price, minprice);
}
return maxprofit;
}
};
题目分析:只能买卖一次,就是数组中最大的值和最小的值之间的差值,且较小的值得在较大的值前面。采用动态规划的思路:01.定义数组;02.找出数组元素之间的关系式;03.找出初始条件
常见的遍历算法有:
1.递归算法
题目描述:输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)
/**
* 递归解决问题
* 节点A和节点B比较,节点A的左节点和B比较,节点A的右节点和B比较
*/
public boolean isSubStructure(TreeNode A, TreeNode B) {
if(A == null || B == null) return false;
return dfs(A, B) || isSubStructure(A.left, B) || isSubStructure(A.right, B);
}
/**
* 节点A的值和节点B的值相等
* 节点A的左节点和节点B的左节点相等
* 节点A的右节点和节点B的右节点相等
*/
public boolean dfs(TreeNode A, TreeNode B){
if(B == null) return true;
if(A == null) return false;
//节点值相等、左子树 = 左子树、右子树 = 右子树
return A.val == B.val && dfs(A.left, B.left) && dfs(A.right, B.right);
}
题目分析:本题的核心在于巧妙地使用了递归算法对两个TreeNode进行了遍历,总结一下递归算法的使用条件:
2.迭代算法
题目描述:给你一棵二叉树的根节点 root ,返回其节点值的后序遍历
后序遍历:按照访问左子树—右子树—根节点的方式遍历这棵树,而在访问左子树或者右子树的时候,我们按照同样的方式遍历,直到遍历完整棵树。
/**
* 利用栈后进先出的特点来存储树的节点
* 找到根节点的左子树,存储左子树
*/
class Solution {
public List postorderTraversal(TreeNode root) {
List res = new ArrayList();
if (root == null) {
return res;
}
Deque stack = new LinkedList();
TreeNode prev = null;
while (root != null || !stack.isEmpty()) {
while (root != null) {
stack.push(root);
root = root.left; //找到根节点的左子树
}
root = stack.pop();
if (root.right == null || root.right == prev) { //右子树不存在,或者左子树就是右子树
res.add(root.val);
prev = root;
root = null;
} else {
stack.push(root); //根节点出栈
root = root.right; //根节点向下遍历
}
}
return res;
}
}
题目解析:本题利用迭代的思想,逐层迭代来获取树的后序遍历节点。总结一下迭代算法的使用条件:
优秀的算法需要深入的理解和充分的运用,希望大家在以后编程的过程中,充分吸收这些算法的设计思想,代码越来越优雅,越来越严谨。