本章内容包括
1、掌握基础结构的写法:
自定义数据类型的快排
解析:基本数据类型的快排使用:Arrays.sort()方法,譬如电影节这样的问题,程序需要对每部电影的结束时间进行从小到大进行排序。就需要自己在自定义类改写“compareTo()”方法
package summarizeMyWork;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
//此类结构常常出现在贪心算法中,接下来的程序是以“电影节”这个问题为原型展开的:
/*描述
大学生电影节在北大举办! 这天,在北大各地放了多部电影,给定每部电影的放映时间区间,区间重叠的电影不可能同时看(端点可以重合),问李雷最多可以看多少部电影。
输入
多组数据。每组数据开头是n(n<=100),表示共n场电影。
接下来n行,每行两个整数(0到1000之间),表示一场电影的放映区间
n=0则数据结束
输出
对每组数据输出最多能看几部电影
样例输入
8
3 4
0 7
3 8
15 19
15 20
10 15
8 18
6 12
0
样例输出
3
*/
public class 自定义数据类型的快排 {
static class movie implements Comparable<movie>{//这里容易忘记写后面的“implements Comparable<movie>”!
int start;
int end;
public movie(int s,int e) {
this.start=s;
this.end=e;
}
@Override
public String toString() {
return "电影开始时间:"+this.start+"电影结束时间:"+this.end;
}
@Override
public int compareTo(movie next) {
return this.end-next.end;//返回结果为负,即为升序
}
}
public static void main(String[] args) {
movie[] m=new movie[10];
m[0]=new movie(2, 9);
m[1]=new movie(21, 29);
m[2]=new movie(2, 93);
m[3]=new movie(5, 3);
m[4]=new movie(2, 0);
m[5]=new movie(0, 7);
m[6]=new movie(2, 6);
m[7]=new movie(44, 3);
m[8]=new movie(47, 88);
m[9]=new movie(3, 38);
List<movie> list=new ArrayList<movie>(Arrays.asList(m));//对于List这个类,eclips会自动引包,指向awt,需要删掉改成util包!
Collections.sort(list);
//遍历
for(movie e:list) {
System.out.println(e.toString());
}
//排序结果重新赋给数组
int c=0;
for(movie e:list) {
m[c++]=e;
}
//获取链表中的某个对象
System.out.println("获取到的对象 "+list.get(6));
}
}
自定义数据类型的优先队列
package summarizeMyWork;
import java.util.Comparator;
import java.util.PriorityQueue;
import java.util.Queue;
//此类结构常常出现在贪心算法中,接下来的程序是以“畜栏保留问题”这个问题为原型展开的:
/*
* 描述
农场有N头牛,每头牛会在一个特定的时间区间[A, B](包括A和B)在畜栏里挤奶,且一个畜栏里同时只能有一头牛在挤奶。现在农场主希望知道最少几个畜栏能满足上述要求,并要求给出每头牛被安排的方案。对于多种可行方案,主要输出一种即可。
输入
输入的第一行包含一个整数N(1 ≤ N ≤ 50, 000),表示有N牛头;接下来N行每行包含两个数,分别表示这头牛的挤奶时间[Ai, Bi](1 ≤ A≤ B ≤ 1, 000, 000)。
输出
输出的第一行包含一个整数,表示最少需要的畜栏数;接下来N行,第i+1行描述了第i头牛所被分配的畜栏编号(从1开始)。
样例输入
5
1 10
2 4
3 6
5 8
4 7
样例输出
4
1
2
3
2
4
*/
//分析:譬如畜栏保留这样的问题,程序需要设置一个优先队列,按照挤奶时间结束时间早晚存放畜栏。则需要写一个“ new Comparator<>”对象,用于优先队列
public class 自定义数据类型的优先队列 {
static class corral {
int code;
int lasttime;
public corral(int c,int l) {
this.code=c;
this.lasttime=l;
}
@Override
public String toString() {
return "畜栏编码"+this.code+"畜栏结束时间"+this.lasttime;
}
}
static Comparator<corral> comparator=new Comparator<corral>() {
public int compare(corral c1,corral c2) {
return c1.lasttime-c2.lasttime;
}
};
public static void main(String[] args) {
corral[] c=new corral[5];
c[0]=new corral(1, 9);
c[1]=new corral(2, 29);
c[2]=new corral(3, 96);
c[3]=new corral(4, 21);
c[4]=new corral(5, 1);
Queue<corral> queue=new PriorityQueue<corral>(comparator);
for(int i=0;i<5;i++) {
queue.add(c[i]);//对于队列而言,只能通过add操作将数据存入队列,才会进行排序
}
//返回队首元素,基本上常用的就是代码里面使用到的这四个方法
System.out.println(queue.peek());
//与链表不同的是,队列必须采用以下方法进行遍历:
System.out.println(queue.size());
while(!queue.isEmpty()) {
System.out.println(queue.poll());
}
// for(int i=0;i<queue.size();i++) {//,采用这个方法只打印了3个结果,是因为队列的size是动态在变化的
// System.out.println(queue.poll());
// }
}
}
普通数据类型的优先队列
package summarizeMyWork;
import java.util.PriorityQueue;
import java.util.Queue;
public class 普通数据类型的优先队列 {
public static void main(String[] args) {
Queue<Integer> queue=new PriorityQueue<Integer>();
queue.offer(1);
queue.offer(0);
queue.offer(-1);
System.out.println(queue.element());//返回队首元素
while(!queue.isEmpty()) {
System.out.println(queue.poll());
}
}
}
归并排序
package summarizeMyWork;
public class 归并排序 {
//存储归并排序所需的数据必须在全局变量之下
static int[] array=new int[100];
static int[] b=new int[100];
public static void main(String[] args) {
int c=2039;
for(int i=0;i<100;i++) {
array[i]=c--;
}
MergeSort(array,0,99);//与String.substring方法不同的是:归并排序传入的值应当是(start_index,end_index)
for(int i=0;i<100;i++) {
System.out.println(array[i]);
}
}
static void MergeSort(int[] a,int s,int e) {
if(s<e) {
int mid=s+(e-s)/2;
MergeSort(a, s, mid);
MergeSort(a, mid+1, e);
Judge(a,s,mid,e);
}
}
static void Judge(int[] a,int s,int mid,int e) {
int p1=s,p2=mid+1;
int pb=0;
while(p1<=mid&&p2<=e) {
if(a[p1]>=a[p2]) {
b[pb++]=a[p2++];
}
else {
b[pb++]=a[p1++];
//像排列的逆序数这样的问题,只是在这里加了一个ans=mid-p1+1;
}
}
while(p1<=mid){//粗心大意,这些界限容易写错
b[pb++]=a[p1++];
}
while(p2<=e) {
b[pb++]=a[p2++];
}
for(int i=0;i<e-s+1;i++) {
a[s+i]=b[i];
}
}
}
什么时候使用归并排序?
归并排序(MERGE-SORT)是利用归并的思想实现的排序方法,该算法采用经典的分治(divide-and-conquer)策略(分治法将问题分(divide)成一些小的问题然后递归求解,而治(conquer)的阶段则将分的阶段得到的各答案"修补"在一起,即分而治之)。
归并排序最优时间复杂度是O(nlog2n) ,最坏的时间复杂度是O(nlog2n) , 它是稳定性很好的一个排序算法。
在大多数情况下,快速排序的效率都要比归并排序高,那归并排序的优势是什么呢?在我们的快排中!我们的一切操作都是在原有 的列表之上进行操作的,并没有生成新的列表。但是我们的归并排序的时候,我们需要先切分,这时候我们切分之后会形成新的列表,然后是对新的列表进行操作的!这就是区别。在算法中往往不仅仅是对数组进行排序,归并排序运用分治的思想,譬如“排列的逆序数”这样的问题,需要用到分治解决问题时,归并排序就有了天然的优势。
归并排序+自定义数据类型+自定义排序的综合性问题(DNA序列)
题目是:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Scanner;
//3:DNA排序
//查看 提交 统计 提问
//总时间限制: 1000ms 内存限制: 65536kB
//描述
//现在有一些长度相等的DNA串(只由ACGT四个字母组成),请将它们按照逆序对的数量多少排序。
//逆序对指的是字符串A中的两个字符A[i]、A[j],具有i < j 且 A[i] > A[j] 的性质。如字符串”ATCG“中,T和C是一个逆序对,T和G是另一个逆序对,这个字符串的逆序对数为2。
//输入
//第1行:两个整数n和m,n(0<n<=50)表示字符串长度,m(0<m<=100)表示字符串数量
//第2至m+1行:每行是一个长度为n的字符串
//输出
//按逆序对数从少到多输出字符串,逆序对数一样多的字符串按照输入的顺序输出。
//样例输入
//10 6
//AACATGAAGG
//TTTTGGCCAA
//TTTGGCCAAA
//GATCAGATTT
//CCCGGGGGGA
//ATCGATGCAT
//样例输出
//CCCGGGGGGA
//AACATGAAGG
//GATCAGATTT
//ATCGATGCAT
//TTTTGGCCAA
//TTTGGCCAAA
/*
* 这道题知识点是分治归并排序+自定义数据类型+自定义排序
* 描述
现在有一些长度相等的DNA串(只由ACGT四个字母组成),请将它们按照逆序对的数量多少排序。
逆序对指的是字符串A中的两个字符A[i]、A[j],具有i < j 且 A[i] > A[j] 的性质。如字符串”ATCG“中,T和C是一个逆序对,T和G是另一个逆序对,这个字符串的逆序对数为2。
输入
第1行:两个整数n和m,n(0<n<=50)表示字符串长度,m(0<m<=100)表示字符串数量
第2至m+1行:每行是一个长度为n的字符串
输出
按逆序对数从少到多输出字符串,逆序对数一样多的字符串按照输入的顺序输出。*/
public class DNA排序的分治 {
static numm[] num;
static char[] b;
static class numm implements Comparable<numm>{
int code;
int count;
public numm(int c,int co) {
this.code=c;
this.count=co;
}
@Override
public int compareTo(numm next) {
return this.count-next.count;
}
@Override
public String toString () {
return "编号"+code+"序列数"+count;
}
}
public static void main(String[] args) {
Scanner reader=new Scanner(System.in);
int m=reader.nextInt();//认真读题!比如这道题就是先给你列,再给你行
int n=reader.nextInt();
String[] string=new String[n];
char[] a=new char[m];
b=new char[m];
num=new numm[n];
for(int i=0;i<n;i++) {
string[i]=reader.next();
num[i]=new numm(i,0);
for(int j=0;j<m;j++)
a[j]=string[i].charAt(j);
// for(int j=0;j<m-1;j++) {
// for(int k=j+1;k<m;k++) {
// if(string[i].charAt(j)>string[i].charAt(k)) {
// count++;
// num[i].count=count;//注意必须在上面new 对象,在这里new就会出现多个。
// }
// }
// }
MergeSort(a,i,0,m-1);
// System.out.println(num[i].count);
}
List<numm> nList=new ArrayList<numm>(Arrays.asList(num));
Collections.sort(nList);
for(numm e:nList) {
// System.out.print(e.toString()+" ");
System.out.println(string[e.code]);
}
}
static void MergeSort(char[] a,int i,int s,int e) {
if(s<e) {
int mid=s+(e-s)/2;
MergeSort(a,i, s, mid);
MergeSort(a,i, mid+1, e);
Judge(a, i,s,mid, e);
}
}
static void Judge(char[] a,int i,int s,int mid,int e) {
int p1=s,p2=mid+1;//错过,写成p2=e
int pb=0;
while(p1<=mid&&p2<=e) {
if(a[p1]>a[p2]) {
num[i].count+=mid-p1+1;
b[pb++]=a[p2++];
}
else {
b[pb++]=a[p1++];
}
}
while(p1<=mid)
b[pb++]=a[p1++];
while(p2<=e)
b[pb++]=a[p2++];
for(int k=0;k<e-s+1;k++) {
a[s+k]=b[k];
}
}
}
HashMap
package summarizeMyWork;
import java.util.HashMap;
public class hashMap {
public static void main(String[] args) {
// TODO Auto-generated method stub
HashMap<String,String> map=new HashMap<>();
map.put("账号", "密码");
String string1="cqust";
System.out.println(map.containsKey(string1));
System.out.println(map.get("账号"));
}
}
二分查找
package summarizeMyWork;
public class 二分查找 {
public static void main(String[] args) {
// TODO Auto-generated method stub
int[] array=new int[100];
for(int i=0;i<100;i++) {
array[i]=i;
}
//数组是排序好了的。
System.out.println(BinarySort(array,0,99));
}
static int BinarySort(int[] a,int s,int e) {
int left=s,right=e,max=0;
int mid=left+(right-left)/2;
while(left<=right) {
if(mid<87) {//查找小于87的最大的数,具体问题具体分析
max=mid;
left=mid+1;
}
else {
right=mid-1;
}
mid=left+(right-left)/2;
}
return max;
}
}
2、掌握基础知识:
基本数据类型的范围
int型(2 * 10^9)
long型(9 * 10^18)
递归什么时候越界
溢出的意思就是越界,操作系统会给每个进程分配一个最大上限的堆栈空间,操作系统分配给一个进程的栈空间是2M,堆空间在32位机器上是4G。
撇如“数字三角形”这样的问题,如果使用递归求解,算法的时间复杂度 是在2^100 左右。这样的程序不存在计算机可以在一个人的有生之年之内算出来。
中学基本知识——数学公式
0、a%1000007的含义是a/1000007的余数是多少。
1、欧几里得定理(也叫辗转相除法)gcd(a,b)=gcd(a,a%b);
像下面这道题如果还是暴力求解一定会出现越界的情况,如果使用数学公式就很简单gcd(f(2020),f(520))=f(gcd(2020,520));
2、大整数取模:(这个如果想要掌握就需要反复记忆)
(a+b)%n=(a%n+b%n)%n;
(a-b)%n=((a%n-b%n)+n)%n;
(ab)%n=(a%b)(b%n)%n
对于一个大的只能用数组存储的数来说:
因为数字有个规律1234=((1*10+2)*10+3)*10+4
for(int i=0;i<a.lenght;i++){
long ans=(int )(ans 10+a[i]-‘0’)%n;
}
3、奇数,偶数,质数,合数,众数,平均
(1)奇数,又称单数, 整数中,能被2整除的数是偶数,不能被2整除的数是奇数,奇数的个位为1,3,5,7,9。
(2)偶数,整数中,能够被2整除的数,叫做偶数,正的偶数又称双数。偶数包括正偶数、负偶数和0。
(3)质数,又称素数,有无限个。质数定义为在大于1的自然数中,除了1和它本身以外不再有其他因数。
(4)合数,是指自然数中除能被1和本数整除外,还能被其他的数整除的数。
(5)众数(Mode)众数是指一组数据中出现次数最多的那个数据,一组数据可以有多个众数,也可以没有众数。众数是统计学名词,在统计分布上具有明显集中趋势点的数值,代表数据的一般水平(众数可以不存在或多于一个)。
(6)平均数是指在一组数据中所有数据之和再除以数据的个数。平均数是表示一组数据集中趋势的量数,它是反映数据集中趋势的一项指标。
(7)方差计算公式:s^2 =(1/n)[(x1-x0)^2 + (x2-x0)^2 +…+ (xn-x0)^2]
(8)极差计算公式:x=xmax-xmin
(9)标准差=方差的算术平方根
4、正三角、平行四边形、2个菱形、2个梯形、圆形、圆周长、2个弧长、2个扇形、圆柱圆锥侧面积和全面积公式
弧长:l = n(圆心角)× π(圆周率)× r(半径)/180=α(圆心角弧度数)× r(半径)
扇形面积:
圆锥侧面积:圆锥的侧面展开图是扇形,该扇形的半径是母线长L(注意,此处L是圆锥母线的长,不是弧长),扇形的弧长是圆锥底面圆的周长2πr,其中r是底面圆的半径。套用第二条扇形面积公式得
S圆锥侧=L(2πr)/2=πrL。
5、其他偶尔需要用到的就请各位道友务必自行百度。
字符串类型的灵活运用
String类型是Java组算法经常使用的数据类型,对于它的运用也非常得灵活,需要熟练掌握
StringBuilder 、BigInteger、BigDecimal、Integer等类的常用方法。
package summarizeMyWork;
public class 字符串API {
public static void main(String[] args) {
//1、将int转换成string类型
String s=String.valueOf(122*111);
System.out.println(s);
//2、判断字符串是不是以参数指定内容结束
boolean s2=s.endsWith("00");
//3、判断字符串是不是以参数指定内容开始
boolean s3=s.startsWith("1");
System.out.println(s2+" "+s3);
//4、获取子串,常用于校验+隐藏,含头不含尾
String s4=s.substring(0,3);
System.out.println(s4);
//5、!字符串!转换成Int型
int s5=Integer.valueOf(s);
//6、!字符!转换成Int型,也可以用于将字符串转换成整型
int s6=Integer.parseInt(String.valueOf(s.charAt(4)));
System.out.println(s5+" "+s6);
//7、去除开头结尾空格和tab
String s7=s.trim();
//8、将字符串按照给定正则表达式的分割成子串
String[] s8=s.split(" ");
//9、统一转换大小写
String s9=s.toLowerCase();
String s10=s.toUpperCase();
//10、将子一个字符串变为字符数组
char[] c=s.toCharArray();
//将字符数组变为字符串
String s11=new String(c);
//将部分字符串变为string
String s12=new String(c,0,3);
//将整型(整型数组)转成字符串
String s13=" ";
s13+=Integer.toString(c[0]);
//11、按字典顺序比较两个字符串(区分大小写与不区分大小写)
if(s9.compareTo(s10)>0)System.out.println("区分大小写");
if(s9.compareToIgnoreCase(s10)==0)System.out.println("不区分大小写");
//12、判断此字符串第一次/最后一次出现指定字符处的索引
int s14=s.indexOf('1',0);
int s15=s.lastIndexOf('1',0);
//13、替换字符/字符串,不能用字符串替换字符,也不能用字符替换字符串
String s16=s.replace('1', '9');
System.out.println(s16);
//14、对一个字符串进行修改
StringBuilder s17=new StringBuilder();
s17.append("zxcvbnm");
s17.insert(3, "asdf");
s17.setCharAt(0, 'Z');
String s18=s17.toString();
System.out.println(s18);
//15、以二进制返回一个整数的字符串表示形式
String s19=Integer.toBinaryString(10);
String s20=Integer.toHexString(10);
String s21=Integer.toOctalString(10);
}
}