基本数据类型处理技巧
int
//target /= 10 配合 int temp = target % 10 来使用 HashSet<Integer> hs = new HashSet<>();// 使用HashSet来判断是否是不重复的 int target = sc.nextInt(); while(target != 0){ int temp = target % 10;//个位 if(hs.add(temp)) // 如果能加入,就是说明没有重复 System.out.print(temp); target /= 10;// 除10能去掉最右边的数字 } System.out.println()
String str = Integer.toBinaryString(num);//二进制 字符串 int length = str.length(); String newStr = str.replaceAll("1", ""); System.out.println(length - newStr.length());
//判断数 二进制表示时 其末尾是否为1 if((num&1) == 1) //如果末位为1则计数n++; num = num >>> 1; //无符号右移 减掉尾巴
int val = Integer.parseInt(str, 16);//将16进制的字符串转换为int int d = Integer.valueOf(c,16);
double
- System.out.println(String.format("%.1f", x));//格式化输出
关于集合的必知必会
数组工具类
Arrays.sort(ss);//按英语字典顺序排序,入参String[],输出ss即为有序
Arrays.sort(score,(o1,o2) ->{
if(flag==0){
return o2[1] - o1[1];//按第二列降序排列,如果相等的话,返回0,顺序不变
}else{
return o1[1] - o2[1];//按第二列升序
}
});
Arrays.sort(pIntegerArray, new Comparator<Integer>() {
public int compare(Integer o1, Integer o2) {
return o1 - o2;
}
});
Collections.sort(list);//List<String> list = new ArrayList<>();类比
List<Integer> linkedlist = new ArrayList<>();
linkedlist.add(head);
linkedlist.add(linkedlist.indexOf(target) + 1, value);
linkedlist.remove(linkedlist.indexOf(remove));
//对intervals对象数组进行排序(指定了排序规则)
Collections.sort(intervals, (v1,v2)->v1.start-v2.start);
ArrayList<Integer> list = new ArrayList<>(set);//取当前所有的结果,这样构造
自定义排序
//内部类
public class ComparaKey implements Comparator<Integer> {
@Override
public int compare(Integer num1, Integer num2) {
return keyOperations.get(num1) - keyOperations.get(num2);
}
}
//使用
LinkedHashMap<Integer, Integer> container = null; // 容器,用于存放 (key, value)
int[] keys = container.keySet().stream().sorted(new ComparaKey()).mapToInt(Integer::intValue).toArray();
LinkedHashSet
- 按原来的顺序(按插入的顺序)
HashSet
- HashSet
hs = new HashSet<>();//集合中的元素是不重复的 - hs.add(temp);//返回值是Boolean类型(已经存在了就加不进去了,就返回false)
- BitSet bitSet = new BitSet(128);//集合是可以指定大小的
- HashSet
set = new HashSet<>();//-->ArrayList list = new ArrayList<>(set); - 凡是有Tree的集合,都是有序的,凡是有Set的就是不重复的
TreeSet
- 有序的(排序的)
Map
java为数据结构中的映射定义了一个接口java.util.Map;
它有四个实现类,分别是HashMap Hashtable LinkedHashMap 和TreeMap.
Map主要用于存储健值对,根据键得到值,因此不允许键重复(重复了覆盖了),但允许值重复。
Hashmap 是一个最常用的Map,它根据键的HashCode值存储数据,根据键可以直接获取它的值,具有很快的访问速度,遍历时,取得数据的顺序是完全随机的。 HashMap最多只允许一条记录的键为Null;允许多条记录的值为 Null;HashMap不支持线程的同步,即任一时刻可以有多个线程同时写HashMap;可能会导致数据的不一致。如果需要同步,可以用 Collections的synchronizedMap方法使HashMap具有同步的能力,或者使用ConcurrentHashMap。
Hashtable与HashMap类似,它继承自Dictionary类,不同的是:它不允许记录的键或者值为空;它支持线程的同步,即任一时刻只有一个线程能写Hashtable,因此也导致了 Hashtable在写入时会比较慢。
LinkedHashMap 是HashMap的一个子类,保存了记录的插入顺序,在用Iterator遍历LinkedHashMap时,先得到的记录肯定是先插入的.也可以在构造时用带参数,按照应用次数排序。在遍历的时候会比HashMap慢,不过有种情况例外,当HashMap容量很大,实际数据较少时,遍历起来可能会比 LinkedHashMap慢,因为LinkedHashMap的遍历速度只和实际数据有关,和容量无关,而HashMap的遍历速度和他的容量有关。
TreeMap实现SortMap接口,能够把它保存的记录根据键排序,默认是按键值的升序排序,也可以指定排序的比较器,当用Iterator 遍历TreeMap时,得到的记录是排过序的。
一般情况下,我们用的最多的是HashMap,在Map 中插入、删除和定位元素,HashMap 是最好的选择。但如果您要按自然顺序或自定义顺序遍历键,那么TreeMap会更好。如果需要输出的顺序和输入的相同,那么用LinkedHashMap 可以实现,它还可以按读取顺序来排列.
//构建方式
static Map<String, String> map = new HashMap<String, String>(){
{
put("reset", "reset what");
put("reset board", "board fault");
put("board add", "where to add");
put("board delete", "no board at all");
put("reboot backplane", "impossible");
put("backplane abort", "install first");
}
};
TreeMap
map的key是不能重复的
TreeMap<Integer, Integer> map = new TreeMap<>();//特点是key是有序的
map.put(a,map.getOrDefault(a,0) + b);//getOrDefault这个方法是一定要会的!
map.containsKey(a);
for (Integer i : map.keySet()) {//通过键的set集合进行遍历
//为一个key无序的map有序输出 Set<Map.Entry<Integer, Integer>> entries = result.entrySet(); for (Map.Entry<Integer, Integer> entry:entries) { System.out.println(entry.getKey() + " " + entry.getValue()); }
链表
public class Main {
private ListNode head;
private class ListNode {
int val;
ListNode next;
ListNode(int val) {
this.val = val;
}
ListNode(int val, ListNode next) {
this.val = val;
this.next = next;
}
}
public Main(int val) {
head = new ListNode(val);
}
public void insert(int val, int node) {
ListNode p = head;
while (p.val != node) {
p = p.next;
}
ListNode newNode = new ListNode(val, p.next);
p.next = newNode;
}
public void delete(int node) {
ListNode dummy = new ListNode(0, head);
ListNode p = dummy;
while (p.next != null && p.next.val != node) {
p = p.next;
}
if (p.next != null) {
p.next = p.next.next;
}
head = dummy.next;
}
public ListNode head() {
return this.head;
}
……
输入输出处理
- int n = Integer.parseInt(in.nextLine());//一个大坑:如果直接nextInt,后面循环会少取一次,会闯鬼!需要在后面再sc.nextLine();可避坑
import java.io.*;//java.io.*;
import java.util.*;//java.util.*;
public class Main{
public static void main(String[] args) throws Exception{//throws Exception
//这样可以获取一行中用空格分开的多个int数值
int a = sc.nextInt();
int b = sc.nextInt();
}
}
//格式化输出double
if(countP==0){
System.out.printf("0.0");
}else{
System.out.printf("%.1f\n",sum/countP);
}
字符串处理API
- Character.isLetter(c);//判断一个字符是否为英文字母
- Character.isDigit(c);Character.isSpaceChar(c);//!!!
- Character.toLowerCase(cs[j]);//将字符转换为小写形式
- StringBuilder stringBuilder = new StringBuilder(scanner.nextLine()).reverse();//反转
- value = value.toLowerCase(Locale.ROOT);//String value;
正则表达式
str.split("\\s+");//匹配空白
if(!line.matches("\\d+")){//如果匹配的不是数字
String[] words = str.split("[^A-Za-z]");// 匹配非字母的字符进行分割
if(!s.matches("[WASD][0-9]{1,2}")){continue;}//指定第一个字母,而后是数字,1位或者2位
if (!maskBinary.matches("[1]{1,}[0]{1,}")) {return true;}//1个或多个1接1个或多个0
String[] ipTable = ip.split("\\.");//点号分隔
String s1=str.replaceAll("[A-Z]+|[a-z]+", "");
String s3=s2.replaceAll("[0-9]+", "");
//str字符串是否包含大写字符 str中包含了[A-Z]
Pattern p1 = Pattern.compile("[A-Z]");
if(p1.matcher(str).find()){count++;}
//当正则完全匹配字符串,从头到尾正好匹配上字符串,matches()方法是true,find()方法为false
//当正则只能匹配字符串中的部分内容,matches()方法是fasle ,find()方法是true
Pattern p4 = Pattern.compile("[^a-zA-Z0-9]");//^注意这个表示否
if(c>='A'&& c<='Y'){ //如果是A~Y的大写字母则需要将其+32位转换成小写再向后移1位
char newChar = (char)(c+32+1);
buffer.append(String.valueOf(newChar));
}
//把所有的数字段提取出来,前后加上星号再放回去
System.out.println(input.replaceAll("([0-9]+)","*$1*"));
字符串反转
import java.util.Scanner;
import java.util.Stack;
public class Main {
//1 StringBuffer 工具类
public static void test1() {
Scanner scanner = new Scanner(System.in);
String s = scanner.nextLine();
StringBuffer stringBuffer = new StringBuffer(s);
System.out.println(stringBuffer.reverse().toString());
}
//2 StringBuilder 工具类
public static void test2() {
Scanner scanner = new Scanner(System.in);
String s = scanner.nextLine();
StringBuilder stringBuilder = new StringBuilder(s);
System.out.println(stringBuilder.reverse());
}
//3 char数组
public static void test3() {
Scanner scanner = new Scanner(System.in);
String s = scanner.nextLine();
char[] chars = s.toCharArray();
String temp = "";
for (int i = chars.length - 1; i >= 0; i--) {
temp += chars[i];
}
System.out.println(temp);
}
//4 字符串操作
public static void test4() {
Scanner scanner = new Scanner(System.in);
String s = scanner.nextLine();
for (int i = s.length() - 1; i >= 0; i--) {
System.out.print(s.charAt(i));
}
}
//5 递归实现的Java解法 二分翻转
public static void test5() {
Scanner scanner = new Scanner(System.in);
while (scanner.hasNext()) {
String s = scanner.nextLine();
System.out.println(Main.reverse(s));
//return;
}
}
public static String reverse(String s) {
int len = s.length();
if (len <= 1) {
return s;
}
String left = s.substring(0, len / 2);
String right = s.substring(len / 2, len);
return reverse(right) + reverse(left);//右边加左边,实现递归翻转
}
//6 栈 数据结构
static class Transfer {
public String transfer(String str) {
Stack<String> stack = new Stack<String>();
for(int i=0; i<str.length(); i++) {
char s = str.charAt(i);
stack.push(String.valueOf(s)); //将每一个字符压入栈中
}
String result = "";
while(!stack.empty()) {
result += stack.pop(); //按出栈顺序组成字符串,即为反转字符串
}
return result;
}
}
public static void test6() {
Scanner sc = new Scanner(System.in);
String s = sc.nextLine();
String result = new Transfer().transfer(s);
System.out.println(result);
}
public static void main(String[] args) {
test6();
}
}
动态规划算法(DP)
- 背包系列问题
- 空间换时间
- 状态转移方程式
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
while (sc.hasNextLine()) {
int money = sc.nextInt();
int m = sc.nextInt();
sc.nextLine();
money /= 10;
int[][] prices = new int[m+1][3];
int[][] weights = new int[m+1][3];
for (int i = 1; i <= m; i++) {
int a = sc.nextInt();
int b = sc.nextInt();
int c = sc.nextInt();
a /= 10;//price
b = b * a;//weight
if (c == 0) {
// 主件
prices[i][0] = a;
weights[i][0] = b;
} else if (prices[c][1] == 0) {
// 附件1
prices[c][1] = a;
weights[c][1] = b;
} else {
// 附件2
prices[c][2] = a;
weights[c][2] = b;
}
sc.nextLine();
}
int[][] dp = new int[m+1][money+1];
for (int i = 1; i <= m; i++) {
for(int j = 1; j <= money; j++) {
int a = prices[i][0];
int b = weights[i][0];
int c = prices[i][1];
int d = weights[i][1];
int e = prices[i][2];
int f = weights[i][2];
dp[i][j] = j - a >= 0 ? Math.max(dp[i-1][j], dp[i-1][j-a] + b) : dp[i-1][j];
dp[i][j] = j-a-c >= 0 ? Math.max(dp[i][j], dp[i-1][j-a-c] + b + d):dp[i][j];
dp[i][j] = j-a-e >= 0 ? Math.max(dp[i][j], dp[i-1][j-a-e] + b + f):dp[i][j];
dp[i][j] = j-a-c-e >= 0 ? Math.max(dp[i][j], dp[i-1][j-a-c-e] + b +d + f):dp[i][j];
}
}
System.out.println(dp[m][money] * 10);
}
}
}
排序算法
冒泡排序
//冒泡排序
char[] cs = sb.toString().toCharArray();
for(int i = 0; i < cs.length; i++){//cs.length-1 最外层可以少跑一趟
for(int j = 0; j < cs.length - i - 1; j++){
if(Character.toLowerCase(cs[j]) - Character.toLowerCase(cs[j + 1]) > 0){
char temp = cs[j];
cs[j] = cs[j + 1];
cs[j + 1] = temp;
}
}
}
递归函数实现排列组合
//在1,2,3,4,中选3个有多少种组合方式
import java.util.Stack;
public class Main {
public static Stack<Integer> stack = new Stack<Integer>();
public static void main(String[] args) {
int shu[] = {1,2,3,4};
f(shu,3,0,0); // 从这个数组4个数中选择三个
}
/**
* @param shu 元素
* @param targ 要选多少个元素
* @param has 当前有多少个元素
* @param cur 当前选到的下标
* 1 2 3 //开始下标到2
* 1 2 4 //然后从3开始
*/
private static void f(int[] shu, int targ, int has, int cur) {
if(has == targ) {
System.out.println(stack);
return;
}
for(int i=cur;i<shu.length;i++) {
if(!stack.contains(shu[i])) {
stack.add(shu[i]);
f(shu, targ, has+1, i);
stack.pop();
}
}
}
}
常用算法
识别有效的IP地址和掩码并进行分类统计
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int aNum = 0;
int bNum = 0;
int cNum = 0;
int dNum = 0;
int eNum = 0;
int errNum = 0;
int pNum = 0;
while (sc.hasNextLine()) {
String str = sc.nextLine();
String[] strArr = str.split("~");
int ipFirst = getIpSeg(strArr[0], 0);
if (ipFirst == 0 || ipFirst == 127) {
continue;
}
if (maskIsInvaild(strArr[1])) {
errNum++;
continue;
}
if (ipIsInvaild(strArr[0])) {
errNum++;
continue;
}
if (ipFirst >= 1 && ipFirst <= 126) {
aNum++;
}
if (ipFirst >= 128 && ipFirst <= 191) {
bNum++;
}
if (ipFirst >= 192 && ipFirst <= 223) {
cNum++;
}
if (ipFirst >= 224 && ipFirst <= 239) {
dNum++;
}
if (ipFirst >= 240 && ipFirst <= 255) {
eNum++;
}
int ipSecond = getIpSeg(strArr[0], 1);
if (ipFirst == 10 || (ipFirst == 172 && ipSecond >= 16 && ipSecond <=31) || (ipFirst == 192 && ipSecond == 168)) {
pNum++;
}
}
System.out.println(aNum + " " + bNum + " " + cNum + " " + dNum + " " + eNum + " " + errNum + " " + pNum);
}
public static boolean maskIsInvaild(String mask) {
String[] maskArr = mask.split("\\.");
if (maskArr.length != 4) {
return true;
}
String maskBinary = toBinary(maskArr[0]) + toBinary(maskArr[1]) + toBinary(maskArr[2]) + toBinary(maskArr[3]);
if (!maskBinary.matches("[1]{1,}[0]{1,}")) {
return true;
}
return false;
}
public static String toBinary(String num) {
String numBinary = Integer.toBinaryString(Integer.valueOf(num));
while (numBinary.length() < 8) {
numBinary = "0" + numBinary;
}
return numBinary;
}
public static boolean ipIsInvaild(String ip) {
String[] ipArr = ip.split("\\.");
if (ipArr.length != 4) {
return true;
}
if (Integer.valueOf(ipArr[0]) > 255 || Integer.valueOf(ipArr[1]) > 255 || Integer.valueOf(ipArr[2]) > 255 || Integer.valueOf(ipArr[3]) > 255) {
return true;
}
return false;
}
public static int getIpSeg(String ip, int index) {
String[] ipArr = ip.split("\\.");
return Integer.valueOf(ipArr[index]);
}
}
判断两个IP是否属于同一子网
import java.io.*;
import java.util.*;
public class Main{
public static void main(String[] args) throws Exception{
Scanner sc = new Scanner(System.in);
while(sc.hasNextLine()){
String mask = sc.nextLine();
String ip1 = sc.nextLine();
String ip2 = sc.nextLine();
check( mask, ip1, ip2);
}
}
public static void check(String mask, String ip1, String ip2){
boolean codeMask = checkFormat(mask);
boolean codeIp1 = checkFormatIP(ip1);
boolean codeIp2 = checkFormatIP(ip2);
if(!codeMask || !codeIp1 || !codeIp2){
System.out.println(1);
return;
}
long maskNum = ip2num(mask);
long ip1Num = ip2num(ip1);
long ip2Num = ip2num(ip2);
//统一网段
if((maskNum & ip1Num) == (maskNum & ip2Num)){
System.out.println(0);
return;
}
System.out.println(2);
return;
}
//校验子网掩码
public static boolean checkFormat(String mask){
String[] ss = mask.split("\\.");
int[] m = new int[ss.length];
for(int i = 0; i < ss.length; i++){
m[i] = Integer.parseInt(ss[i]);
}
return m[0] >= 0 && m[0] <= 255 &&
m[1] >= 0 && m[1] <= 255 &&
m[2] >= 0 && m[2] <= 255 &&
m[3] >= 0 && m[3] <= 255 &&
m[0] >= m[1] && m[1] >= m[2] && m[2] >= m[3];
}
//校验合法ip
public static boolean checkFormatIP(String str){
String[] ss = str.split("\\.");
for(String s : ss){
int num = Integer.parseInt(s);
if(!(num >= 0 && num <= 255)){
return false;
}
}
return true;
}
//为校验同一网段 ip转大数
public static long ip2num(String str){
String[] ss = str.split("\\.");
long sum = 0L;
long mul = 1L;
for(int i = ss.length - 1; i >= 0; i--){
long seg = Long.parseLong(ss[i]);
sum += seg * mul;
mul *= 256;
}
return sum;
}
}
学英语
import java.util.Scanner;
public class Main {
private static String[] ones = new String[]{
"zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine",
"ten", "eleven", "twelve", "thirteen", "forteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen", "twenty"};
private static String[] twieties = new String[]{"zero", "ten", "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety"};
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
while (sc.hasNext()) {
String str = sc.nextLine();
if (str.length() <= 9) {
System.out.println(getEn(Integer.parseInt(str)));
} else {
System.out.println("error");
}
}
sc.close();
}
private static String getEn(int num) {
String stren = "error";
if (num <= 20) {
stren = ones[num];
} else if (num < 100) {
int remainder = num % 10;
if (remainder == 0) {
stren = twieties[num / 10];
} else {
stren = twieties[num / 10] + " " + ones[remainder];
}
} else if (num < 1000) {
int remainder = num % 100;
if (remainder == 0) {
stren = ones[num / 100] + " hundred";
} else {
stren = ones[num / 100] + " hundred and " + getEn(remainder);
}
} else if (num < 1000000) {
int remainder = num % 1000;
if (remainder == 0) {
stren = getEn(num / 1000) + " thousand";
} else {
stren = getEn(num / 1000) + " thousand " + getEn(remainder);
}
} else if (num < 1000000000) {
int remainder = num % 1000000;
if (remainder == 0) {
stren = getEn(num / 1000000) + " million";
} else {
stren = getEn(num / 1000000) + " million " + getEn(remainder);
}
}
return stren;
}
}
人民币转换
import java.util.Scanner;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
public static String[] ten = {"零","壹","贰","叁","肆","伍","陆","柒","捌","玖"};
public static String[] power = {"万","亿"};
public static String[] daiwei = {"元","角","分","整"};
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
// 注意 hasNext 和 hasNextLine 的区别
while (in.hasNextLine()) { // 注意 while 处理多个 case
String[] s = in.nextLine().split("\\.");//分割为整数部分和小数部分
if(s[1].equals("00")){
System.out.println("人民币" + solveZheng(Double.parseDouble(s[0])) + "元整");
}else if(s[0].equals("0")){
System.out.println("人民币" + solveXiao(s[1]));
}else{
System.out.println("人民币" + solveZheng(Double.parseDouble(s[0])) + "元" + solveXiao(s[1]));
}
}
}
public static String solveXiao(String s2){
StringBuilder sb = new StringBuilder();
int jiao = Integer.parseInt(s2.substring(0,1));
int fen = Integer.parseInt(s2.substring(1,2));
if(jiao!=0){
sb.append(ten[jiao]);
sb.append("角");
}
if(fen!=0){
sb.append(ten[fen]);
sb.append("分");
}
return sb.toString();
}
public static String solveZheng(double zheng){
StringBuilder sb = new StringBuilder();
int pow = 0;
while((int)zheng != 0){
if(pow!=0){
sb.append(power[pow-1]);
}
int temp = (int)(zheng % 10000);
//个位
int gewei = temp % 10;
int shiwei = (temp / 10) % 10;
int baiwei = (temp/100) % 10;
int qianwei = (temp/1000) % 10;
if(gewei!=0){
sb.append(ten[gewei]);
}
//十位
if(shiwei!=0){
sb.append("拾");
if(shiwei!=1){
sb.append(ten[shiwei]);
}
}else{
if(gewei != 0 && (temp>99 || (int)zheng > 10000)){
sb.append(ten[0]);
}
}
//百位
if(baiwei!=0){
sb.append("佰");
sb.append(ten[baiwei]);
}else{
if(shiwei != 0 && (temp>999 || (int)zheng > 10000)){
sb.append(ten[0]);
}
}
if(qianwei!=0){
sb.append("仟");
sb.append(ten[qianwei]);
}else{
if(baiwei != 0 && (int)zheng > 10000){
sb.append(ten[0]);//
}
}
zheng /= 10000;
pow++;
if(pow>2){
pow=1;
}
}
return sb.reverse().toString();
}
}
迷宫问题
二维数组+深度优先路径搜索+递归+回溯+自定义位置
import java.util.*;
// 题目已经提示了 【迷宫只有一条通道】,则直接使用 DFS 找路径就行了,如不有多条路径找最短考虑使用 BFS
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
while (in.hasNextInt()) { // 注意 while 处理多个 case
int n = in.nextInt();
int m = in.nextInt();
// 构造迷宫
int[][] map = new int[n][m];
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
map[i][j] = in.nextInt();
}
}
// 路径存储的数组
List<Pos> path = new ArrayList<>();
// DFS 搜索路径
dfs(map, 0, 0, path);
// 输出
for (Pos p : path) {
System.out.println("(" + p.x + "," + p.y + ")");
}
}
}
// 返回值 标记是否找到可通行的路劲
public static boolean dfs(int[][] map, int x, int y, List<Pos> path) {
// 添加路径并标记已走
path.add(new Pos(x, y));
map[x][y] = 1;
// 结束标志
if (x == map.length - 1 && y == map[0].length - 1) {
return true;
}
// 向下能走时
if (x + 1 < map.length && map[x + 1][y] == 0) {
if (dfs(map, x + 1, y, path)) {
return true;
}
}
// 向右能走时
if (y + 1 < map[0].length && map[x][y + 1] == 0) {
if (dfs(map, x, y + 1, path)) {
return true;
}
}
// 向上能走时
if (x - 1 > -1 && map[x - 1][y] == 0) {
if (dfs(map, x - 1, y, path)) {
return true;
}
}
// 向左能走时
if (y - 1 > -1 && map[x][y - 1] == 0) {
if (dfs(map, x, y - 1, path)) {
return true;
}
}
// 回溯
path.remove(path.size() - 1);
map[x][y] = 0;
return false;
}
// 简单的位置类
public static class Pos {
int x;
int y;
public Pos(int x, int y) {
this.x = x;
this.y = y;
}
}
}
计算字符串的编辑距离
import java.util.*;
public class Main{
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
while(sc.hasNext()){
String s1 = sc.next();
String s2 = sc.next();
int dp[][] = new int[s1.length()+1][s2.length()+1];
dp[0][0] = 0;
for(int i = 1;i<dp.length;i++){
dp[i][0] = i;
}
for(int i = 1;i<dp[0].length;i++){
dp[0][i] = i;
}
for(int i = 1;i<dp.length;i++){
for(int j = 1;j<dp[0].length;j++){
if(s1.charAt(i-1)==s2.charAt(j-1))
{
dp[i][j] = dp[i-1][j-1];
} else {
dp[i][j] = Math.min
(dp[i-1][j-1]+1,Math.min(dp[i][j-1]+1,dp[i-1][j]+1));
}
}
}
System.out.println(dp[s1.length()][s2.length()]);
}
}
}
24点游戏算法
import java.util.*;
import java.io.*;
public class Main{
static int[] nums = new int[4];
static boolean[] visit = new boolean[4];
static int flag = 0;
public static void main(String[] args){
Scanner in = new Scanner(System.in);
while(in.hasNext()){
String[] a = in.nextLine().split(" ");
for(int i = 0; i < 4; i ++)
nums[i] = Integer.parseInt(a[i]);
dfs(0, 0);
System.out.println( flag == 1 );
}
}
// tmp是前面n个数字运算结果,u表示已经使用了多少个数字
static boolean dfs(int u,float tmp){
// 递归终止条件:已经使用了数组四个元素,同时结果为24,满足题意
if(tmp == 24 && u == 4){
flag = 1;
return true;
}
for(int i = 0; i < 4; i ++){
if(visit[i] == false){
visit[i] = true;
// 加减乘除当前数字num[i]
if( dfs(u + 1, tmp + nums[i]) ||
dfs(u + 1, tmp - nums[i]) ||
dfs(u + 1,tmp * nums[i]) ||
dfs(u + 1, tmp / nums[i])){
return true;
}
// 相当于回溯
visit[i] = false;
}
}
return false;
}
}
瑞年
public static boolean is_leap(int n){
if(n % 4 == 0 && n % 100 != 0 || n % 400 == 0){
return true;
}else{
return false;
}
}
火车进站
import java.util.*;
// 思路:主要思想是递归,之所以产生很多方案的原因就是,每次进来一辆火车后,我们将其压入栈中,然后我们可以有两种选择,一是不弹出,二是弹出;
// 对于第二种弹出元素,弹出的个数的范围取决于当前栈中元素的个数,所以遍历每种情况,在遍历每种情况的时候再递归到下一辆火车
public class Main {
static Deque<Integer> stack;
static int[] nums, outNums;
static int n;
static List<String> res;
public static void main(String[] args) {
// 完成输入
Scanner in = new Scanner(System.in);
n = in.nextInt();
nums = new int[n];
for (int i=0; i<n; i++) nums[i] = in.nextInt();
// outNums是用来记录当前递归中,已经出站的火车编号; stack是记录当前车站中还有哪些火车;
// res是记录所有结果并进行排序,因为题目要求所有方案以字典序排序输出,所以没办法必须加
outNums = new int[n];
stack = new LinkedList<>();
res = new ArrayList<>();
// 开始遍历
dfs(0, 0);
// 对所有方案排序,并输出
Collections.sort(res);
for (String str:res) System.out.println(str);
}
// i代表已经递归到了第i辆火车,cnt代表已经已经出站的火车数量,即是outNums的下标
public static void dfs(int i, int cnt) {
// 这个tmp栈很重要,这是保证dfs返回时,stack中的元素和进来时一样
Deque<Integer> tmp = new LinkedList<>();
// 压入当前火车
stack.push(nums[i]);
// 当递归到最后一辆火车时,我们只需要将其压入栈中,这时所能做的只有弹出栈中所有元素,并将其添加到outNums数组中
if (i==n-1) {
// 弹出栈中所有元素
while (stack.size() > 0) {
tmp.push(stack.peek());
outNums[cnt++] = stack.pop();
}
// 这里>1是因为stack中本身不含有nums[i]
while (tmp.size() > 1) stack.push(tmp.pop());
// 将当前方案以字符串形式保存到res中
StringBuilder sb = new StringBuilder();
for (int outNum:outNums) sb.append(outNum).append(" ");
res.add(sb.toString());
}
// 如果没有递归到最后一辆火车,那么在将当前火车编号压入栈后,有很多选择,这也就产生了很多方案
// 一种就是继续压;还有一种就是开始弹出元素,弹出元素个数范围是[0, size](包含两边界),那么就需要依次遍历
else {
int size = stack.size();
// 继续压
dfs(i+1, cnt);
// 开始弹出元素
for (int j=0; j<size; j++) {
tmp.push(stack.peek());
outNums[cnt++] = stack.pop();
dfs(i+1, cnt);
}
// 这里>1是因为stack中本身不含有nums[i],
while (tmp.size() > 1) stack.push(tmp.pop());
}
}
}
求解立方根
import java.util.Scanner;
public class Main{
public static void main(String[] args){
Scanner input = new Scanner(System.in);
while (input.hasNextDouble()){
double num = input.nextDouble();
double x = 1.0;
for (; Math.abs(Math.pow(x,3)-num)>1e-3; x=x-((Math.pow(x,3)-num)/(3*Math.pow(x,2))));
System.out.println(String.format("%.1f", x));
}
}
}
求最小公倍数
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner in=new Scanner(System.in);
int a=in.nextInt();
int b=in.nextInt();
System.out.println(a*b/gcd(a,b));
}
private static int gcd(int a, int b) {
return b==0?a:gcd(b,a%b);
}
}
判断质数
public static boolean isSushu(int c){
if(c==1) return false;//1不是质数
for(int i=2;i<=(int)Math.sqrt(c);i++){
if(c%i==0){
return false;
}
}
return true;
}
//用这个!
private boolean isPrime(int num) {
for (int i = 2; i <= num/i; i++) {
if (num % i == 0) return false;
}
return true;
}
回溯法
import java.util.Scanner;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int[][] board = new int[9][9];
for(int i=0;i<9;i++){
for(int j=0;j<9;j++){
board[i][j] = in.nextInt();
}
}
solveSudoku(board);
//输出二维矩阵
for(int i=0;i<9;i++){
for(int j=0;j<8;j++){
System.out.print(board[i][j] + " ");
}
System.out.println(board[i][8]);//换行,每一行的最后一个数字
}
}
public static boolean solveSudoku(int[][] board){
//「一个for循环遍历棋盘的行,一个for循环遍历棋盘的列,
// 一行一***定下来之后,递归遍历这个位置放9个数字的可能性!」
for (int i = 0; i < 9; i++){ // 遍历行
for (int j = 0; j < 9; j++){ // 遍历列
if (board[i][j] != 0){ // 跳过原始数字
continue;
}
for (int k = 1; k <= 9; k++){ // (i, j) 这个位置放k是否合适
if (isValidSudoku(i, j, k, board)){
board[i][j] = k;//将k放在(i,j)
if (solveSudoku(board)){ // 如果找到合适一组立刻返回
return true;
}
board[i][j] = 0;//回溯
}
}
// 9个数都试完了,都不行,那么就返回false
return false;
// 因为如果一行一***定下来了,这里尝试了9个数都不行,说明这个棋盘找不到解决数独问题的解!
// 那么会直接返回, 「这也就是为什么没有终止条件也不会永远填不满棋盘而无限递归下去!」
}
}
// 遍历完没有返回false,说明找到了合适棋盘位置了
return true;
}
/**
* 判断棋盘是否合法有如下三个维度:
* 同行是否重复
* 同列是否重复
* 9宫格里是否重复
*/
public static boolean isValidSudoku(int row, int col, int val, int[][] board){
// 同行是否重复
for (int i = 0; i < 9; i++){
if (board[row][i] == val){
return false;
}
}
// 同列是否重复
for (int j = 0; j < 9; j++){
if (board[j][col] == val){
return false;
}
}
// 9宫格里是否重复
int startRow = (row / 3) * 3;
int startCol = (col / 3) * 3;
for (int i = startRow; i < startRow + 3; i++){
for (int j = startCol; j < startCol + 3; j++){
if (board[i][j] == val){
return false;
}
}
}
return true;
}
}
苹果放到盘子的方案
// 递归功能:当前持有m个苹果,有n个盘子可供存放,返回摆放方案数
private static int count(int m, int n) {
// 递归出口:当苹果数m=0, 此时表示什么都不做,输出1种方案,再递归下去m<0,题目要求m>=0
// 当盘子只有一个时,剩下的苹果m只能全部摆放这个盘子,递归返回1种方案,再递归下去n==0, 题目要求n>=1
if(m == 0 || n == 1) {
return 1;
}
// 当盘子数大于苹果数,一定有n-m个盘子空着,而且每个盘子都一样,因此count(m,n)==count(m,n-1)
if(n > m) {
return count(m, n-1);
} else {
// 当盘子数<=苹果数,有两种情况:
// 1.至少有一个盘子可以不放,因此count(m, n-1)
// 2.至少每个盘子都有一个苹果,摆放后苹果数为(m-n),因此coutn(m-n, n)
return count(m, n - 1) + count(m - n, n);//加号两边的集合拼成一个全集
}
}
//实际上是一种方案拆分的思想
//更精简,帅啊!
public static int count(int m,int n){
if(m<0||n<=0)
return 0;
//细分到苹果数为一或盘子数为一的情况返回一
if(m==1||n==1||m==0)
return 1;
//将此事件无线细分
return count(m,n-1)+count(m-n,n);
}
数组
乾坤大挪移
不借助外力,挪动数组的元素
import java.util.*;
public class Solution {
/**
* 旋转数组 不允许使用另外数组的前提下
* @param n int整型 数组长度
* @param m int整型 右移距离
* @param a int整型一维数组 给定数组
* @return int整型一维数组
*/
public int[] solve (int n, int m, int[] a) {
for(m = m%n; m>0; m--){
for(int i=n-1;i>0;i--){
swap(a,i,i-1);
}
}
return a;
}
public void swap(int[] nums,int a,int b){
int temp = nums[a];
nums[a] = nums[b];
nums[b] = temp;
}
}
螺旋矩阵
import java.util.*;
public class Solution {
public ArrayList<Integer> spiralOrder(int[][] matrix) {
ArrayList<Integer> list = new ArrayList<>();
if(matrix.length == 0) {
return list;
}
int left = 0;
int right = matrix[0].length - 1;
int top = 0;
int bottom = matrix.length - 1;
int x = 0;
while(true) {
//从左到右
for(int i = left; i <= right; i++) {list.add(matrix[top][i]);}
if(++top > bottom){break;}
//从上到下
for(int i = top; i <= bottom; i++){list.add( matrix[i][right]);}
if(left > --right){break;}
//从右到左
for(int i = right; i >= left; i--){list.add(matrix[bottom][i]);}
if(top > --bottom){break;}
//从下到上
for(int i = bottom; i >= top; i--){list.add(matrix[i][left]);}
if(++left > right){break;}
}
return list;
}
}
顺时针旋转矩阵
import java.util.*;
public class Solution {
public int[][] rotateMatrix2(int[][] mat, int n) {
int[][] uu = new int[n][n];
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
uu[j][i]=mat[n-1-i][j];/*找规律就能看出来了*/
}
}
return uu;
}
public int[][] rotateMatrix(int[][] matrix, int n) {
int length = matrix.length;
//先上下交换
for (int i = 0; i < length / 2; i++) {
int temp[] = matrix[i];
matrix[i] = matrix[length - i - 1];
matrix[length - i - 1] = temp;
}
//在按照对角线交换
for (int i = 0; i < length; ++i) {
for (int j = i + 1; j < length; ++j) {
int temp = matrix[i][j];
matrix[i][j] = matrix[j][i];
matrix[j][i] = temp;
}
}
return matrix;
}
}
内存数据淘汰策略
*最近最少使用
LRU(Least recently used),最近最少使用
import java.util.*;
public class Solution {
private int capacity;
private Map<Integer, Node> map;
private Node head;
private Node tail;
private int used;
class Node {
int key;
int value;
Node prev;
Node next;
Node(int key, int value, Node prev, Node next) {
this.key = key;
this.value = value;
this.prev = prev;
this.next = next;
}
}
public Solution(int capacity) {
this.capacity = capacity;
this.map = new HashMap<>();
this.used = 0;
}
public int get(int key) {
if (!map.containsKey(key)) {
return -1;
}
makeRecently(key);
return map.get(key).value;
}
public void set(int key, int value) {
// 如果 key 已存在,直接修改值,再移到链表头部
if (map.containsKey(key)) {
map.get(key).value = value;
makeRecently(key);
return;
}
// 如果达到容量上限,就要移除尾部节点,注意 HashMap 要 remove!!!
if (used == capacity) {
map.remove(tail.key);
tail = tail.prev;
tail.next = null;
used--;
}
// 头节点为空,单独处理
if (head == null) {
head = new Node(key, value, null, null);
tail = head;
} else {
Node t = new Node(key, value, null, head);
head.prev = t;
head = t;
}
map.put(key, head);
used++;
}
// 把 key 对应的节点移到链表头部
private void makeRecently(int key) {
Node t = map.get(key);
if (t != head) {
if (t == tail) {
tail = tail.prev;
tail.next = null;
} else {
t.prev.next = t.next;
t.next.prev = t.prev;
}
t.prev = null;
t.next = head;
head.prev = t;
head = t;
}
}
}
*最不经常使用
LFU(Least Frequently Used),最不经常使用页置换算法
import java.util.*;
public class Solution {
/**
* lfu design
* @param operators int整型二维数组 ops
* @param k int整型 the k
* @return int整型一维数组
*/
class Lfu{
int key,val,frequency;
public Lfu(int key,int val){
this.key=key;
this.val=val;
this.frequency=0;
}
}
int fre=0;
int minFre=1;
Map<Integer,Lfu> valueMap=new HashMap<>();
Map<Integer,LinkedList<Lfu>>freMap=new HashMap<>();
public void updateFre(Lfu lfu){
if(freMap.containsKey(lfu.frequency)){
LinkedList tmp=freMap.get(lfu.frequency);
tmp.remove(lfu);
if(tmp.isEmpty()&&lfu.frequency==minFre){
minFre++;
}
}
lfu.frequency++;
if(lfu.frequency==1){
minFre=1;
}
if(freMap.containsKey(lfu.frequency)){
freMap.get(lfu.frequency).addFirst(lfu);
} else{
freMap.put(lfu.frequency,new LinkedList<Lfu>());
freMap.get(lfu.frequency).addFirst(lfu);
}
}
public int get(int key){
if(!valueMap.containsKey(key)){
return -1;
}
Lfu tmp=valueMap.get(key);
updateFre(tmp);
return tmp.val;
}
public void put(Lfu lfu,int k){
int key=lfu.key;
int val=lfu.val;
if(valueMap.containsKey(key)){
Lfu tmp=valueMap.get(key);
tmp.val=val;
updateFre(tmp);
}else{
if(valueMap.size()<k){
;
} else{
Lfu tmp=freMap.get(minFre).removeLast();
valueMap.remove(tmp.key);
}
valueMap.put(key,lfu);
updateFre(valueMap.get(key));
}
}
public int[] LFU (int[][] operators, int k) {
int n=operators.length;
List<Integer> res=new ArrayList<>();
for(int i=0;i<n;i++){
if(operators[i][0]==1){
put(new Lfu(operators[i][1],operators[i][2]),k);
} else{
res.add(get(operators[i][1]));
}
}
int[]result=new int[res.size()];
for(int i=0;i<res.size();i++){
result[i]=res.get(i);
}
return result;
}
}
贪心算法
分糖果
import java.util.*;
public class Solution {
/**
* pick candy
* @param arr int整型一维数组 the array
* @return int整型
*/
public int candy (int[] arr) {
Boolean changed = false;
int res = 0;
int[] candyCount = new int[arr.length];
for (int i = 0; i < arr.length; i++) {
candyCount[i] = 1;
}
do {
changed = false;
for (int i = 0; i < arr.length - 1; i++) {
if (arr[i] == arr[i + 1]) {
continue;
} else if (arr[i] > arr[i + 1] && candyCount[i] <= candyCount[i + 1]) {
candyCount[i]++;
changed = true;
} else if (arr[i] < arr[i + 1] && candyCount[i] >= candyCount[i + 1]) {
candyCount[i + 1]++;
changed = true;
}
}
} while (changed);
for (int i = 0; i < arr.length; i++) {
res += candyCount[i];
}
return res;
}
}
主持人调度
import java.util.*;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
* 计算成功举办活动需要多少名主持人
* @param n int整型 有n个活动
* @param startEnd int整型二维数组 startEnd[i][0]用于表示第i个活动的开始时间,startEnd[i][1]表示第i个活动的结束时间
* @return int整型
*/
public int minmumNumberOfHost (int n, int[][] startEnd) {
//初始化两个数组,分别记录开始时间和结束时间
int[] start=new int[n];
int[] end=new int[n];
//将活动的开始和结束时间赋值道start和end数组
for(int i=0;i<n;i++){
start[i]=startEnd[i][0];
end[i]=startEnd[i][1];
}
//按从小到大的顺序对start和end数组排序
Arrays.sort(start);
Arrays.sort(end);
int res=0,index=0;
for(int i=0;i<n;i++){
//如果大于等于当前最小的结束时间,说明当前主持人可以搞定
if(start[i]>=end[index]){
index++;
}
//否则,需要新增主持人
else{
res++;
}
}
return res;
}
}
双指针
合并两个有序的数组
import java.util.*;
public class Solution {
public void merge(int A[], int m, int B[], int n) {
int p1 = 0, p2 = 0;
//新开一个M+n大小的数组
int[] sorted = new int[m + n];
int cur;
//循环选择
while (p1 < m || p2 < n) {
if (p1 == m) {
cur = B[p2++];
} else if (p2 == n) {
cur = A[p1++];
} else if (A[p1] < B[p2]) {
cur = A[p1++];
} else {
cur = B[p2++];
}
sorted[p1 + p2 - 1] = cur;
}
//移动
for (int i = 0; i != m + n; ++i) {
A[i] = sorted[i];
}
}
}
回文字符串
import java.util.*;
public class Solution {
public boolean judge (String str) {
// 判断特殊情况
if (str == null || str.length() == 0) return false;
// 定义双指针,不相同则不是回文串
for (int i = 0, j = str.length()-1; i < j; i++, j--)
if (str.charAt(i) != str.charAt(j)) return false;
return true;
}
}
合并区间
import java.util.*;
/**
* Definition for an interval.
* public class Interval {
* int start;
* int end;
* Interval() { start = 0; end = 0; }
* Interval(int s, int e) { start = s; end = e; }
* }
*/
public class Solution {
public ArrayList<Interval> merge(ArrayList<Interval> intervals) {
Collections.sort(intervals, (v1,v2) ->v1.start - v2.start);
ArrayList<Interval> result = new ArrayList<Interval>();
int index = -1;
for(Interval interval :intervals){
if(index == -1 ||interval.start >result.get(index).end){
// 区间 [a,b] [c,d] c 大于 b的话
//不合并
result.add(interval);
index ++ ;
}else{
//如果 c 大于 b 那么就需要找一个最大的作为 右边的边界 因为数据start都从左到右排好序了
result.get(index).end = Math.max(interval.end, result.get(index).end);
}
}
return result;
}
}
最小覆盖子串
import java.util.*;
public class Solution {
/**
* @param S string字符串
* @param T string字符串
* @return string字符串
*/
public String minWindow (String S, String T) {
//n为S的长度,m为T的长度
int n = S.length();
int m = T.length();
//记录子串长度
int len = 10001;
//记录子串起始点
int start = 0;
//统计T中字符出现次数
int[] map = new int[128];
for (int i = 0; i < m; i++) {
map[T.charAt(i)]++;
}
//滑动窗口的左右边界l和r,以及待消除的字符数量count
int l = 0, r = 0, count = m;
while (r < n) {
//如果出现次数大于0,说明需要消除,count减1
if (map[S.charAt(r++)]-- > 0) {
count--;
}
//只要count为0,说明当前子串合法
while (count == 0) {
//如果长度小于len,则更新len和start
if (len > r - l) {
len = r - l;
start = l;
}
//如果左边界字符出现次数为0(要么小于0,要么等于0),则需要重新消除,后移l
if (map[S.charAt(l++)]++ == 0) {
//后移之后,count需加1
count++;
}
}
}
return len == 10001 ? "" : S.substring(start, start + len);
}
}
最长无重复子数组
import java.util.*;
public class Solution {
/**
* @param arr int整型一维数组 the array
* @return int整型
*/
public int maxLength(int[] arr) {
if (arr.length == 0)
return 0;
HashMap<Integer, Integer> map = new HashMap<>();
int max = 0;
for (int i = 0, j = 0; i < arr.length; ++i) {
if (map.containsKey(arr[i])) {
j = Math.max(j, map.get(arr[i]) + 1);
}
map.put(arr[i], i);
max = Math.max(max, i - j + 1);
}
return max;
}
public int maxLength2(int[] arr) {
//用链表实现队列,队列是先进先出的
Queue<Integer> queue = new LinkedList<>();
int res = 0;
for (int c : arr) {
while (queue.contains(c)) {
//如果有重复的,队头出队
queue.poll();
}
//添加到队尾
queue.add(c);
res = Math.max(res, queue.size());
}
return res;
}
public int maxLength3(int[] arr) {
int maxLen = 0;
Set<Integer> set = new HashSet<>();
int left = 0, right = 0;
while (right < arr.length) {
while (set.contains(arr[right]))
set.remove(arr[left++]);
set.add(arr[right++]);
maxLen = Math.max(maxLen, right - left);
}
return maxLen;
}
public int maxLength4(int[] arr) {
int left = 0, right = 0, max = 0;
Set<Integer> set = new HashSet<>();
while (right < arr.length) {
if (set.contains(arr[right])) {
set.remove(arr[left++]);
} else {
set.add(arr[right++]);
max = Math.max(max, set.size());
}
}
return max;
}
}
盛水最多的容器
import java.util.*;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
* @param height int整型一维数组
* @return int整型
*/
public int maxArea (int[] height) {
//获取数组长度,左右指针分别指向开头和结尾,把最大值初始为0
int len = height.length, l = 0, r = len - 1, maxx = 0;
//执行循环
while (l < r) {
//每次对最大值进行一个更新
maxx = Math.max(maxx, Math.min(height[l], height[r]) * (r - l));
//每次我们把数组值小的那边的指针移动
if (height[l] < height[r])
l += 1;
else
r -= 1;
}
return maxx;
}
}
接雨水
import java.util.*;
public class Solution {
/**
* max water
* @param arr int整型一维数组 the array
* @return long长整型
*/
public long maxWater(int[] arr) {
int left = 0, right = arr.length - 1, bucketHeight = 0;
long water = 0;
while (left < right) {
//取height[left]和height[right]的最小值
int minHeight = Math.min(arr[left], arr[right]);
//如果最小值minHeight大于桶的高度bucketHeight,要更新桶的高度到minHeight
bucketHeight = bucketHeight < minHeight ? minHeight : bucketHeight;
water += arr[left] >= arr[right] ? (bucketHeight - arr[right--]) :
(bucketHeight - arr[left++]);
}
return water;
}
}
动态规划
青蛙跳台阶
本质是斐波那契
public class Solution {
public int jumpFloor(int target) {
if(target == 1){
return 1;
}
if(target == 2){
return 2;
}
return jumpFloor(target-1)+jumpFloor(target-2);
}
}
//如果要求效率,可用递推公式
public int jumpFloor(int target) {
double sqrt = Math.sqrt(5);
return (int) ((Math.pow((1 + sqrt) / 2, target + 1) - Math.pow((1 - sqrt) / 2, target + 1)) / sqrt);
}
//非递归法
public int jumpFloor(int target) {
if (target <= 2)
return target;
int first = 1, second = 2, sum = 0;
while (target-- > 2) {
sum = first + second;
first = second;
second = sum;
}
return sum;
}
//或者非递归法
public int jumpFloor(int target) {
if (target <= 1)
return 1;
int[] dp = new int[target + 1];
dp[1] = 1;
dp[2] = 2;
for (int i = 3; i <= target; i++) {
dp[i] = dp[i - 1] + dp[i - 2];
}
return dp[target];
}
最长公共子序列
"1A2C3D4B56","B1D23A456A"
"123456"
import java.util.*;
public class Solution {
/**
* longest common subsequence
* @param s1 string字符串 the string
* @param s2 string字符串 the string
* @return string字符串
*/
public String LCS (String s1, String s2) {
// write code here
int n = s1.length();
int m = s2.length();
int [][]dp = new int[n + 1][m + 1];
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (s1.charAt(i - 1) == s2.charAt(j - 1)) {
dp[i][j] = dp[i - 1][j - 1] + 1;
} else {
dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
}
}
}
if (dp[n][m] == 0)return "-1";
StringBuilder sb = new StringBuilder();
while (n > 0 && m > 0) {
if (s1.charAt(n - 1) == s2.charAt(m - 1)) {
sb.append(s1.charAt(n - 1));
n--;
m--;
} else {
if (dp[n - 1][m] > dp[n][m - 1]) {
n--;
} else {
m--;
}
}
}
return sb.reverse().toString();
}
}
最长公共子串
"1AB2345CD","12345EF"
"2345"
import java.util.*;
public class Solution {
/**
* longest common substring
* @param str1 string字符串 the string
* @param str2 string字符串 the string
* @return string字符串
*/
public String LCS(String str1, String str2) {
int maxLenth = 0;//记录最长公共子串的长度
//记录最长公共子串最后一个元素在字符串str1中的位置
int maxLastIndex = 0;
int[] dp = new int[str2.length() + 1];
for (int i = 0; i < str1.length(); i++) {
//注意这里是倒叙
for (int j = str2.length() - 1; j >= 0; j--) {
//递推公式,两个字符相等的情况
if (str1.charAt(i) == str2.charAt(j)) {
dp[j + 1] = dp[j] + 1;
//如果遇到了更长的子串,要更新,记录最长子串的长度,
//以及最长子串最后一个元素的位置
if (dp[j + 1] > maxLenth) {
maxLenth = dp[j + 1];
maxLastIndex = i;
}
} else {
//递推公式,两个字符不相等的情况
dp[j + 1] = 0;
}
}
}
//最字符串进行截取,substring(a,b)中a和b分别表示截取的开始和结束位置
return str1.substring(maxLastIndex - maxLenth + 1, maxLastIndex + 1);
}
public String LCS2(String str1, String str2) {
int maxLenth = 0;//记录最长公共子串的长度
//记录最长公共子串最后一个元素在字符串str1中的位置
int maxLastIndex = 0;
int[][] dp = new int[str1.length() + 1][str2.length() + 1];
for (int i = 0; i < str1.length(); i++) {
for (int j = 0; j < str2.length(); j++) {
//递推公式,两个字符相等的情况
if (str1.charAt(i) == str2.charAt(j)) {
dp[i + 1][j + 1] = dp[i][j] + 1;
//如果遇到了更长的子串,要更新,记录最长子串的长度,
//以及最长子串最后一个元素的位置
if (dp[i + 1][j + 1] > maxLenth) {
maxLenth = dp[i + 1][j + 1];
maxLastIndex = i;
}
} else {
//递推公式,两个字符不相等的情况
dp[i + 1][j + 1] = 0;
}
}
}
//最字符串进行截取,substring(a,b)中a和b分别表示截取的开始和结束位置
return str1.substring(maxLastIndex - maxLenth + 1, maxLastIndex + 1);
}
}
不同路径的数目
import java.util.*;
public class Solution {
/**
* @param m int整型
* @param n int整型
* @return int整型
*/
public int uniquePaths (int m, int n) {
// 起点(1,1) 这里为什么是(1,1)呢?其实是一样的,我们上面的方法定义了(0,0)为起点,那么终点就为(m-1,n-1)
// 这里起点为(1,1)那么终点就为 (m,n)
if (m == 1 || n == 1)
return 1;
// 终点(m,n)
return uniquePaths(m, n - 1) + uniquePaths(m - 1, n);
}
}
矩阵的最小路径和
import java.util.*;
public class Solution {
/**
*把计算过的值存储到一个map中,下次计算的时候先看map中是否有,如果有就直接从map中取,如果没有再计算,计算之后再把结果放到map中
* @param matrix int整型二维数组 the matrix
* @return int整型
*/
public int minPathSum(int[][] matrix) {
return minPathSum(matrix, matrix.length - 1, matrix[0].length - 1,
new HashMap<String, Integer>());
}
public int minPathSum(int[][] grid, int i, int j, Map<String, Integer> map) {
if (i == 0 && j == 0)
return grid[i][j];
String key = i + "*" + j;
if (map.containsKey(key))
return map.get(key);
int res = 0;
//第一行只能从左边走过来
if (i == 0)
res = grid[i][j] + minPathSum(grid, i, j - 1, map);
//第一列只能从上面走下来
else if (j == 0)
res = grid[i][j] + minPathSum(grid, i - 1, j, map);
//取从上面走下来和从左边走过来的最小值+当前坐标的值
else
res = grid[i][j] + Math.min(minPathSum(grid, i - 1, j, map), minPathSum(grid, i,
j - 1, map));
map.put(key, res);
return res;
}
}
把数字翻译成字符串
有一种将字母编码成数字的方式:'a'->1, 'b->2', ... , 'z->26'。
import java.util.*;
public class Solution {
/**
* 解码
* @param nums string字符串 数字串
* @return int整型
*/
public int solve (String nums) {
if(nums==null ||nums.length()==0) return 0;
int[] dp = new int[nums.length()+1];
dp[0]=1;
dp[1]=nums.charAt(0)=='0'?0:1;
for(int i=2;i<dp.length;i++){
//无法独立编码也无法组合编码
if(nums.charAt(i-1)=='0' && (nums.charAt(i-2)=='0' || nums.charAt(i-2)>'2')){
return 0;
//只能组合编码
}else if(nums.charAt(i-1)=='0'){
dp[i] = dp[i-2];
//只能独立编码
}else if(nums.charAt(i-2)=='0' || nums.charAt(i-2)>'2' || nums.charAt(i-2)=='2'&& nums.charAt(i-1)>'6' ){
dp[i] = dp[i-1];
//两种编码方式都可以
}else{
dp[i] = dp[i-1]+dp[i-2];
}
}
return dp[nums.length()];
}
}
兑换零钱
import java.util.*;
public class Solution {
/**
* 最少货币数
* @param arr int整型一维数组 the array
* @param aim int整型 the target
* @return int整型
*/
public int minMoney (int[] arr, int aim) {
// write code here
//如何使用最少arr元素 构成 aim值
//dp[i] 代表给定钱数为i的时候最少货币数 就是凑成 i 元钱,需要dp[i] 张arr中面值纸币
//没办法兑换 arr[i] dp[i] = dp[i]
//可以dp[i] = dp[i - arr[i]] + 1
//dp[i] = min(dp[i], dp[i-a[j]])
if (arr == null || arr.length == 0) {
return -1;
}
int[] dp = new int[aim + 1];
for (int i = 0; i <= aim; i++) {
dp[i] = aim + 1;
}
dp[0] = 0;
for (int i = 1; i <= aim; i++) {
for (int j = 0; j < arr.length; j++) {
if (arr[j] <= i) {
//给了一张 3 元钱,小于 需要找零的4 元钱,那 就等于 1 + 需要找零剩下的钱dp[i -arr[j]] 4 - 3
dp[i] = Math.min(dp[i], dp[i - arr[j]] + 1);
}
}
}
return (dp[aim] > aim) ? -1 : dp[aim];
}
}
最长上升子序列
import java.util.*;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
* 给定数组的最长严格上升子序列的长度。
* @param arr int整型一维数组 给定的数组
* @return int整型
*/
public int LIS (int[] arr) {
int n = arr.length;
//特殊请款判断
if (n == 0) return 0;
//dp[i]表示以下标i结尾的最长上升子序列长度
int[] dp = new int[n];
for (int i = 0; i < n; i++) {
//初始化为1
dp[i] = 1;
for (int j = 0; j < i; j++) {
if (arr[i] > arr[j]) {
//只要前面某个数小于当前数,则要么长度在之前基础上加1,要么保持不变,取较大者
dp[i] = Math.max(dp[i], dp[j] + 1);
}
}
}
//返回所有可能中的最大值
return Arrays.stream(dp).max().getAsInt();
}
}
//二分优化
import java.util.*;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
* 给定数组的最长严格上升子序列的长度。
* @param arr int整型一维数组 给定的数组
* @return int整型
*/
public int LIS (int[] arr) {
int n=arr.length;
//特殊情况判断
if(n==0) return 0;
//维护一个单调递增的数组
int[] tail=new int[n];
//指向tail数组的最后一位
int end=-1;
for(int i=0;i<n;i++){
//如果数组为空或数组末尾值小于arr[i],直接加在后面
if(i==0||tail[end]<arr[i]){
tail[++end]=arr[i];
}
//否则找到tail数组中第一个大于等于arr[i]的数的下标,替换为arr[i]
else{
int index=binarySearch(tail,end,arr[i]);
tail[index]=arr[i];
}
}
return end+1;
}
//二分法找到tail数组中第一个大于等于arr[i]的数的下标
private int binarySearch(int[] tail,int end,int target){
int l=0;
int r=end;
while(l<r){
int mid=l+(r-l)/2;
if(tail[mid]>=target){
r=mid;
}
else{
l=mid+1;
}
}
return l;
}
}
连续子数组的最大和
public class Solution {
public int FindGreatestSumOfSubArray(int[] array) {
int sum = 0;
int max = array[0];
for (int i = 0; i < array.length; i++) {
// 优化动态规划,确定sum的最大值
sum = Math.max(sum + array[i], array[i]);
// 每次比较,保存出现的最大值
max = Math.max(max, sum);
}
return max;
}
}
最长回文字符
import java.util.*;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
* @param A string字符串
* @return int整型
*/
public int getLongestPalindrome (String A) {
// write code here
return getLongestPalindrome(A, A.length());
}
public int getLongestPalindrome(String A, int n) {
//边界条件判断
if (n < 2)
return A.length();
//start表示最长回文串开始的位置,
//maxLen表示最长回文串的长度
int maxLen = 1;
boolean[][] dp = new boolean[n][n];
for (int right = 1; right < n; right++) {
for (int left = 0; left <= right; left++) {
//如果两种字符不相同,肯定不能构成回文子串
if (A.charAt(left) != A.charAt(right))
continue;
//下面是s.charAt(left)和s.charAt(right)两个
//字符相同情况下的判断
//如果只有一个字符,肯定是回文子串
if (right == left) {
dp[left][right] = true;
} else if (right - left <= 2) {
//类似于"aa"和"aba",也是回文子串
dp[left][right] = true;
} else {
//类似于"a******a",要判断他是否是回文子串,只需要
//判断"******"是否是回文子串即可
dp[left][right] = dp[left + 1][right - 1];
}
//如果字符串从left到right是回文子串,只需要保存最长的即可
if (dp[left][right] && right - left + 1 > maxLen) {
maxLen = right - left + 1;
}
}
}
//最长的回文子串
return maxLen;
}
}
数字字符串转化成IP地址
import java.util.*;
public class Solution {
ArrayList<String> res = new ArrayList<>();
public ArrayList<String> restoreIpAddresses (String s) {
func(0, 0, s, new ArrayList<Integer>());
return res;
}
public void func(int index, int cur, String s, ArrayList<Integer> templist) {
if (index == s.length()) {
if (templist.size() == 4) {
String str = "";
for (int i = 0; i < templist.size(); ++i) {
if (templist.size() - 1 != i)
str += templist.get(i) + ".";
else str += templist.get(i);
}
if (str.length() != s.length() + 3)return;
res.add(str);
}
return ;
}
int temp = cur * 10 + s.charAt(index) - '0';
if (temp >= 0 && temp <= 255) {
func(index + 1, temp, s, new ArrayList<Integer>(templist));
templist.add(temp);
func(index + 1, 0, s, new ArrayList<Integer>(templist));
}
}
}
编辑距离
import java.util.*;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
* @param str1 string字符串
* @param str2 string字符串
* @return int整型
*/
public int editDistance (String str1, String str2) {
// write code here
int[][] dp = new int[2][str2.length() +
1]; //定义动规数组,两行分别表示当前和上一轮的结果,分别为i%2和(i+1)%2
for (int i = 1; i <= str2.length(); i++) { //初始化
dp[0][i] = i;
}
for (int i = 1; i <= str1.length(); i++) {
for (int j = 1; j <= str2.length(); j++) {
if (str1.charAt(i - 1) == str2.charAt(j - 1)) { //第一种情况
if (j - 1 == 0) {
dp[i % 2][j] = i - 1;
} else {
dp[i % 2][j] = dp[(i + 1) % 2][j - 1];
}
} else { //第二种情况
if (j - 1 == 0) {
dp[i % 2][j] = Math.min(dp[(i + 1) % 2][j] + 1, i);
} else {
dp[i % 2][j] = Math.min(dp[(i + 1) % 2][j] + 1,
Math.min(dp[(i + 1) % 2][j - 1] + 1, dp[i % 2][j - 1] + 1));
}
}
}
}
return dp[str1.length() % 2][str2.length()];
}
}
正则表达式匹配
import java.util.*;
public class Solution {
public boolean match (String ss, String pp) {
// 技巧:往原字符头部插入空格,这样得到 char 数组是从 1 开始,而且可以使得 f[0][0] = true,可以将 true 这个结果滚动下去
int n = ss.length(), m = pp.length();
ss = " " + ss;
pp = " " + pp;
char[] s = ss.toCharArray();
char[] p = pp.toCharArray();
// f(i,j) 代表考虑 s 中的 1~i 字符和 p 中的 1~j 字符 是否匹配
boolean[][] f = new boolean[n + 1][m + 1];
f[0][0] = true;
for (int i = 0; i <= n; i++) {
for (int j = 1; j <= m; j++) {
// 如果下一个字符是 '*',则代表当前字符不能被单独使用,跳过
if (j + 1 <= m && p[j + 1] == '*') continue;
// 对应了 p[j] 为普通字符和 '.' 的两种情况
if (i - 1 >= 0 && p[j] != '*') {
f[i][j] = f[i - 1][j - 1] && (s[i] == p[j] || p[j] == '.');
}
// 对应了 p[j] 为 '*' 的情况
else if (p[j] == '*') {
f[i][j] = (j - 2 >= 0 && f[i][j - 2]) || (i - 1 >= 0 && f[i - 1][j] &&
(s[i] == p[j - 1] || p[j - 1] == '.'));
}
}
}
return f[n][m];
}
}
最长的括号字串
import java.util.*;
public class Solution {
/**
* @param s string字符串
* @return int整型
*/
int max = 0;
public int longestValidParentheses (String s) {
// write code here
int n = s.length();
if (n <= 1) {
return 0;
}
char []c = s.toCharArray();
Deque<Integer> stack = new LinkedList<>();
stack.push(-1);
for (int i = 0; i < n; i++) {
if (c[i] == '(') {
stack.push(i); //push的是坐标
} else {
stack.pop();
if (stack.isEmpty()) {
stack.push(i); //表示能匹配的左括号前面的那个元素
} else {
max = Math.max(max, i -
stack.peek()); //stack 保存的是最后一个被匹配的’(‘的前面的索引 }
}
}
}
return max;
}
}
打家劫舍(一)
import java.lang.Math;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
* @param nums int整型一维数组
* @return int整型
*/
public int rob (int[] nums) {
// 只有一个元素,就返回该元素的值
if (nums.length == 1) {
return nums[0];
}
// 两个元素,就返回两元素中其中一个最大值
if (nums.length == 2) {
return Math.max(nums[0], nums[1]);
}
// 定义一个数组,记录 n-1个值的和 和n-2个值的和
int[] maxVal = new int[nums.length];
// 初始化结果数组 第0 个元素和第 1个元素
maxVal[0] = nums[0];
maxVal[1] = Math.max(nums[0], nums[1]);
for (int i = 2; i < nums.length; i++) {
maxVal[i] = Math.max(maxVal[i - 1], maxVal[i - 2] + nums[i]);
}
return maxVal[maxVal.length - 1];
}
}
打家劫舍(二)
客家土楼
import java.util.*;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
* @param nums int整型一维数组
* @return int整型
*/
public int rob (int[] nums) {
int n = nums.length;
//在0到n-2范围内找
int rob1 = getRob(nums, 0, n - 1);
//在1到n-1范围内找
int rob2 = getRob(nums, 1, n);
return Math.max(rob1, rob2);
}
private int getRob(int[] nums, int start, int end) {
//记录到前前家为止最多偷多少
int prepre = 0;
//记录到前一家为止最多偷多少
int pre = 0;
int n = nums.length;
for (int i = start; i < end; i++) {
//要么是前前家+当前,要么是前一家,取较大者
int cur = Math.max(prepre + nums[i], pre);
//状态后移
prepre = pre;
pre = cur;
}
return pre;
}
}
买卖股票的最好时机(一)
import java.util.*;
public class Solution {
/**
* @param prices int整型一维数组
* @return int整型
*/
//暴力解法优化
public int maxProfit (int[] prices) {
// write code here
int len = prices.length;
int minPrices = Integer.MAX_VALUE;
int ans = 0;
for(int i=0;i<len;i++){
//寻找最低点
if(prices[i]<minPrices){
minPrices = prices[i];
}else if(prices[i]-minPrices>ans){
//更新答案(最大利润)
ans = prices[i]-minPrices;
}
}
return ans;
}
}
买卖股票的最好时机(二)
多次交易
import java.util.*;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
* 计算最大收益
* @param prices int整型一维数组 股票每一天的价格
* @return int整型
*/
public int maxProfit(int[] prices) {
int total = 0;
for (int i = 0; i < prices.length - 1; i++) {
//原数组中如果后一个减去前一个是正数,说明是上涨的,
//我们就要累加,否则就不累加
total += Math.max(prices[i + 1] - prices[i], 0);
}
return total;
}
}
买卖股票的最好时机(三)
只两次交易
import java.util.*;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
* 两次交易所能获得的最大收益
* @param prices int整型一维数组 股票每一天的价格
* @return int整型
*/
public int maxProfit (int[] prices) {
int n = prices.length;
//赋初值
int buy1 = -prices[0], sell1 = 0;
int buy2 = -prices[0], sell2 = 0;
for (int i = 1; i < n; i++) {
//状态转移
buy1 = Math.max(buy1, -prices[i]);
sell1 = Math.max(sell1, buy1 + prices[i]);
buy2 = Math.max(buy2, sell1 - prices[i]);
sell2 = Math.max(sell2, buy2 + prices[i]);
}
return sell2;
}
}
递归/回溯
没有重复项数字的全排列
import java.util.*;
public class Solution {
ArrayList<ArrayList<Integer>> ans = new ArrayList<>();
public ArrayList<ArrayList<Integer>> permute(int[] num) {
// sort just to insure output in alphabet order
Arrays.sort(num);
// copy sorted nums to remain
ArrayList<Integer> remain = new ArrayList<>();
for (int n : num) remain.add(n);
permuteRec(remain, new ArrayList<>());
return ans;
}
public void permuteRec(ArrayList<Integer> remain, ArrayList<Integer> perm) {
// base case, i.e. no remaining elem
if (remain.isEmpty()) {
// 存答案时必须存copy, 不然之后的回溯会overwrite存入的答案。
ans.add(new ArrayList<>(perm));
return;
}
for (int i = 0; i < remain.size(); i++) {
Integer a = remain.remove(i);
perm.add(a);
permuteRec(remain, perm);
// 复原 (a.k.a 回溯)
remain.add(i, a);
perm.remove(perm.size() - 1);
}
}
}
有重复项数字的全排列
import java.util.*;
public class Solution {
//用于标记是否访问过
boolean []mark;
public ArrayList<ArrayList<Integer>> permuteUnique(int[] num) {
//存储总的返回结果集
ArrayList<ArrayList<Integer>> res = new ArrayList<>();
//存储一个合法全排列
LinkedList<Integer> track = new LinkedList<>();
mark = new boolean[num.length];
//因为存在重复项,为了更好的处理,将数组先排序
Arrays.sort(num);
backtrack(num,res,track);
return res;
}
public void backtrack(int[] num, ArrayList<ArrayList<Integer>> res, LinkedList<Integer> track){
//若 找到一个全排列,则将它加进结果集中,然后返回(回溯)
if(track.size() == num.length){
res.add(new ArrayList<Integer>(track));
return;
}
for(int i = 0; i < num.length; i++){
// 当此时的被访问过
// 当i>0 &&此时的数等于它的上一个&&上一个没访问过(没访问过证明是回溯后将前面的置为false,所以此时避免重复得用 !mark[i-1] 满足要求然后跳过该数字)
// arr[1,1,1,2,3]
// 1,1,1 1,1,2 1,1,3 前面两次回溯得到三个结果
// 接下来再进行回溯,此时mark[1]被置为false
// 此时按道理应该遍历到arr[2]这个位置
// 1,arr[2] 然后后面再加进去,但是我们发现arr[2]==arr[1],并且此时mark[1]==false
// 证明它的已经访问过然后回溯的,所以我们跳过arr[2],直接从1,arr[3]开始
// 也就是说此时得到全排列的结果将会是 1,2,1 1,2,3 而不再是 1,1 ··· 这些重复的了
if(mark[i] || i>0 && num[i] == num[i-1] && !mark[i-1]){
continue;
}
//添加进全排列数组中
track.add(num[i]);
//标记为已经访问
mark[i] = true;
//继续寻找下一个数
backtrack(num,res,track);
//将上一次全排列的结果中,最后一个数移除掉
track.removeLast();
//移除掉的数置为 未访问
mark[i] = false;
}
}
}
岛屿数量
import java.util.*;
public class Solution {
/**
* 判断岛屿数量
* @param grid char字符型二维数组
* @return int整型
*/
public int solve(char[][] grid) {
//边界条件判断
if (grid == null || grid.length == 0)
return 0;
//统计岛屿的个数
int count = 0;
//两个for循环遍历每一个格子
for (int i = 0; i < grid.length; i++)
for (int j = 0; j < grid[0].length; j++) {
//只有当前格子是1才开始计算
if (grid[i][j] == '1') {
//如果当前格子是1,岛屿的数量加1
count++;
//然后通过bfs把当前格子的上下左右4
//个位置为1的都要置为0,因为他们是连着
//一起的算一个岛屿,
bfs(grid, i, j);
}
}
return count;
}
private void bfs(char[][] grid, int x, int y) {
//把当前格子先置为0
grid[x][y] = '0';
int n = grid.length;
int m = grid[0].length;
//使用队列,存储的是格子坐标转化的值
Queue<Integer> queue = new LinkedList<>();
//我们知道平面坐标是两位数字,但队列中存储的是一位数字,
//所以这里是把两位数字转化为一位数字
int code = x * m + y;
//坐标转化的值存放到队列中
queue.add(code);
while (!queue.isEmpty()) {
//出队
code = queue.poll();
//在反转成坐标值(i,j)
int i = code / m;
int j = code % m;
if (i > 0 && grid[i - 1][j] == '1') {//上
//如果上边格子为1,把它置为0,然后加入到队列中
//下面同理
grid[i - 1][j] = '0';
queue.add((i - 1) * m + j);
}
if (i < n - 1 && grid[i + 1][j] == '1') {//下
grid[i + 1][j] = '0';
queue.add((i + 1) * m + j);
}
if (j > 0 && grid[i][j - 1] == '1') { //左
grid[i][j - 1] = '0';
queue.add(i * m + j - 1);
}
if (j < m - 1 && grid[i][j + 1] == '1') {//右
grid[i][j + 1] = '0';
queue.add(i * m + j + 1);
}
}
}
}
字符串的排列
import java.util.ArrayList;
public class Solution {
public ArrayList<String> Permutation(String str) {
ArrayList<String> result = new ArrayList<>();
if (str.length() == 0) {
return result;
}
recur(str, "", result);
return result;
}
public void recur(String str, String cur, ArrayList<String> result) {
if (str.length() == 0) {
if (!result.contains(cur)) {
result.add(cur);
}
}
for (int i = 0; i < str.length(); i++) {
recur(str.substring(0, i) + str.substring(i + 1, str.length()),
cur + str.charAt(i), result);
}
}
}
N皇后问题
import java.util.*;
public class Solution {
/**
*
* @param n int整型 the n
* @return int整型
*/
Set<Integer> column = new HashSet<Integer>(); //标记列不可用
Set<Integer> posSlant = new HashSet<Integer>();//标记正斜线不可用
Set<Integer> conSlant = new HashSet<Integer>();//标记反斜线不可用
int result = 0;
public int Nqueen (int n) {
// write code here
compute(0, n);
return result;
}
private void compute(int i, int n) {
if (i == n) {
result++;
return;
}
for (int j = 0; j < n; j++) {
if (column.contains(j) || posSlant.contains(i - j) ||
conSlant.contains(i + j)) {
continue;
}
column.add(j);//列号j
posSlant.add(i - j);//行号i - 列号j 正斜线
conSlant.add(i + j);//行号i + 列号j 反斜线
compute(i + 1, n); //计算下一行
column.remove(j); //完成上一步递归计算后,清除
posSlant.remove(i - j);
conSlant.remove(i + j);
}
}
}
括号生成
import java.util.*;
public class Solution {
/**
*
* @param n int整型
* @return string字符串ArrayList
*/
ArrayList<String> res = new ArrayList<>();
int n;
public ArrayList<String> generateParenthesis (int _n) {
// write code here
n = _n;
dfs("", 0, 0);
return res;
}
public void dfs(String s, int cnt1, int cnt2) {
if (cnt1 == n && cnt2 == n) {
res.add(s);
return;
}
if (cnt1 < n) {
dfs(s + "(", cnt1 + 1, cnt2);
}
if (cnt1 > cnt2 && cnt2 < n) {
dfs(s + ")", cnt1, cnt2 + 1);
}
}
}
矩阵最长递增路径
import java.util.*;
public class Solution {
int[][] directions = new int[][] {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
int n, m, maxLen;
public int solve (int[][] matrix) {
n = matrix.length;
m = matrix[0].length;
maxLen = 0;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
solveRec(i, j, 0, matrix);
}
}
return maxLen;
}
public void solveRec(int i, int j, int len, int[][] matrix) {
if (++len > maxLen)
maxLen = len;
for (int[] dir : directions) {
int i_next = i + dir[0], j_next = j + dir[1];
if (i_next < 0 || i_next >= n || j_next < 0 || j_next >= m)
continue;
if (matrix[i_next][j_next] <= matrix[i][j])
continue;
solveRec(i_next, j_next, len, matrix);
}
}
}
哈希
两数之和
import java.util.*;
public class Solution {
/**
*
* @param numbers int整型一维数组
* @param target int整型
* @return int整型一维数组
*/
public int[] twoSum (int[] numbers, int target) {
// write code here
HashMap<Integer, Integer> map = new HashMap<>();
//遍历数组
for (int i = 0; i < numbers.length; i++) {
//将不包含target - numbers[i],装入map中,包含的话直接返回下标
if (map.containsKey(target - numbers[i]))
return new int[] {map.get(target - numbers[i]) + 1, i + 1};
else
map.put(numbers[i], i);
}
throw new IllegalArgumentException("No solution");
}
}
数组中出现次数超过一半的数字
import java.util.*;
public class Solution {
public int MoreThanHalfNum_Solution(int [] array) {
if (array.length == 0) {
return 0;
}
int len = array.length;
int threshold = len / 2;
Map<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < len; i++) {
if (!map.keySet().contains(array[i])) {
map.put(array[i], 1);
} else {
map.put(array[i], map.get(array[i]) + 1);
}
}
for (Integer key : map.keySet()) {
if (map.get(key) > threshold) {
return key;
}
}
return 0;
}
}
数组中只出现一次的两个数字
import java.util.*;
/**
其实这个题的关键就在于怎么分离第一次异或之后所得的a^b
首先要知道,a和b不是同一个数,那么异或结果不为0,那么结果的二进制肯定至少有一个是1
也就是说,为1的这一位上,a和b的二进制一个是0,一个是1
只要我们下一次异或只把是0(或者是1)的异或进来,出现两次还是会异或为0,不影响,剩下的就是a或者b
取一个不为0的数二进制最右边的1是固定的写法。自己与上自己取反加1的值,取反加1在括号里
搞出来一个a或者b了,再和一开始的a^b异或在一起,得到另外一个数
最后三元运算符得到两个数中较小的一个,放在前面就可以了。
*/
public class Solution {
public int[] FindNumsAppearOnce (int[] array) {
int eor = 0;//任何数和0异或都等于它本身
for (int i = 0; i < array.length; i++) {
eor ^= array[i];//这样结束了就是两个不一样的数相异或
}
int eor2 = 0;//再搞一个eor
int rightOne = eor & (~eor +
1);//固定写法,一个不为0的数最右边的1就这么求
//自己与上自己取反加1的值就是最右边的一个1
for (int j = 0; j < array.length; j++) {
if ((array[j] & rightOne) ==
0) { //如果某个数在这一位上面是1,相与结果才是1,注意优先级
eor2 ^= array[j]; //这样异或下来eor2就是其中一个数
}
}
int a = eor2;
int b = eor ^ eor2;
return a < b ? new int[] {a, b} : new int[] {b, a};
}
}
缺失的第一个正整数
import java.util.*;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
* @param nums int整型一维数组
* @return int整型
*/
public int minNumberDisappeared (int[] nums) {
int []arr = new int [nums.length + 1];
for (int i = 0; i < nums.length; ++i) {
if (nums[i] <= nums.length && nums[i] > 0) {
arr[nums[i]] = 1;
}
}
for (int i = 1; i < arr.length; ++i)
if (arr[i] == 0)return i;
return arr.length;
}
}
三数之和
import java.util.*;
public class Solution {
public ArrayList<ArrayList<Integer>> threeSum(int[] num) {
Arrays.sort(num);
ArrayList<ArrayList<Integer>> result = new ArrayList<ArrayList<Integer>>();
//我再负数区 选一个则,必须在 正数区选一个,或者选两个
for (int i = 0; i < num.length; i++) {
if (num[i] > 0) {
//非正数选完代表选完
break;
}
if (i != 0 && num[i] == num[i - 1]) {
//去重
continue;
}
//两数求和
int start = i + 1;
int end = num.length - 1;
while (start < end) {
//先左移 因为排序原因 start 为 相对于 i 第二小值
while (start < end && num[i] + num[start] + num[end] > 0) {
end--;
}
if (start < end && num[i] + num[start] + num[end] == 0) {
ArrayList<Integer> item = new ArrayList<Integer>();
item.add(num[i]);
item.add(num[start]);
item.add(num[end]);
result.add(item);
//右移去重
while (start < end && item.get(1) == num[start]) {
start++;
}
} else {
//右移增大整体数
start++;
}
}
}
return result;
}
}
堆/栈/队列
- Stack
temp = new Stack<>(); - public PriorityQueue
maxQueue = new PriorityQueue ((o1,o2)->o2 - o1);//大顶堆 - Queue
queue = new LinkedList<>();
有效括号序列
处理类似问题的经典数据结构、思想!
import java.util.*;
public class Solution {
/**
* @param s string字符串
* @return bool布尔型
*/
public boolean isValid (String s) {
// write code here
if (s == null) {
return false;
}
Stack<Character> temp = new Stack<>();
for (char item : s.toCharArray()) {
if (item == '[') {
temp.push(']');
} else if (item == '{') {
temp.push('}');
} else if (item == '(') {
temp.push(')');
} else if (temp.isEmpty() || temp.pop() != item) {
//如果 还有数据 并且不是 [ { ( ,那么temp就是空的,不符合要求,或者弹出的元素不等于当前的 也不是
return false;
}
}
return temp.isEmpty();
}
}
滑动窗口的最大值
- 法一:熟悉此数据结构
- 法二:常规解法
import java.util.*;
/**
思路:用一个大顶堆,保存当前滑动窗口中的数据。滑动窗口每次移动一格,就将前面一个数出堆,后面一个数入堆。
*/
public class Solution {
//大顶堆
public PriorityQueue<Integer> maxQueue = new PriorityQueue<Integer>((o1,o2)->o2 - o1);
public ArrayList<Integer> result = new ArrayList<Integer>();//保存结果
public ArrayList<Integer> maxInWindows(int [] num, int size) {
if (num == null || num.length <= 0 || size <= 0 || size > num.length) {
return result;
}
int count = 0;
for (; count < size; count++) { //初始化滑动窗口
maxQueue.offer(num[count]);
}
while (count < num.length) {
//对每次操作,找到最大值(用优先队列的大顶堆),然后向后滑动(出堆一个,入堆一个)
result.add(maxQueue.peek());
maxQueue.remove(num[count - size]);
maxQueue.add(num[count]);
count++;
}
//最后一次入堆后没保存结果,这里额外做一次即可
result.add(maxQueue.peek());
return result;
}
}
//双指针+双层遍历
import java.util.*;
public class Solution {
static ArrayList<Integer> res = new ArrayList();
public ArrayList<Integer> maxInWindows(int [] num, int size) {
if (size == 0)return new ArrayList();
int p1 = 0, p2 = 0;
while (true) {
if (p1 + size > num.length)break;
p2 = p1 + size - 1;
int max = 0;
for (int i = p1; i <= p2; i++) {
if (num[i] >= max)max = num[i];
}
res.add(max);
p1++;
}
return res;
}
}
最小的K个数
import java.util.*;
public class Solution {
public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
ArrayList<Integer> res = new ArrayList<>(k);
//根据题意要求,如果K>数组的长度,返回一个空的数组
if (k > input.length || k == 0)
return res;
//创建最大堆
PriorityQueue<Integer> queue = new PriorityQueue<>((num1, num2) -> num2 - num1);
//先在堆中放数组的前k个元素
for (int i = 0; i < k; ++i) {
queue.offer(input[i]);
}
//因为是最大堆,也就是堆顶的元素是堆中最大的,遍历数组后面元素的时候,
//如果当前元素比堆顶元素大,就把堆顶元素给移除,然后再把当前元素放到堆中,
for (int i = k; i < input.length; ++i) {
if (queue.peek() > input[i]) {
queue.poll();
queue.offer(input[i]);
}
}
//最后再把堆中元素转化为数组
for (int i = 0; i < k; ++i) {
res.add(queue.poll());
}
return res;
}
}
//用map,思想是一样的
import java.util.*;
public class Solution {
public ArrayList<Integer> GetLeastNumbers_Solution(int[] input, int k) {
ArrayList<Integer> res = new ArrayList<>(k);
//根据题意要求,如果K>数组的长度,返回一个空的数组
if (k > input.length || k == 0)
return res;
//map中key存放数组中元素,value存放这个元素的个数
TreeMap<Integer, Integer> map = new TreeMap<>();
int count = 0;
for (int i = 0; i < input.length; i++) {
//map中先存放k个元素,之后map中元素始终维持在k个
if (count < k) {
map.put(input[i], map.getOrDefault(input[i], 0) + 1);
count++;
continue;
}
Map.Entry<Integer, Integer> entry = map.lastEntry();
//从第k+1个元素开始,每次存放的时候都要和map中最大的那个比较,如果比map中最大的小,
//就把map中最大的给移除,然后把当前元素加入到map中
if (entry.getKey() > input[i]) {
//移除map中最大的元素,如果只有一个直接移除。如果有多个(数组中会有重复的元素),移除一个就行
if (entry.getValue() == 1) {
map.pollLastEntry();
} else {
map.put(entry.getKey(), entry.getValue() - 1);
}
//把当前元素加入到map中
map.put(input[i], map.getOrDefault(input[i], 0) + 1);
}
}
//把map中key存放到集合list中
for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
int keyCount = entry.getValue();
while (keyCount-- > 0) {
res.add(entry.getKey());
}
}
return res;
}
}
寻找第K大
堆排序是最快的,而且堆排不用全排序完,题目要第几个就排几次就可以直接终止了
import java.util.*;
public class Solution {
public int findKth(int[] a, int n, int K) {
// write code here
for (int i = a.length / 2 - 1; i >= 0; i--) {
// 建堆
buildHeap(a, i, a.length);
}
// 这里不是按照a.length逐次-1,是按照k的大小,即每次将大顶堆根节点换到数组末尾的时候,我就们找到第i个最大值了,i=k时就是结果;
for (int i = 0; i < K; i++) {
swap(a, 0, a.length - 1 - i);
buildHeap(a, 0, a.length - 1 - i);
}
return a[a.length - K];
}
private void buildHeap(int[] arr, int i, int len) {
int tmp = arr[i];
for (int j = 2 * i + 1 ; j < len; j = j * 2 + 1) {
if (j + 1 < len && arr[j] < arr[j + 1]) {
j++;
}
if (tmp < arr[j]) {
arr[i] = arr[j];
i = j;
}
}
arr[i] = tmp;
}
private void swap (int[] arr, int idx1, int idx2) {
int tmp = arr[idx1];
arr[idx1] = arr[idx2];
arr[idx2] = tmp;
}
}
数据流中的中位数
让数组的前半部分为大顶堆,需要最大值。后半部分为小顶堆,需要最小值
import java.util.PriorityQueue;
import java.util.Comparator;
public class Solution {
PriorityQueue<Integer> min_heap = new PriorityQueue<>();
PriorityQueue<Integer> max_heap = new PriorityQueue<>(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1; //默认是小顶堆,o1-o2
}
});
int count = 0; //记录已获取数字的个数
public void Insert(Integer num) {
if (count % 2 == 0) {
max_heap.offer(num);
int tmp = max_heap.poll();
min_heap.offer(tmp);
} else {
min_heap.offer(num);
int tmp = min_heap.poll();
max_heap.offer(tmp);
}
count++;
}
public Double GetMedian() {
if (count % 2 == 0) {
return new Double(min_heap.peek() + max_heap.peek()) / 2;
} else {
return new Double(min_heap.peek());
}
}
}
表达式求值
import java.util.*;
public class Solution {
// 使用 map 维护一个运算符优先级
// 这里的优先级划分按照「数学」进行划分即可
Map<Character, Integer> map = new HashMap<Character, Integer>() {
{
put('-', 1);
put('+', 1);
put('*', 2);
put('/', 2);
put('%', 2);
put('^', 3);
}
};
public int solve(String s) {
// 将所有的空格去掉
s = s.replaceAll(" ", "");
char[] cs = s.toCharArray();
int n = s.length();
// 存放所有的数字
Deque<Integer> nums = new ArrayDeque<>();
// 为了防止第一个数为负数,先往 nums 加个 0
nums.addLast(0);
// 存放所有「非数字以外」的操作
Deque<Character> ops = new ArrayDeque<>();
for (int i = 0; i < n; i++) {
char c = cs[i];
if (c == '(') {
ops.addLast(c);
} else if (c == ')') {
// 计算到最近一个左括号为止
while (!ops.isEmpty()) {
if (ops.peekLast() != '(') {
calc(nums, ops);
} else {
ops.pollLast();
break;
}
}
} else {
if (isNumber(c)) {
int u = 0;
int j = i;
// 将从 i 位置开始后面的连续数字整体取出,加入 nums
while (j < n && isNumber(cs[j])) u = u * 10 + (cs[j++] - '0');
nums.addLast(u);
i = j - 1;
} else {
if (i > 0 && (cs[i - 1] == '(' || cs[i - 1] == '+' || cs[i - 1] == '-')) {
nums.addLast(0);
}
// 有一个新操作要入栈时,先把栈内可以算的都算了
// 只有满足「栈内运算符」比「当前运算符」优先级高/同等,才进行运算
while (!ops.isEmpty() && ops.peekLast() != '(') {
char prev = ops.peekLast();
if (map.get(prev) >= map.get(c)) {
calc(nums, ops);
} else {
break;
}
}
ops.addLast(c);
}
}
}
// 将剩余的计算完
while (!ops.isEmpty() && ops.peekLast() != '(') calc(nums, ops);
return nums.peekLast();
}
// 计算逻辑:从 nums 中取出两个操作数,从 ops 中取出运算符,然后根据运算符进行计算即可
void calc(Deque<Integer> nums, Deque<Character> ops) {
if (nums.isEmpty() || nums.size() < 2) return;
if (ops.isEmpty()) return;
int b = nums.pollLast(), a = nums.pollLast();
char op = ops.pollLast();
int ans = 0;
if (op == '+') ans = a + b;
else if (op == '-') ans = a - b;
else if (op == '*') ans = a * b;
else if (op == '/') ans = a / b;
else if (op == '^') ans = (int)Math.pow(a, b);
else if (op == '%') ans = a % b;
nums.addLast(ans);
}
boolean isNumber(char c) {
return Character.isDigit(c);
}
}
二叉树
二叉树的前序遍历
import java.util.*;
/*
* public class TreeNode {
* int val = 0;
* TreeNode left = null;
* TreeNode right = null;
* public TreeNode(int val) {
* this.val = val;
* }
* }
*/
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
* @param root TreeNode类
* @return int整型一维数组
*/
public int[] preorderTraversal(TreeNode root) {
LinkedList<Integer> list = new LinkedList<>();
if (root == null) {
return new int[0];
}
Stack<TreeNode> stack = new Stack<>();
stack.push(root);
while (!stack.isEmpty()) {
//处理根节点
TreeNode node = stack.pop();
list.add(node.val);
//根左右,由于栈是先进后出,所以要让左节点后入栈,这样才能先弹出来
if (node.right != null) {
stack.push(node.right);
}
if (node.left != null) {
stack.push(node.left);
}
}
return list.stream().mapToInt(Integer::intValue).toArray();
}
}
二叉树的中序遍历
import java.util.*;
public class Solution {
ArrayList<Integer> list = new ArrayList<>();
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
* @param root TreeNode类
* @return int整型一维数组
*/
public int[] inorderTraversal (TreeNode root) {
ArrayList<Integer> helpList = inorder(root, list);
int[] intArr = list.stream().mapToInt(Integer::intValue).toArray();
return intArr;
}
public ArrayList<Integer> inorder(TreeNode root, ArrayList<Integer> list) {
if (root == null) {
return list;
}
inorder(root.left, list);
list.add(root.val);
inorder(root.right, list);
return list;
}
}
二叉树的后续遍历
import java.util.*;
/*
* public class TreeNode {
* int val = 0;
* TreeNode left = null;
* TreeNode right = null;
* public TreeNode(int val) {
* this.val = val;
* }
* }
*/
public class Solution {
public int[] postorderTraversal (TreeNode root) {
// write code here
if (root == null)return new int[0];
List<Integer> list = new ArrayList<>();
postOr(list, root);
int[] res = new int[list.size()];
for (int i = 0; i < res.length; i++) {
res[i] = list.get(i);
}
return res;
}
//递归方法
public void postOr(List<Integer> list, TreeNode t) {
if (t.left != null) postOr(list, t.left);
if (t.right != null) postOr(list, t.right);
list.add(t.val);
}
}
求二叉树的层序遍历
import java.util.*;
/*
* public class TreeNode {
* int val = 0;
* TreeNode left = null;
* TreeNode right = null;
* }
*/
public class Solution {
/**
*
* @param root TreeNode类
* @return int整型ArrayList<ArrayList<>>
*/
public ArrayList<ArrayList<Integer>> levelOrder (TreeNode root) {
// write code here
ArrayList<ArrayList<Integer>> result = new ArrayList<>();
if (root == null) {
return result;
}
// 队列,用于存储元素
Queue<TreeNode> queue = new LinkedList<>();
// 根节点先入队
queue.offer(root);
// 当队列不为空的时候
while (!queue.isEmpty()) {
// 队列的大小就是这一层的元素数量
int size = queue.size();
ArrayList<Integer> list = new ArrayList<>();
// 开始遍历这一层的所有元素
for (int i = 0; i < size; i ++) {
TreeNode node = queue.poll();
// 如果左节点不为空,则入队,作为下一层来遍历
if (node.left != null) {
queue.offer(node.left);
}
// 同上
if (node.right != null) {
queue.offer(node.right);
}
// 存储一层的节点
list.add(node.val);
}
// 将一层所有的节点汇入到总的结果集中
result.add(list);
}
return result;
}
}
按之字形顺序打印二叉树
import java.util.*;
/*
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
public class Solution {
public ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {
LinkedList<TreeNode> q = new LinkedList<>();
ArrayList<ArrayList<Integer>> res = new ArrayList<>();
boolean rev = true;
q.add(pRoot);
while (!q.isEmpty()) {
int size = q.size();
ArrayList<Integer> list = new ArrayList<>();
for (int i = 0; i < size; i++) {
TreeNode node = q.poll();
if (node == null) {
continue;
}
if (rev) {
list.add(node.val);
} else {
list.add(0, node.val);
}
q.offer(node.left);
q.offer(node.right);
}
if (list.size() != 0) {
res.add(list);
}
rev = !rev;
}
return res;
}
}
二叉树的最大深度
import java.util.*;
/*
* public class TreeNode {
* int val = 0;
* TreeNode left = null;
* TreeNode right = null;
* }
*/
public class Solution {
/**
*递归法
* @param root TreeNode类
* @return int整型
*/
public int maxDepth(TreeNode root) {
return root == null ? 0 : Math.max(maxDepth(root.left), maxDepth(root.right)) + 1;
}
//广度优先
public int maxDepth2(TreeNode root) {
if (root == null)
return 0;
//创建一个队列
Deque<TreeNode> deque = new LinkedList<>();
deque.push(root);
int count = 0;
while (!deque.isEmpty()) {
//每一层的个数
int size = deque.size();
while (size-- > 0) {
TreeNode cur = deque.pop();
if (cur.left != null)
deque.addLast(cur.left);
if (cur.right != null)
deque.addLast(cur.right);
}
count++;
}
return count;
}
//深度优先
public int maxDepth(TreeNode root) {
if (root == null)
return 0;
//stack记录的是节点,而level中的元素和stack中的元素
//是同时入栈同时出栈,并且level记录的是节点在第几层
Stack<TreeNode> stack = new Stack<>();
Stack<Integer> level = new Stack<>();
stack.push(root);
level.push(1);
int max = 0;
while (!stack.isEmpty()) {
//stack中的元素和level中的元素同时出栈
TreeNode node = stack.pop();
int temp = level.pop();
max = Math.max(temp, max);
if (node.left != null) {
//同时入栈
stack.push(node.left);
level.push(temp + 1);
}
if (node.right != null) {
//同时入栈
stack.push(node.right);
level.push(temp + 1);
}
}
return max;
}
}
二叉树中和为某一值的路径(一)
import java.util.*;
/*
* public class TreeNode {
* int val = 0;
* TreeNode left = null;
* TreeNode right = null;
* }
*/
public class Solution {
/**
*递归
* @param root TreeNode类
* @param sum int整型
* @return bool布尔型
*/
public boolean hasPathSum(TreeNode root, int sum) {
//如果根节点为空,或者叶子节点也遍历完了也没找到这样的结果,就返回false
if (root == null)
return false;
//如果到叶子节点了,并且剩余值等于叶子节点的值,说明找到了这样的结果,直接返回true
if (root.left == null && root.right == null && sum - root.val == 0)
return true;
//分别沿着左右子节点走下去,然后顺便把当前节点的值减掉,左右子节点只要有一个返回true,
//说明存在这样的结果
return hasPathSum(root.left, sum - root.val) ||
hasPathSum(root.right, sum - root.val);
}
public boolean hasPathSum(TreeNode root, int sum) {
if (root == null)
return false;
Stack<TreeNode> stack = new Stack<>();
stack.push(root);//根节点入栈
while (!stack.isEmpty()) {
TreeNode cur = stack.pop();//出栈
//累加到叶子节点之后,结果等于sum,说明存在这样的一条路径
if (cur.left == null && cur.right == null) {
if (cur.val == sum)
return true;
}
//右子节点累加,然后入栈
if (cur.right != null) {
cur.right.val = cur.val + cur.right.val;
stack.push(cur.right);
}
//左子节点累加,然后入栈
if (cur.left != null) {
cur.left.val = cur.val + cur.left.val;
stack.push(cur.left);
}
}
return false;
}
}
二叉搜索树与双向链表
/**
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
public class Solution {
TreeNode pre = null;
TreeNode root = null;
public TreeNode Convert(TreeNode pRootOfTree) {
if (pRootOfTree == null) return null;
Convert(pRootOfTree.left);
if (root == null) {
root = pRootOfTree;
}
if (pre != null) {
pRootOfTree.left = pre;
pre.right = pRootOfTree;
}
pre = pRootOfTree;
Convert(pRootOfTree.right);
return root;
}
}
二分查找/排序
二分查找-I
import java.util.*;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
* @param nums int整型一维数组
* @param target int整型
* @return int整型
*/
public int search (int[] nums, int target) {
int left = 0, right = nums.length - 1;
int medd;
while (left <= right) {
medd = (left + right) / 2;
if (nums[medd] < target) {
left = medd + 1;
} else if (nums[medd] > target) {
right = medd - 1;
} else {
return medd;
}
}
return -1;
}
}
二维数组中的查找
public class Solution {
public boolean Find(int target, int [][] array) {
int rows = array.length;
if (rows == 0) {
return false;
}
int cols = array[0].length;
if (cols == 0) {
return false;
}
// 左下
int row = rows - 1;
int col = 0;
while (row >= 0 && col < cols) {
if (array[row][col] < target) {
col++;
} else if (array[row][col] > target) {
row--;
} else {
return true;
}
}
return false;
}
}
寻找峰值
import java.util.*;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
* @param nums int整型一维数组
* @return int整型
*/
public int findPeakElement (int[] nums) {
// write code here
//关键思想:下坡的时候可能找到波峰,但是可能找不到,一直向下走的
//上坡的时候一定能找到波峰,因为题目给出的是nums[-1] = nums[n] = -∞
int left = 0;
int right = nums.length - 1;
while (left < right) {
int mid = left + (right - left) / 2;
//证明右边的路是下坡路,不一定有坡峰
if (nums[mid] > nums[mid + 1]) {
right = mid;
} else {
//这里是右边的路是上坡路
left = mid + 1;
}
}
return right;
}
}
数组中的逆序对
归并排序
public class Solution {
int[] nums, temp;
public int merge_sort(int l, int r) {
if (l >= r)return 0;
int m, i, j, k;
long res;
m = (l + r) / 2;
res = (merge_sort(l, m) + merge_sort(m + 1, r)) % 1000000007;
//merge
for (k = l; k <= r; k++)temp[k] = nums[k];
i = l;
j = m + 1;
k = l;
for (k = l; k <= r; k++) {
if (i == m + 1)nums[k] = temp[j++];
else if (j == r + 1 || temp[i] <= temp[j])nums[k] = temp[i++];
else {
res = (res + m - i + 1) % 1000000007;
nums[k] = temp[j++];
}
}
return (int)res;
}
public int InversePairs(int [] array) {
nums = array;
temp = new int[array.length];
return merge_sort(0, array.length - 1);
}
}
旋转数组的最小数字
import java.util.ArrayList;
public class Solution {
public int minNumberInRotateArray(int[] array) {
if (array.length == 0) {
return 0;
}
int l = 0;
int r = array.length - 1;
while (l < r - 1) {
int mid = (l + r) >> 1;
if (array[mid] >= array[l]) {
l = mid; /// 说明mid所在的位置是在第一个非递减子数组中
} else if (array[mid] <= array[r]) {
r = mid; /// 说明mid所在的位置是在第二个非递减子数组中
}
}
return array[r];
}
}
比较版本号
import java.util.*;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
* 比较版本号
* @param version1 string字符串
* @param version2 string字符串
* @return int整型
*/
public int compare (String version1, String version2) {
// write code here
String[] str1 = version1.split("\\.");
String[] str2 = version2.split("\\.");
int len = Math.max(str1.length, str2.length);
for (int i = 0; i < len; i++) {
String a = i < str1.length ? str1[i] : "0";
String b = i < str2.length ? str2[i] : "0";
for (int j = 0; j < a.length(); j++) {
if (j == a.length() - 1 || a.charAt(j) != '0') {
a = a.substring(j);
break;
}
}
for (int j = 0; j < b.length(); j++) {
if (j == b.length() - 1 || b.charAt(j) != '0') {
b = b.substring(j);
break;
}
}
if (a.length() > b.length()) {
return 1;
} else if (a.length() < b.length()) {
return -1;
} else {
if (a.compareTo(b) > 0) {
return 1;
} else if (a.compareTo(b) < 0) {
return -1;
}
}
}
return 0;
}
}
链表
反转链表
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}*/
import java.util.Stack;
public class Solution {
public ListNode ReverseList(ListNode head) {
Stack<ListNode> stack = new Stack<>();
//把链表节点全部摘掉放到栈中
while (head != null) {
stack.push(head);
head = head.next;
}
if (stack.isEmpty())
return null;
ListNode node = stack.pop();
ListNode dummy = node;
//栈中的结点全部出栈,然后重新连成一个新的链表
while (!stack.isEmpty()) {
ListNode tempNode = stack.pop();
node.next = tempNode;
node = node.next;
}
//最后一个结点就是反转前的头结点,一定要让他的next
//等于空,否则会构成环
node.next = null;
return dummy;
}
}
链表内指定区间反转
import java.util.*;
/*
* public class ListNode {
* int val;
* ListNode next = null;
* }
*/
public class Solution {
/**
*
* @param head ListNode类
* @param m int整型
* @param n int整型
* @return ListNode类
*/
//
//说明:方便理解,以下注释中将用left,right分别代替m,n节点
public ListNode reverseBetween (ListNode head, int m, int n) {
//设置虚拟头节点
ListNode dummyNode = new ListNode(-1);
dummyNode.next = head;
ListNode pre = dummyNode;
for (int i = 0; i < m - 1; i++) {
pre = pre.next;
}
ListNode cur = pre.next;
ListNode Cur_next ;
for (int i = 0; i < n - m; i++) {
Cur_next = cur.next;
cur.next = Cur_next.next;
Cur_next .next = pre.next;
pre.next = Cur_next ;
}
return dummyNode.next;
}
}
合并k个已排序的链表
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
import java.util.*;
public class Solution {
//合并K个有序链表的方法
public ListNode mergeKLists(ArrayList<ListNode> lists) {
return mergeList(lists, 0, lists.size() - 1);
}
public ListNode mergeList(ArrayList<ListNode> lists, int left, int right) {
if (left == right) return lists.get(left);
if (left > right) return null;
int mid = left + (right - left) / 2;
return merge(mergeList(lists, left, mid), mergeList(lists, mid + 1, right));
}
//合并两个有序链表(和力扣的21题一样)
public ListNode merge(ListNode l1, ListNode l2) {
if (l1 == null)return l2;
if (l2 == null) return l1;
if (l1.val < l2.val) {
l1.next = merge(l1.next, l2);
return l1;
} else {
l2.next = merge(l1, l2.next);
return l2;
}
}
}
网络知识
子网掩码
IP地址是由4个0-255之间的整数构成的,用"."符号相连;
二进制的IP地址格式有32位,例如:10000011,01101011,00000011,00011000;每八位用十进制表示就是131.107.3.24
子网掩码是用来判断任意两台计算机的IP地址是否属于同一子网络的根据。
子网掩码与IP地址结构相同,是32位二进制数,由1和0组成,且1和0分别连续,其中网络号部分全为“1”和主机号部分全为“0”。
你可以简单的认为子网掩码是一串连续的1和一串连续的0拼接而成的32位二进制数,左边部分都是1,右边部分都是0。
利用子网掩码可以判断两台主机是否中同一子网中。
若两台主机的IP地址分别与它们的子网掩码进行逻辑“与”运算(按位与/AND)后的结果相同,则说明这两台主机在同一子网中。
示例:
I P 地址 192.168.0.1
子网掩码 255.255.255.0
转化为二进制进行运算:
I P 地址 11000000.10101000.00000000.00000001
子网掩码 11111111.11111111.11111111.00000000
AND运算 11000000.10101000.00000000.00000000
转化为十进制后为:
192.168.0.0
I P 地址 192.168.0.254
子网掩码 255.255.255.0
转化为二进制进行运算:
I P 地址 11000000.10101000.00000000.11111110
子网掩码 11111111.11111111.11111111.00000000
AND运算 11000000.10101000.00000000.00000000
转化为十进制后为:
192.168.0.0
通过以上对两台计算机IP地址与子网掩码的AND运算后,我们可以看到它运算结果是一样的。均为192.168.0.0,所以这二台计算机可视为是同一子网络。
输入一个子网掩码以及两个ip地址,判断这两个ip地址是否是一个子网络。
若IP地址或子网掩码格式非法则输出1,若IP1与IP2属于同一子网络输出0,若IP1与IP2不属于同一子网络输出2。
注:
有效掩码与IP的性质为:
1. 掩码与IP每一段在 0 - 255 之间
2. 掩码的二进制字符串前缀为网络号,都由‘1’组成;后缀为主机号,都由'0'组成
不讲武德API
四则运算
import javax.script.*;
ScriptEngine scriptEngine = new ScriptEngineManager().getEngineByName("nashorn");
System.out.println(scriptEngine.eval(input));//input为四则运算