1 ,全排列
方法一 交换法
最好的优化(看核心代码就好)
package algorithm_text;
import java.util.Scanner;
//2021年1月28日下午9:38:58
//writer:apple
public class permu {
static int n;
static int a[]=new int [100];
public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner scanner=new Scanner(System.in);
n=scanner.nextInt();
for(int i=1;i<=n;i++)
{
a[i]=i;
}
perm(1,n);
}
public static void perm(int start,int end)
{
if(start==end)
{
for(int i=1;i<=n;i++)
{
System.out.print(" "+a[i]);
}
System.out.println();
}
else {
for(int i=start;i<=end;i++)
{
swap(start,i);
perm(start+1, end);
swap(start,i);
}
}
}
public static void swap(int i,int j)
{
int t =a[i];
a[i]=a[j];
a[j]=t;
}
}
----交换法应对有重复数据
public class 交换法去重 {
public static void main(String[] args) {
// TODO Auto-generated method stub
char a[]= {'1','2','2','3'};
perm(a,0);
}
private static void perm(char[] a,int star) {
// TODO Auto-generated method stub
if(star==a.length)
{
System.out.println(a);
}
else {
int mark[]=new int[10000];
for(int i=star;i<a.length;i++)
{
if(mark[a[i]]==1) continue;
mark[a[i]]=1;
swap(a,star,i);
perm(a,star+1);
swap(a,star,i);
}
}
}
private static void swap(char[] a, int star, int i) {
// TODO Auto-generated method stub
char temp=a[star];
a[star]=a[i];
a[i]=temp;
}
}
其实还可以使用dfs递归哦~
import java.util.Scanner;
//2021年1月28日下午10:08:01
//writer:apple
public class dfsperm {
static int n;
static int a[]=new int[100];
static int vis[]=new int [100];
static int ans[]=new int [100];
public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner scanner=new Scanner(System.in);
n=scanner.nextInt();
for(int i=1;i<=n;i++)
{
a[i]=i;
}
dfs(1);
}
public static void dfs(int step)
{
if(step==n+1)
{
for(int j=1;j<=n;j++)
{
System.out.print(" "+ans[j]);
}
System.out.println();
}
for(int j=1;j<=n;j++)
{
if(vis[j]==1) continue;
else {
vis[j]=1;
ans[step]=j;
dfs(step+1);
vis[j]=0;
}
}
}
}
方法二 前缀法
public class 前缀法 {
static int count;
public static void main(String[] args) {
// TODO Auto-generated method stub
String str="1234";
perm("",str);
}
public static void perm(String pre,String str)
{
if(pre.length()==str.length()) System.out.println(pre);
for(int i=0;i<str.length();i++)
{
char t=str.charAt(i);
if(cnt(pre,t)<cnt(str,t)) perm(pre+t, str);
}
}
public static int cnt(String str,char t)
{
int c=0;
for(int i=0;i<str.length();i++) if(str.charAt(i)==t) c++;
return c;
}
方法三 插入法
理解简单
缺点,乱序
public static void main(String[] args) {
// TODO Auto-generated method stub
String x="1234";
ArrayList<String> ans=frontperm(x);
System.out.println(ans.size());
System.out.println(ans);
}
private static ArrayList<String> frontperm(String x) {
// TODO Auto-generated method stub
int len=x.length();
ArrayList<String> a=new ArrayList<>();
a.add(x.charAt(0)+"");
for(int i=1;i<len;i++)
{
ArrayList<String> newa=new ArrayList<>();
char c=x.charAt(i);
for(String str:a)//将c插入到每一个字符串的每一个位置
{
String newstr=c+ str;
newa.add(newstr);
for(int j=1;j<str.length();j++)
{
newstr=str.substring(0,j)+c+str.substring(j);
newa.add(newstr);
}
newstr=str+c;
newa.add(newstr);
}
a=newa;
}
return a;
}
三种方法总结:
交换很常用,但是不能维持字典序。
前缀法,可以维持字典序。(通常解决求第几个排列)
插入法用的少,不能维持字典序。
前缀法求第k个排列:
import java.util.*;
public class 前缀法 {
static int count;
static int k;
public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner s=new Scanner(System.in);
String str="1234";
k=s.nextInt();
perm("",str);
}
private static void perm(String prefix,String str) {//前缀字符串和原字符串
// TODO Auto-generated method stub
if(prefix.length()==str.length())
{
k--;
// System.out.println(prefix);
if(k==0)
{
System.out.println(prefix);System.exit(1);
}
}
for(int i=0;i<str.length();i++)
{
char c=str.charAt(i);
if(cot(prefix,c)<cot(str,c))//c字符在前缀中出现的次数小于 在原字符中出现的次数,就继续用c
{
perm(prefix+c,str);
}
}
}
private static int cot(String prefix, char c) {
// TODO Auto-generated method stub
int cc=0;
for(int i=0;i<prefix.length();i++)
{
if(prefix.charAt(i)==c) cc++;
}
return cc;
}
}
2,重写的sort方法
super 常用 方法:
容器中也可以使用
static class rizhi
{
int t;
int id;
public rizhi(int tt,int dd)
{
t=tt;id=dd;
}
}
rizhi ri[]=new rizhi[n];
Arrays.sort(ri,(a,b)->a.t-b.t);//按时间升序
Arrays.sort(ri,(a,b)->a.t-b.t);//按时间升序常用简单版
如果数据类型是double类型,要注意小数部分。
Arrays.sort(m,(a,b)->(b.height-a.height)<=0?-1:1);//下面的也行
Arrays.sort(m,0,n,new Comparator<monbing>() {
public int compare(monbing a,monbing b)
{
return (b.height-a.height)<=0?-1:1;//降序 double a.height
}
});
java中和c++一样有自带的sort方法:(默认递增排列)
Arrays.sort(int[] a, int fromIndex, int toIndex);Arrays.sort(数组名,起始下标,终止下标+1);
如果问题中的排序条件增加就要,重写sort中 compare的方法
package apple;
import java.util.Arrays;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.Scanner;
public class Main {
static class Student
{
int h;
int d;
int c;
public Student(int h,int d,int c)
{
this.h=h;
this.d=d;
this.c=c;
}
}
public static void main(String[] args) {
Scanner scanner=new Scanner(System.in);
int n=scanner.nextInt();
Student []stu=new Student[n+1];
for(int i=1;i<=n;i++)
{
stu[i]=new Student(scanner.nextInt(), scanner.nextInt(),i);
}
//重点重点重点重点重点重点
Arrays.sort(stu,1,n+1,new Comparator<Student>() {
public int compare(Student a ,Student b) {
if(a.h==b.h)
{
if(a.d==b.d)
{
return a.c-b.c;//a-b为升序 b-a为降序
}
return a.d-b.d;
}
return a.h-b.h;
}
});
//重点重点重点重点重点重点
for(int i=1;i<=n;i++)
{
System.out.print(stu[i].c+" ");
}
}
}
也可以更改对象类型
package apple;
import java.util.Arrays;
import java.util.Comparator;
//2021年1月27日下午12:00:25
//writer:apple
public class sort {
static Integer a[]= {6,3,2,5};//无法对integer数组进行,空间初始化,所以不合适
public static void main(String[] args) {
// TODO Auto-generated method stub
//重点重点重点重点重点重点
Arrays.sort(a,0,4,new Comparator<Integer>() {
public int compare(Integer i,Integer j)
{
return j-i;
}
});
//重点重点重点重点重点重点
for(int i:a) System.out.print(i);
System.out.println();
}
}
对int数组排序:
升序:Arrays.sort 默认为升序
降序:Arrays.sort之后再使用for循环
package three;
import java.util.Arrays;
import java.util.Scanner;
//2021年3月3日上午9:30:16
//writer:apple
public class ceshi {
//将b数组 降序排列
public static void main(String[] args) {
// TODO Auto-generated method stub
int a[]=new int [20];
Scanner scanner =new Scanner(System.in);
int n=scanner.nextInt();
for(int i=0;i<n;i++)
{
a[i]=scanner.nextInt();
}
Arrays.sort(a,0,n);//默认为升序
int b[]=new int [20];
for(int i=0;i<n;i++)
{
b[i]=a[n-i-1];
}
for(int i=0;i<n;i++)
{
System.out.println(b[i]);
}
}
}
3,动态规划
01背包dp
核心代码
for(int j=0;j<=T;j++)//边界初始化 可以不需要
{
if(t[1]<=j) dp[1][j]=p[1];
}
for(int i=2;i<=n;i++)
{
for(int j=T;j>=t[i];j--) //在能放进去的情况下找状态就行
{
dp[i][j]=Math.max(dp[i-1][j], dp[i-1][j-t[i]]+p[i]);
}
}
也可以用滚动数组 将二维dp降为一维dp
for(int i=1;i<=n;i++)
{
for(int j=T;j>=t[i];j--)
{
dp[j]=Math.max(dp[j], dp[j-t[i]]+p[i]);
}
}
System.out.println(dp[T]);
完全背包dp和01很像,可以重复放入,不过要注意枚举顺序是正推的
for(int i=1;i<=n;i++) //这是完全背包问题的!
{
for(int j=t[i];j<=T;j++)
{
dp[j]=Math.max(dp[j], dp[j-t[i]]+p[i]);
}
}
多重背包 转换成01背包就行了
多重背包也是 0-1 背包的一个变式。与 0-1 背包的区别在于每种物品 y 有 个,而非 个。
一个很朴素的想法就是:把「每种物品选 次」等价转换为「有 个相同的物品,每个物品选一次」。
这样就转换成了一个 0-1 背包模型,套用上文所述的方法就可已解决。
for(int i=1;i<=n;i++) //这是多重背包问题的!
{
for(int j=T;j>=t[i];j--)
{
for(int k=1;k<=c[i];k++)//c[i]为第i个物品的总数量
{
if((j-t[i]*k)<0) break;
dp[j]=Math.max(dp[j], dp[j-t[i]*k]+p[i]*k);
}
}
}
System.out.println(dp[T]);
区间动态
核心代码:三层循环
for(int len=2;len<=n;len++){//枚举区间长度
for(int i=1;i<n;++i){//枚举区间的起点
int j=i+len-1;//根据起点和长度得出终点
if(j>n) break;//符合条件的终点
for(int k=i;k<=j;++k)//枚举最优分割点
dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+w[i][j]);//状态转移方程
}
}
题目和solution:
n石子合并: len i j k dp[i][j]=max(dp[i][j],dp[i][k]+dp[k+1][j]+a[k])
能量项链: len i j k dp[i][j]=max(dp[i][j],dp[i][k]+dp[k+1][j]+a[k])
矩阵取数: 每行 len left right dp[i][j]=max(dp[i-1][j]+a[i],dp[i][j-1]+a[n-j+1]
例题 石子合并
将堆石子绕圆形操场排放,现要将石子有序地合并成一堆。规定每次只能选相邻的两堆合并成新的一堆,并将新的一堆的石子数记做该次合并的得分。
请编写一个程序,读入堆数 及每堆的石子数,并进行如下计算:
选择一种合并石子的方案,使得做 次合并得分总和最大。
选择一种合并石子的方案,使得做 次合并得分总和最小。
输入格式
输入第一行一个整数 ,表示有 堆石子。
第二行 个整数,表示每堆石子的数量。
输出格式
输出共两行:
第一行为合并得分总和最小值,
第二行为合并得分总和最大值。
package apple;
import java.util.Scanner;
//2021年1月30日下午8:25:36
//writer:apple
public class sectiondptrue {
static int n;
static int a[]=new int[300];
static int sum[]=new int[1000];
static int ansmax[][]=new int [1000][1000];
static int ansmin[][]=new int [1000][1000];
static int max;
static int min=Integer.MAX_VALUE;
public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner scanner=new Scanner(System.in);
n=scanner.nextInt();
for(int i=1;i<=n;i++)
{
a[i]=scanner.nextInt();
a[i+n]=a[i+n*2]=a[i+n*3]=a[i];
}
for(int i=1;i<=4*n;i++)
{
sum[i]=sum[i-1]+a[i];
}
for(int len=2;len<=n;len++)
{
for(int i=1;i<=2*n-1;i++)
{
int j=i+len-1;
if(j>=(n<<1)) break;
ansmin[i][j]=Integer.MAX_VALUE;
for(int k=i;k<j;k++)
{
ansmax[i][j]=Math.max(ansmax[i][j], ansmax[i][k]+ansmax[k+1][j]+sum[j]-sum[i-1]);
ansmin[i][j]=Math.min(ansmin[i][j], ansmin[i][k]+ansmin[k+1][j]+sum[j]-sum[i-1]);
}
}
}
for(int i=1;i<=n;i++)
{
int j=i+n-1;
max=Math.max(ansmax[i][j], max);
min=Math.min(ansmin[i][j],min);
}
System.out.println(min);
System.out.println(max);
}
}
4,重复初始化数组
Arrays.fill()
用法1:接受2个参数
Arrays.fill( a1, value );
boolean[] a1 = new boolean[5];
Arrays.fill( a1,true );
用法2:接受4个参数
例如:
String[] a9 = new String[6];
Arrays.fill(a9, "Hello");
Arrays.fill(a9, 3, 6,"World");//数组 初始下标 终止下标+1 value
结果是 a9[] = {Hello,Hello,Hello,World,World,World};
//初始化:一维和 二维分开初始化
Arrays.fill(a, 0);Arrays.fill(b, 0);Arrays.fill(c, 0);Arrays.fill(vis, 0);
memset();
public static void memset()
{
for(int i=0;i<=n+1;i++)
{
for(int j=0;j<=n+1;j++)
{
connect[i][j]=0;
}
}
}
5,写pow方法
示例 1:
输入: 2.00000, 10
输出: 1024.00000
示例 2:
输入: 2.10000, 3
输出: 9.26100
示例 3:
输入: 2.00000, -2
输出: 0.25000
解释: 2-2 = 1/22 = 1/4 = 0.25
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/powx-n
public double myPow(double x, int n) {
//核心思想主要还是折半,把原本比较大的这个幂变成原来的一半,这样结果就可以通过这个一半的幂相乘来组成了
if (n == 0) {//任何数的0次幂都是1
return 1;
}
//把这个问题转换成更小的问题进行解决
double half = myPow(x,n/2);
//如果是个偶数的话,就正好是一半幂的乘积
if (n%2 == 0) {
return half*half;
}
//如果是奇数并且n>0的话
if(n > 0) {
return half*half*x;
}
return half*half/x;
}
经典的把一个相对较大的问题使用折半的方法转换成较小的问题,代码注释中也写明了解释。
还有另一种相同的思想,只是用循环来实现的,如下:
记住复杂度是logn,所以i一直除以2;
而且x一直重复相乘;
在中途判断,i若为基数就多乘以一个x
public double myPow(double x, int n) {
double res = 1.0;
int i = n;
while (i != 0) {
//如果是奇数,那么还要多乘一个x
if (i%2 != 0) {
res *= x;
}
//偶数直接自我平方
x *= x;
//折半
i /= 2;
}
//判断n的正负来返回正确答案
return n < 0 ? 1/res : res;
6,输入单个字符
第一种方法:通过接收字符串 再接收其第一个字符
常用的方法
import java.util.Scanner; //导包
class T1
{ public static void main(String[] args){
Scanner sc = new Scanner(System.in);
System.out.println("请输入字符或字符串");
char c =sc.next().charAt(0); //只接收字符串的第一个字符
System.out.println(c);
}
}
第二种方法: 同样可以实现录入一个字符的功能
import java.util.*;
public class Test_01
{
public static void main(String[] args)throws Exception
{
System.out.println("请输入一个字符");
char c=(char)System.in.read();
System.out.println(c);
}
}
7,将数组存进二叉树
封装treenode(包含value,left,right)
创建函数createtree
package apple;
import java.util.Scanner;
//import java.lang.*;
class Treenode{
int value;
Treenode left;
Treenode right;
Treenode(int n) {
value=n;
}
}
public class appl {
public static Treenode createtree(Integer []arr,int index)
{
Treenode treenode=null;
if(index<arr.length)
{
Integer a=arr[index];
if(a==null) return null;
else {
treenode=new Treenode(a);
}
treenode.left=createtree(arr, index*2+1);
treenode.right=createtree(arr, index*2+2);
return treenode;
}
return treenode;
}
public static void main(String[] args) {
// TODO Auto-generated method stua
Integer []tree={1,2,3,4,null};
Treenode treenode= createtree(tree,0);
while(treenode.left!=null)
{
System.out.println(treenode.value);
treenode=treenode.left;
}
}
}
**
8,string.format方法:保留小数位数
**
string.format保留小数点位
String s=String.format("%.2f", Ans);
System.err.println(s);
**
9,replaceAll()方法正则表达式
**
public class L0011 {
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
String aa=br.readLine();
String bb=br.readLine();
//A.replaceAll(regex, replacement) 将字符串A按照regex正则表达替换成replacement替换字符。
// 正则:[B]代表匹配B中的任意一个字符
System.out.print(aa.replaceAll("["+bb+"]", ""));
}
}
10,binarySearch() 封装的二分搜索
binarySearch()方法提供了多种重载形式,用于满足各种类型数组的查找需要,binarySearch()有两种参数类型
注:此法为二分搜索法,故查询前需要用sort()方法将数组排序,如果数组没有排序,则结果是不确定的,另外
如果数组中含有多个指定值的元素,则无法保证找到的是哪一个。
⑴.binarySearch(object[ ], object key);
如果key在数组中,则返回搜索值的索引;否则返回-1或者”-“(插入点)。插入点是索引键将要插入数组的那一点,即第一个大于该键的元素索引。
import java.util.Arrays;
public class IntFunction
{
public static void main (String []args)
{
int a[] = new int[] {1, 3, 4, 6, 8, 9};
int x1 = Arrays.binarySearch(a, 5);
int x2 = Arrays.binarySearch(a, 4);
int x3 = Arrays.binarySearch(a, 0);
int x4 = Arrays.binarySearch(a, 10);
System.out.println("x1:" + x1 + ", x2:" + x2);
System.out.println("x3:" + x3 + ", x4:" + x4);
}
}
/*输出结果:
x1:-4, x2:2
x3:-1, x4:-7
*/
1.不存在时由1开始计数;
2.存在时由0开始计数。
⑵.binarySearch(object[ ], int fromIndex, int endIndex, object key);
如果要搜索的元素key在指定的范围内,则返回搜索键的索引;否则返回-1或者”-“(插入点)。
eg:
1.该搜索键在范围内,但不在数组中,由1开始计数;
2.该搜索键在范围内,且在数组中,由0开始计数;
3.该搜索键不在范围内,且小于范围内元素,由1开始计数;
4.该搜索键不在范围内,且大于范围内元素,返回-(endIndex + 1);(特列)
import java.util.Arrays;
public class IntFunction
{
public static void main (String []args)
{
int a[] = new int[] {1, 3, 4, 6, 8, 9};
int x1 = Arrays.binarySearch(a, 1, 4, 5);
int x2 = Arrays.binarySearch(a, 1, 4, 4);
int x3 = Arrays.binarySearch(a, 1, 4, 2);
int x4 = Arrays.binarySearch(a, 1, 3, 10);
System.out.println("x1:" + x1 + ", x2:" + x2);
System.out.println("x3:" + x3 + ", x4:" + x4);
}
}
/*输出结果:
x1:-4, x2:2
x3:-2, x4:-4
*/
本文介绍了从C++转到Java时需要掌握的算法和编程技巧,包括全排列的三种方法(交换法、前缀法、插入法)、重写sort方法、动态规划、重复初始化数组的操作、自定义pow方法、单个字符输入处理、数组构建二叉树、String.format方法的使用以及replaceAll的正则表达式和二分搜索的应用。

被折叠的 条评论
为什么被折叠?



