c++转型java 需要总结 需要记牢的算法(全排列,重写的sort方法 ,动态规划,重复初始化数组

本文介绍了从C++转到Java时需要掌握的算法和编程技巧,包括全排列的三种方法(交换法、前缀法、插入法)、重写sort方法、动态规划、重复初始化数组的操作、自定义pow方法、单个字符输入处理、数组构建二叉树、String.format方法的使用以及replaceAll的正则表达式和二分搜索的应用。

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
*/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值