1、关于Comparable和Comparator
Comparable接口位于java.lang包下,Comparator位于java.util包下。
Comparable接口只提供了一个compareTo()方法,Comparator接口不仅提供了compara()方法,还提供了其他默认方法,如reversed()、thenComparing(),使我们可以按照更多的方式进行排序
如果要用Comparable接口,则必须实现这个接口,并重写comparaTo()方法;但是Comparator接口可以在类外部使用,通过将该接口的一个匿名类对象当做参数传递给Collections.sort()方法或者Arrays.sort()方法实现排序。Comparator体现了一种策略模式,即可以不用要把比较方法嵌入到类中,而是可以单独在类外部使用,这样我们就可有不用改变类本身的代码而实现对类对象进行排序。
9.1Comparable
Collections类中包含很多对实现Collection接口的容器各种操作的静态方法。当然, 其中最长用的莫过于排序了(Collections.sort(List l)。但是当List容器添加的元素对象是属于自己写的类时, 就可能出问题了。
总而言之, 如果你想1个类的对象支持比较(排序), 就必须实现Comparable接口。Comparable 接口内部只有1个要重写的关键的方法。就是 int compareTo(T o) 这个方法返回1个Int数值,
class Student implements Comparable{
private String name;
private int ranking;
public Student(String name, int ranking){
this.name = name;
this.ranking = ranking;
}
public String toString(){
return this.name + ":" + this.ranking;
}
public int compareTo(Object o){
Student s = (Student)(o);
return this.ranking - s.ranking;
}
}
9.2Comparator
// 1.对学生集合按年龄进行排序
Collections.sort(stus, new Comparator<Student>() {
@Override
public int compare(Student s1, Student s2) {
// 升序
//return s1.getAge()-s2.getAge();
return s1.getAge().compareTo(s2.getAge());
// 降序
// return s2.getAge()-s1.getAge();
// return s2.getAge().compareTo(s1.getAge());
}
});
// 2.对学生集合按姓名首字母排序
Comparator comparator = Collator.getInstance(Locale.CHINA);
Collections.sort(stus, new Comparator<Student>() {
@Override
public int compare(Student s1, Student s2) {
return comparator.compare(s1.getName(), s2.getName());
}
});
BFS模板
https://www.cnblogs.com/jwthong/p/12762326.html
public List<Integer> rightSideView(TreeNode root) {
List<Integer> res = new ArrayList<>();
if (root == null) {
return res;
}
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while (!queue.isEmpty()) {
int size = queue.size();
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);
}
if (i == size - 1) { //将当前层的最后一个节点放入结果列表
res.add(node.val);
}
}
}
return res;
}
快慢指针
给定一个包含 n + 1 个整数的数组 nums,其数字都在 1 到 n 之间(包括 1 和 n),可知至少存在一个重复的整数。假设只有一个重复的整数,找出这个重复的数。
public int findDuplicate(int[] nums) {
int fast=0;
int slow=0;
while(true){
fast=nums[nums[fast]];
slow = nums[slow];
if(fast==slow)break;
}
int k=0;
while(true){
slow=nums[slow];
k=nums[k];
if(slow==k)break;
}
return slow;
}
二分法查找
static int cha(int target,int[] arr,int low,int high) {
if(target>arr[high]||target<arr[low]||low>high) {
return -1;
}
int m=(low+high)/2;
if(arr[m]>target) {
return cha(target,arr,low,m-1);
}else if(arr[m]<target){
return cha(target,arr,m+1,high);
}else {
return m;
}
}
7.看不懂
给定一个含有正整数和负整数的环形数组 nums。 如果某个索引中的数 k 为正数,则向前移动 k 个索引。相反,如果是负数 (-k),则向后移动 k 个索引。因为数组是环形的,所以可以假设最后一个元素的下一个元素是第一个元素,而第一个元素的前一个元素是最后一个元素。
确定 nums 中是否存在循环(或周期)。循环必须在相同的索引处开始和结束并且循环长度 > 1。此外,一个循环中的所有运动都必须沿着同一方向进行。换句话说,一个循环中不能同时包括向前的运动和向后的运动。
示例 1:
输入:[2,-1,1,2,2]
输出:true
解释:存在循环,按索引 0 -> 2 -> 3 -> 0 。循环长度为 3 。
public boolean circularArrayLoop(int[] nums) {
int len =nums.length;
for(int i=0;i<nums.length;i++) {
nums[i]=nums[i]%len;
}
for(int i=0;i<len;i++){
int f=nums[i];
if(f>=len)continue;
int j=i;
int flag = len+i;
int last = j;
while(nums[j]<len) {
if(f*nums[j]<0)break;
int next=(j+nums[j]+len)%len;
nums[j]=flag;
last = j;
j=next;
}
if(nums[j]==flag&&j!=last)return true;
}
return false;
}
二叉搜索树的删除
class Solution {
/*
One step right and then always left
*/
public int successor(TreeNode root) {
root = root.right;
while (root.left != null) root = root.left;
return root.val;
}
/*
One step left and then always right
*/
public int predecessor(TreeNode root) {
root = root.left;
while (root.right != null) root = root.right;
return root.val;
}
public TreeNode deleteNode(TreeNode root, int key) {
if (root == null) return null;
// delete from the right subtree
if (key > root.val) root.right = deleteNode(root.right, key);
// delete from the left subtree
else if (key < root.val) root.left = deleteNode(root.left, key);
// delete the current node
else {
// the node is a leaf
if (root.left == null && root.right == null) root = null;
// the node is not a leaf and has a right child
else if (root.right != null) {
root.val = successor(root);
root.right = deleteNode(root.right, root.val);
}
// the node is not a leaf, has no right child, and has a left child
else {
root.val = predecessor(root);
root.left = deleteNode(root.left, root.val);
}
}
return root;
}
}
最大公约数
static int gcd(int a,int b) {
return b==0?a:gcd(b,a%b);
}
最小公倍数
static int lcm(int a,int b) {
return a*b/gcd(a,b);
}
全排
题目:输入一个字符串,打印出该字符串中字符的所有排列。你可以以任意顺序返回这个字符串数组,但里面不能有重复元素。
示例:
输入:s = “abc”
输出:[“abc”,“acb”,“bac”,“bca”,“cab”,“cba”]
解题:对于一个长度为 n 的字符串(假设字符互不重复),其排列共有n×(n−1)×(n−2)…×2×1 种方案。
class Solution {
List<String> res = new LinkedList<>();
char[] c;
public String[] permutation(String s) {
c = s.toCharArray();
dfs(0);
return res.toArray(new String[res.size()]);
}
void dfs(int x) {
if(x == c.length - 1) {//注意这里,只需要到len-1
res.add(String.valueOf(c)); // 添加排列方案
return;
}
HashSet<Character> set = new HashSet<>();//这里重点:记录重复的(很巧妙)
for(int i = x; i < c.length; i++) {
if(set.contains(c[i])) continue; // 重复,因此剪枝
set.add(c[i]);
swap(i, x); // 交换,将 c[i] 固定在第 x 位
dfs(x + 1); // 开启固定第 x + 1 位字符
swap(i, x); // 恢复交换
}
}
void swap(int a, int b) {
char tmp = c[a];
c[a] = c[b];
c[b] = tmp;
}
}
并查集
【问题描述】
给定一个长度为 N 的数组 A = [A1,A2,……,AN],数组中有可能有重复出现 的整数。
现在小明要按以下方法将其修改为没有重复整数的数组。小明会依次修改 A2,A3,··· ,AN。
当修改 Ai 时,小明会检查 Ai 是否在 A1 ∼ Ai−1 中出现过。如果出现过,则 小明会给 Ai 加上 1 ;如果新的 Ai 仍在之前出现过,小明会持续给 Ai 加 1 ,直 到 Ai 没有在 A1 ∼ Ai−1 中出现过。
当 AN 也经过上述修改之后,显然 A 数组中就没有重复的整数了。 现在给定初始的 A 数组,请你计算出最终的 A 数组。
【输入格式】
第一行包含一个整数 N。 第二行包含 N 个整数 A1,A2,··· ,AN 。
【输出格式】
输出 N 个整数,依次是最终的 A1,A2,··· ,AN。
【样例输入】
5
2 1 1 3 4
【样例输出】
2
1 3 4 5
https://www.cnblogs.com/noKing/p/8018609.html
public class Main{
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
for(int i=0;i<p.length;i++) {
p[i]=i;
}
for(int i=0;i<n;i++) {
int x = sc.nextInt();
x = find(x);
System.out.print(x+" ");
p[x]=x+1;
}
}
static int[] p=new int[1000000];
//注意注意
static int find(int x) {
if(p[x]==x) {
return x;
}else {
p[x]=find(p[x]);//注意这里
return p[x];
}
}
}
组合C(n,m)
static long[][] c = new long[2005][2005];
static int k=100000007;
static long c(int n, int m) {
if (m == 0 || m == n)
return 1;
if (c[n][m] != 0)
return c[n][m];
return c[n][m] = (c(n - 1, m - 1) + c(n - 1, m)) ;
}
最长递增子序列
import java.util.Scanner;
public class Main{
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int[] arr = new int[10002];//arr数组表示输入的序列
int[] dp = new int[10002];//dp数组中存放上升序列的长度,dp[i]表示以arr[i]结尾的子序列的最大长度
for(int i = 1;i <= n;i++) {//输入序列
int a = sc.nextInt();
arr[i] = a;
}
int result = -1;//记录dp中最大的值
for(int i = 1;i <= n;i++) {//按顺序计算dp[i]的值
dp[i] = 1;//假设该子序列中只有arr[i],故长度为1,即其自身成为一个子序列
for(int j = 1;j < i;j++) {
//如果在i之前有比arr[i]小的数(arr[j]),并且把该数(arr[i])放到以arr[j]结尾的子序列末尾后,
//其长度比当前以arr[i]结尾的子序列长度要长
if(arr[i] > arr[j] && dp[j] + 1 > dp[i]) {
dp[i] = dp[j] + 1;//把arr[i]放到以arr[j]结尾的子序列之后,原来的长度+1
}
}
result = Math.max(result, dp[i]);//找出在dp数组中最大的一个,即子序列长度最长的一个
}
System.out.println(result);
}
}