当前位置: 首页 > 聚焦 > > 内容页

蓝桥杯爪哇速通

2023-04-12 17:33:18 来源: 博客园

蓝桥杯爪哇速通

简单题:枚举、找规律、模拟

复杂题:DP、图论、数论、二分、贪心

数据结构

1. 哈希表

分为HashSetHashMap

Set set=new HashSet();set.add(1);//添加元素set.remove(1);//删除元素if(set.contains(1)) {...}//判断是否包含某个元素int len=set.size();//获得集合长度12345Map map=new HashMap();map.put("a", 1);int num1=map.get("a");int num2=map.getOrDefault("a",0);map.replace("a",2);map.remove("a");123456

2. 堆

使用优先队列(PriorityQueue)实现,默认是小根堆


(相关资料图)

Queue q=new PriorityQueue();//创建一个小根堆Queue q=new PriorityQueue((e1,e2)->e2-e1);//创建一个大根堆//(e1,e2)->e2-e1代表升序,可以自定义其他排序规则

时间相关

1. String转Date

import java.text.ParseException;import java.text.SimpleDateFormat;String s1 = "2021-7-24";String s2 = "2022-7-24";//yyyy-MM-dd,注意MM必须大写SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");// 转换成date1需要抛出异常try {    Date date1 = sdf.parse(s1);} catch (ParseException e) {    e.printStackTrace();}123456789101112

2. Date转String(标准格式化)

Date date = new Date(120000000);SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");System.out.println(sdf.format(date));123

3. Calender类(日历,星期)

Calender的月份MONTH是从0开始,也就是1-12月对应 0-11,但日期和年份是从1开始的。DAY_OF_WEEK从1开始,也就是周日到周六对应 1-7。周日是1,周一是2,周六是7。1月是0,12月是11。

// 创建Calendar实例Calendar c = Calendar.getInstance();// 用日期赋值2021年7月1日,或者是用Date赋值c.setTime(Date date);c.set(2021, 7 - 1, 1);System.out.println(c.get(Calendar.YEAR));//年System.out.println(c.get(Calendar.MONTH) + 1);//月System.out.println(c.get(Calendar.DATE));//日System.out.println(c.get(Calendar.DAY_OF_WEEK));// 星期几,1是星期日// 这年的第几天,从1开始算System.out.println(c.get(Calendar.DAY_OF_YEAR));// 这个月的第几天,从1开始算System.out.println(c.get(Calendar.DAY_OF_MONTH));// 这个月的第几周,从1开始算System.out.println(c.get(Calendar.DAY_OF_WEEK_IN_MONTH));

4. 计算时间间隔

主要是通过使用SimpleDateFormat,先把时间写成String,然后再转成Date, 用getTime函数转成毫秒,相减得到相差的毫秒数。注意1s = 1000ms,SimpleDateFormat中,HH代表24小时制,hh是12小时制,MM是月份,mm是分钟。

String start = "2021-7-13 13:14:20";String end = "2021-7-10 13:14:20";// 注意HH是24小时,hh是12小时SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");Date date1 = sdf.parse(start);Date date2 = sdf.parse(end);// 因为不知道谁大谁小,所以要用abslong diff = Math.abs(date1.getTime() - date2.getTime());System.out.println("相差" + diff + "毫米");// 注意1s = 1000mslong seconds = diff / 1000;//秒long minutes = diff / 1000 / 60;//分钟long hours = diff / 1000 / 60 / 60;//小时long day = diff / 1000 / 60 / 60 / 24;//天

字符串

1.int和String的互相转换

//int转Stringint i=1;  String s=""+i://String转intString s=”3”;int i=Integer.parseInt(s);12345

2.判断一个字符串是否是回文

方法一:判断字符串的i到j位是否是回文

public static boolean isPalindrome(String s, int i, int j){        while(i

方法二:快速判断,一个数字是否是回文

class Solution {    public boolean isPalindrome(int x) {        StringBuffer s=new StringBuffer(Integer.toString(x));        String a=s.toString();//创建一个新字符串记录原来的值        return a.equals(s.reverse().toString())?true:false;    }}

BigInteger与BigDecimal

1.BigInteger

import java.math.BigInteger;import java.util.Scanner;public class Main {    public static void main(String[] args) {        // 传入字符串才能形成大数,默认是把字符串当成十进制        BigInteger bs = new BigInteger("15666666666666666");        BigInteger bs1 = new BigInteger("100002123123123");        //加减乘除        bs.add(bs1);bs.subtract(bs1);bs.multiply(bs1);bs.divide(bs1);//取商                // 取余        bs.mod(bs1);bs.remainder(bs1);        // 返回大整数的double、float类型        bs.doubleValue();bs.floatValue();        // 求最大公约数        bs.gcd(bs1);        // 9、将当前大整数转成十进制字符串形式        System.out.println(bs.toString());        // 也可以把字符串当成二进制传入,生成十进制大数        BigInteger bs2 = new BigInteger("100000", 2);        System.out.println(bs2);    }}1234567891011121314151617181920212223

2.BigDecimal

对BigDecimal做加、减、乘时,精度不会丢失,但是做除法时,存在无法除尽的情况,这时,就必须指定精度以及如何进行截断。

package Chapter_5;import java.math.BigDecimal;import java.math.BigInteger;import java.math.RoundingMode;import java.util.Scanner;public class Main {    public static void main(String[] args) {        Scanner scan = new Scanner(System.in);        // 大小数        BigDecimal bs = scan.nextBigDecimal();        // 获取小数位数,如果是整数则输出负数,表示末尾0的个数        System.out.println(bs.scale());        // 去掉小数末尾无用的0        System.out.println(bs.stripTrailingZeros());        // 设置小数位数,可以选择四舍五入或者直接截断        System.out.println(bs.setScale(4, RoundingMode.HALF_UP));  // 四舍五入        // 对BigDecimal做加、减、乘时,精度不会丢失,但是做除法时,存在无法除尽的情况,这时,就必须指定精度以及如何进行截断。        BigDecimal d1 = new BigDecimal("123.456");        BigDecimal d2 = new BigDecimal("23.456789");        BigDecimal d3 = d1.divide(d2, 10, RoundingMode.HALF_UP); //保留10位小数并四舍五入        BigDecimal d4 = d1.divide(d2); // 报错:ArithmeticException,因为除不尽        //比较两个BigDecimal,不能用equals()因为小数的个数问题,要用compareTo()        //它根据两个值的大小分别返回负数、正数和0,分别表示小于、大于和等于        d1.compareTo(d2)    }}

质数和公约数

1.判断一个数是否是质数

假设该数为n, 我们只需要判断[2,sqrt{n}]内是否有n的因子。如果有,则n为合数,否则,n为质数。这种方法被称为试除法, 即试着除一下所有可能的因子。

public static Boolean isprime(int n){    if(n == 1) return false;    for(int i = 2; i<= n/i ; i++){        if(n % i == 0){            return false;        }    }    return true;}123456789

2.求两个数的最大公约数

//递归(返回最大公约数)int gcd(int a,int b){    return b==0?a:gcd(b,a%b);}

3.求两个数最小公倍数

//递归(返回最大公约数)int gcd(int a,int b){    return b==0?a:gcd(b,a%b);}// 借用gcdint lcm(int a, int b){return a * b / gcd(a,b);}

4.分解质因数

设一个质数为p.如果n%p == 0,那么p就是n的一个质因数,接下来就是求p的指数,我们让n = n/p, 这样就从n中剔除了一个p,接着重复上述两步,直到n%p != 0

public static void prime(int n){    for(int i = 2; i <= n/i ; i++){//判断条件用n/i,以防i*i<=n发生溢出        int a = 0;//每次循环都要清零        while(n % i == 0){            n /= i;            a++;        }        if(a > 0)            System.out.println(i + "" + a);//i的a次方    }    //若该数是质数,那么应该将自身(n)也加入    if(n > 1) System.out.println(n + " " + 1);

BFS和回溯DFS框架

回溯DFS

回溯算法框架。解决一个回溯问题,实际上就是一个决策树的遍历过程。你只需要思考 3 个问题:1、路径:也就是已经做出的选择。2、选择列表:也就是你当前可以做的选择。3、结束条件:也就是到达决策树底层,无法再做选择的条件。

代码方面,回溯算法的框架:result = [] def backtrack(路径, 选择列表):    if 满足结束条件:        result.add(路径)        return    for 选择 in 选择列表:        做选择        backtrack(路径, 选择列表)        撤销选择

BFS

本质上就是一幅「图」,让你从一个起点,走到终点,问最短路径,这就是 BFS 的本质。图可能会走回头路,所以要用visited数组存储已经访问过的节点。

/ 计算从起点 start 到终点 target 的最近距离int BFS(Node start, Node target) {    Queue q; // 核心数据结构    HashSet visited; // 保存已经访问过的节点,避免走回头路    q.offer(start); // 将起点加入队列    visited.add(start);    int step = 0; // 记录扩散的步数    while (!q.isEmpty) {        int sz = q.size();        /* 将当前队列中的所有节点向四周扩散 */        for (int i = 0; i < sz; i++) {            Node cur = q.poll();            /* 划重点:这里判断是否到达终点 */            if (cur is target)                return step;            /* 将 cur 的相邻节点加入队列 */            for (Node x : cur.adj())                if (x not in visited) {                    q.offer(x);                    visited.add(x);                }        }        /* 划重点:更新步数在这里 */        step++;    }}

进制转换

/** 十进制转其他进制* Integer.toBinaryString 返回 int 的 二进制 String* Integer.toOctalString 返回 int 的 八进制 String* Integer.toHexString 返回 int 的 十六进制 String* Integer.toString 返回 int 的 X进制 String(两个参数,第一个对应 int 第二个对应X)* */String s1 = Integer.toBinaryString(16);System.out.println(s1);String s2 = Integer.toOctalString(16);System.out.println(s2);String s3 = Integer.toHexString(16);System.out.println(s3);String s4 = Integer.toString(16, 9);System.out.println(s4);/** 其他进制转十进制* Integer.parseInt 返回 X进制的String 的 十进制 Integer(两个参数, 第一个对应String 第二个对应X)* Integer.valueOf 返回 X进制的String 的 十进制 int (两个参数, 第一个对应String 第二个对应X)* */String s5 = "10010";System.out.println(Integer.parseInt(s5, 2));String s6 = "761";System.out.println(Integer.valueOf(s6, 8));

快速幂

  • 快速幂用处: O(log n)复杂度计算a^n的值(常用于n可能很大时)
  • 原理:分治
  • 如果n是奇数,则计算a^floor(n/2),然后将这个数平方,并乘多一个a。
  • 如果n是偶数,则计算a^(n/2),然后将这个数平方。
  • 不断递归下去
  • 复杂度低,不会爆栈。
// 递归版本long long fastpow2(long long a,int n){if(n==1)return a;long long tmp = fastpow2(a,n: n/2);if(n%2==1) return tmp*tmp*a;else return tmp*tmp;}
// 非递归版本 更优long long fastpow1 (long long a,int n){long long ans = 1;while(n){if(n&1) ans*=a;a*=a;n>>=1;}return ans;}

线性筛法求质数

private static int cnt;    private static int[] primes = new int[10010];    private static boolean[] st = new boolean[10010];    public static void get_primes(int n){        for(int i = 2; i <= n; i++){            if(!st[i]) primes[cnt++] = i;            for(int j = 0; primes[j] <= n / i; j++){                st[primes[j] * i] = true;                if(i % primes[j] == 0) break;            }        }    }

算术基本定理

又称为正整数的唯一分解定理,即:每个大于1的自然数,若不是本身就是质数,就是可写为2个以上的质数的积

这个定理又叫做算术基本定理(或者叫正整数的唯一分解定理),是数论中非常重要的一个定理。它的表述是:

任何一个大于1的正整数n,都可以唯一地写成n=p1α1*p2α2...pk^αk的形式,其中p1,p2,...,pk是不同的质数,α1,α2,...,αk是正整数。

这个定理的意思是,任何一个大于1的正整数n,都可以写成若干个质数的乘积的形式,并且这种分解方式是唯一的。例如,60可以分解为2^2 * 3^1 * 5^1,其中2、3、5是不同的质数,2的指数为2,3的指数为1,5的指数为1。

对于一个大于1正整数n可以分解质因数:n=p1^α1*p2^α2...*pk^αkn的正约数的个数就是(a1+1)(a2+1)(a3+1)…(ak+1)。其中a1、a2、a3…akp1、p2、p3,…pk的指数。证明:n可以分解质因数:n=p1^α1*p2^α2...*pk^αk由约数定义可知p1^α1的约数有:p1^0, p1^1, p1^2, ...,p1^α1,共(a1+1)个,同理p2的约数有(a2+1)个;……;pk^αk的约数有(ak+1)个。

根据乘法原理:n的约数的个数就是 (a1+1)(a2+1)(a3+1)…(ak+1)。例:将378000分解质因数378000=2^4×3^3×5^3×7^1由约数个数定理可知378000共有正约数(4+1)×(3+1)×(3+1)×(1+1)=160个。

知道约数个数,然后就可以求约数之和一个数所有约数之和等于:先把每个质因数从0次幂一直加到其最高次幂,再把每个相应质因数幂的和相乘.若一个数分解为(a^m)*(b^n),则这个数所有约数的和为:(a^0+a^1+a^2+a^3+…+a^m)(b^0+b^1+b^2+b^3+…+b^n).例如:(1)12=2²*3,则12所有约数的和为:(2^0+2^1+2^2)*(3^0+3^1)=7*4=28;(2)60=2²*3*5=(2^0+2^1+2^2)*(3^0+3^1)*(5^0+5^1)=7*4*6=168.

Java Lambda 表达式

Java Lambda 表达式是 Java 8 中引入的一种新特性,它可以看作是一种简化了的函数定义形式,可以使代码更加简洁、易读。

Lambda 表达式的基本语法如下:

(parameters) -> expression或(parameters) -> { statements; }

其中,parameters 表示 Lambda 表达式的参数列表,可以为空或包含一个或多个参数,多个参数之间用逗号隔开。expression 或 statements 表示 Lambda 表达式的函数体,可以是一个表达式或一个语句块。

下面是一个使用 Lambda 表达式的例子,假设有一个列表 Listnumbers,我们要对其进行排序,并输出排序结果:

List numbers = Arrays.asList(3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5);Collections.sort(numbers, (a, b) -> a.compareTo(b));System.out.println(numbers);

这里使用了 Collections.sort() 方法对列表进行排序,第二个参数使用了 Lambda 表达式,其中 (a, b) -> a.compareTo(b) 表示一个比较函数,它的作用是比较两个数的大小。这个比较函数使用了箭头符号 ->,箭头符号左侧是参数列表,右侧是函数体,这个比较函数的函数体是 a.compareTo(b),它表示比较 a 和 b 的大小,并返回比较结果。

Lambda 表达式可以用在很多地方,例如函数式接口、Stream API、并发编程等。它可以使代码更加简洁、易读,同时也可以提高代码的性能和可维护性。

Collection

Collection是 Java 中的接口,被多个泛型容器接口所实现。在这里,Collection是指代存放对象类型的数据结构。

Java 中的 Collection元素类型定义时必须为对象,不能为基本数据类型。

以下内容用法均基于 Java 里多态的性质,均是以实现接口的形式出现。

常用的接口包括 ListQueueSetMap

容器定义

  1. 当定义泛型容器类时,需要在定义时指定数据类型。

例如:

List list1 = new LinkedList<>();

倘若不指定数据类型,而当成 Object类型随意添加数据,在 Java 8 中虽能编译通过,但会有很多警告风险。

如果是明确了类型如 List,此时编译器会检查放入的数据类型,只能放入整数的数据。声明集合变量时只能使用包装类型 List或者自定义的 Class,而不能是基本类型如 List

List

ArrayList

ArrayList是支持可以根据需求动态生长的数组,初始长度默认为 10。如果超出当前长度便扩容 。

初始化
import java.io.PrintWriter;import java.util.ArrayList;import java.util.List;public class Main {    static PrintWriter out = new PrintWriter(System.out);    public static void main(String[] args) {        List list1 = new ArrayList<>();  // 创建一个名字为 list1 的可自增数组,初始长度为默认值(10)        List list2 = new ArrayList<>(30);  // 创建一个名字为list2的可自增数组,初始长度为 30        List list3 = new ArrayList<>(list2);  // 创建一个名字为 list3 的可自增数组,使用 list2 里的元素和 size 作为自己的初始值    }}

LinkedList

LinkedList是双链表。

初始化
import java.io.PrintWriter;import java.util.LinkedList;import java.util.List;public class Main {    static PrintWriter out = new PrintWriter(System.out);    public static void main(String[] args) {        List list1 = new LinkedList<>();  // 创建一个名字为 list1 的双链表        List list2 = new LinkedList<>(list1);  // 创建一个名字为 list2 的双链表,将 list1 内所有元素加入进来    }}

常用方法

以下均用 this代替当前 List

函数名功能
size()返回 this 的长度
add(Integer val)在 this 尾部插入一个元素
add(int idx, Integer e)在 this 指定位置插入一个元素
get(int idx)返回 this 中第 idx 位置的值,若越界则抛出异常
set(int idx, Integer e)修改 this 中第 idx 位置的值

使用案例及区别对比:

import java.io.PrintWriter;import java.util.ArrayList;import java.util.LinkedList;import java.util.List;public class Main {    static PrintWriter out = new PrintWriter(System.out);    static List array = new ArrayList<>();    static List linked = new LinkedList<>();    static void add() {        array.add(1);  // 时间复杂度为 O(1)        linked.add(1);  // 时间复杂度为 O(1)    }    static void get() {        array.get(10);  // 时间复杂度为 O(1)        linked.get(10);  // 时间复杂度为 O(11)    }    static void addIdx() {        array.add(0, 2);  // 最坏情况下时间复杂度为 O(n)        linked.add(0, 2);  // 最坏情况下时间复杂度为 O(n)    }    static void size() {        array.size();  // 时间复杂度为 O(1)        linked.size();  // 时间复杂度为 O(1)    }    static void set() {  // 该方法返回值为原本该位置元素的值        array.set(0, 1);  // 时间复杂度为 O(1)        linked.set(0, 1);  // 最坏时间复杂度为 O(n)    }}

遍历

import java.io.PrintWriter;import java.util.ArrayList;import java.util.Iterator;import java.util.LinkedList;import java.util.List;public class Main {    static PrintWriter out = new PrintWriter(System.out);    static List array = new ArrayList<>();    static List linked = new LinkedList<>();       static void function1() {  // 朴素遍历        for (int i = 0; i < array.size(); i++) {            out.println(array.get(i));  // 遍历自增数组,复杂度为 O(n)        }        for (int i = 0; i < linked.size(); i++) {            out.println(linked.get(i));  // 遍历双链表,复杂度为 O(n^2),因为 LinkedList 的 get(i) 复杂度是 O(i)        }    }    static void function2() {  // 增强 for 循环遍历        for (int e : array) {            out.println(e);        }        for (int e : linked) {            out.println(e);  // 复杂度均为 O(n)        }    }    static void function3() {  // 迭代器遍历        Iterator iterator1 = array.iterator();        Iterator iterator2 = linked.iterator();        while (iterator1.hasNext()) {            out.println(iterator1.next());        }        while (iterator2.hasNext()) {            out.println(iterator2.next());        }  // 复杂度均为 O(n)    }}

注意

不要在 for/foreach遍历 List的过程中删除其中的元素,否则会抛出异常。

原因也很简单,list.size()改变了,但在循环中已循环的次数却是没有随之变化。原来预计在下一个 index的数据因为删除的操作变成了当前 index的数据,运行下一个循环时操作的会变为原来预计在下下个 index的数据,最终会导致操作的数据不符合预期。

Queue

LinkedList

可以使用 LinkedList实现普通队列,底层是链表模拟队列。

初始化
Queue q = new LinkedList<>();

LinkedList底层实现了 List接口与 Deque接口,而 Deque接口继承自 Queue接口,所以 LinkedList可以同时实现 ListQueue

ArrayDeque

可以使用 ArrayDeque实现普通队列,底层是数组模拟队列。

初始化
Queue q = new ArrayDeque<>(); 
ArrayDeque底层实现了 Deque接口,而 Deque接口继承自 Queue接口,所以 ArrayDeque可以实现 Queue

LinkedList 与 ArrayDeque 在实现 Queue 接口上的区别

  1. 数据结构:在数据结构上,ArrayDequeLinkedList都实现了 Java Deque 双端队列接口。但 ArrayDeque没有实现了 Java List 列表接口,所以不具备根据索引位置操作的行为。
  2. 线程安全ArrayDequeLinkedList都不考虑线程同步,不保证线程安全。
  3. 底层实现:在底层实现上,ArrayDeque是基于动态数组的,而 LinkedList是基于双向链表的。
  4. 在遍历速度上ArrayDeque是一块连续内存空间,基于局部性原理能够更好地命中 CPU 缓存行,而 LinkedList是离散的内存空间对缓存行不友好。
  5. 在操作速度上ArrayDequeLinkedList的栈和队列行为都是 时间复杂度,ArrayDeque的入栈和入队有可能会触发扩容,但从均摊分析上看依然是 时间复杂度。
  6. 额外内存消耗上ArrayDeque在数组的头指针和尾指针外部有闲置空间,而 LinkedList在节点上增加了前驱和后继指针。

PriorityQueue

PriorityQueue是优先队列,默认是小根堆。

初始化
Queue q1 = new PriorityQueue<>();  // 小根堆 Queue q2 = new PriorityQueue<>((x, y) -> {return y - x;});  // 大根堆 `

常用方法

以下均用 this代替当前 Queue:

函数名功能
size()返回 this 的长度
add(Integer val)入队
offer(Integer val)入队
isEmpty()判断队列是否为空,为空则返回 true
peek()返回队头元素
poll()返回队头元素并删除

使用案例及区别对比:

import java.io.PrintWriter;import java.util.LinkedList;import java.util.PriorityQueue;import java.util.Queue;public class Main {    static PrintWriter out = new PrintWriter(System.out);    static Queue q1 = new LinkedList<>();    static Queue q2 = new PriorityQueue<>();    static void add() {  // add 和 offer 功能上没有差距,区别是是否会抛出异常        q1.add(1);  // 时间复杂度为 O(1)        q2.add(1);  // 时间复杂度为 O(logn)    }    static void isEmpty() {        q1.isEmpty();  // 时间复杂度为 O(1)        q2.isEmpty();  // 空间复杂度为 O(1)    }    static void size() {        q1.size();  // 时间复杂度为 O(1)        q2.size();  // 返回 q2 的长度    }    static void peek() {        q1.peek();  // 时间复杂度为 O(1)        q2.peek();  // 时间复杂度为 O(logn)    }    static void poll() {        q1.poll();  // 时间复杂度为 O(1)        q2.poll();  // 时间复杂度为 O(logn)    }}

遍历

import java.io.PrintWriter;import java.util.LinkedList;import java.util.PriorityQueue;import java.util.Queue;public class Main {    static PrintWriter out = new PrintWriter(System.out);    static Queue q1 = new LinkedList<>();    static Queue q2 = new PriorityQueue<>();    static void test() {        while (!q1.isEmpty()) {  // 复杂度为 O(n)            out.println(q1.poll());        }        while (!q2.isEmpty()) {  // 复杂度为 O(nlogn)            out.println(q2.poll());        }    }}

实现一个泛型为Pair的PriorityQueue,以Pair的第二个元素大小进行排序

import java.util.Comparator;import java.util.PriorityQueue;public class PairPriorityQueue {    public static void main(String[] args) {        PriorityQueue> pq = new PriorityQueue<>(new Comparator>() {            @Override            public int compare(Pair p1, Pair p2) {                return p1.getValue().compareTo(p2.getValue());            }        });        pq.offer(new Pair<>(1, 3));        pq.offer(new Pair<>(2, 2));        pq.offer(new Pair<>(3, 1));        while (!pq.isEmpty()) {            System.out.println(pq.poll());        }    }    static class Pair {        private final K key;        private final V value;        public Pair(K key, V value) {            this.key = key;            this.value = value;        }        public K getKey() {            return key;        }        public V getValue() {            return value;        }        @Override        public String toString() {            return "[" + key + ", " + value + "]";        }    }}

或者实现如下:

import java.util.*;public class PriorityQueueDemo {    public static void main(String[] args) {        // 创建一个泛型为的PriorityQueue,并自定义Comparator        PriorityQueue> pq = new PriorityQueue<>(new Comparator>() {            @Override            public int compare(Map.Entry o1, Map.Entry o2) {                return o2.getValue() - o1.getValue();            }        });        // 添加测试数据        pq.add(new AbstractMap.SimpleEntry<>("hello", 2));        pq.add(new AbstractMap.SimpleEntry<>("world", 3));        pq.add(new AbstractMap.SimpleEntry<>("java", 1));        pq.add(new AbstractMap.SimpleEntry<>("programming", 4));        // 输出队列中的元素        while (!pq.isEmpty()) {            Map.Entry entry = pq.poll();            System.out.println(entry.getKey() + ": " + entry.getValue());        }    }}

Set

Set是保持容器中的元素不重复的一种数据结构。

HashSet

随机位置插入的 Set

初始化
Set s1 = new HashSet<>(); 

LinkedHashSet

保持插入顺序的 Set

初始化
Set s2 = new LinkedHashSet<>(); 

TreeSet

保持容器中元素有序的 Set,默认为升序。

初始化
Set s3 = new TreeSet<>(); Set s4 = new TreeSet<>((x, y) -> {return y - x;});  // 降序 

常用方法

以下均用 this代替当前 Set:

函数名功能
size()返回 this 的长度
add(Integer val)插入一个元素进 this
contains(Integer val)判断 this 中是否有元素 val
addAll(Collection e)将一个容器里的所有元素添加进 this
retainAll(Collection e)将 this 改为两个容器内相同的元素
removeAll(Collection e)将 this 中与 e 相同的元素删除

使用案例:求并集、交集、差集。

import java.io.PrintWriter;import java.util.HashSet;import java.util.LinkedHashSet;import java.util.Set;public class Main {    static PrintWriter out = new PrintWriter(System.out);    static Set s1 = new HashSet<>();    static Set s2 = new LinkedHashSet<>();    static void add() {        s1.add(1);    }    static void contains() {  // 判断 set 中是否有元素值为 2,有则返回 true,否则返回 false        s1.contains(2);    }    static void test1() {  // s1 与 s2 的并集        Set res = new HashSet<>();        res.addAll(s1);        res.addAll(s2);    }    static void test2() {  // s1 与 s2 的交集        Set res = new HashSet<>();        res.addAll(s1);        res.retainAll(s2);    }    static void test3() {  // 差集:s1 - s2        Set res = new HashSet<>();        res.addAll(s1);        res.removeAll(s2);    }}

遍历

import java.io.PrintWriter;import java.util.HashSet;import java.util.LinkedHashSet;import java.util.Set;public class Main {    static PrintWriter out = new PrintWriter(System.out);    static Set s1 = new HashSet<>();    static Set s2 = new LinkedHashSet<>();    static void test() {        for (int key : s1) {            out.println(key);        }        out.close();    }}

实现一个泛型为Pair的TreeSet,以Pair的第二个元素大小进行排序

import java.util.Comparator;import java.util.TreeSet;public class PairTreeSet {    public static void main(String[] args) {        Comparator> comparator = new Comparator>() {            @Override            public int compare(Pair p1, Pair p2) {                return p1.getValue().compareTo(p2.getValue());            }        };        TreeSet> set = new TreeSet<>(comparator);        set.add(new Pair<>(1, 3));        set.add(new Pair<>(2, 2));        set.add(new Pair<>(3, 1));        for (Pair pair : set) {            System.out.println(pair.getKey() + " " + pair.getValue());        }    }}class Pair {    private final K key;    private final V value;    public Pair(K key, V value) {        this.key = key;        this.value = value;    }    public K getKey() {        return key;    }    public V getValue() {        return value;    }}

Map

Map是维护键值对 的一种数据结构,其中 Key唯一。

HashMap

随机位置插入的 Map

初始化
Map map1 = new HashMap<>();

LinkedHashMap

保持插入顺序的 Map

初始化
Map map2 = new LinkedHashMap<>(); 

TreeMap

保持 key有序的 Map,默认升序。

初始化
Map map3 = new TreeMap<>(); Map map4 = new TreeMap<>((x, y) -> {return y - x;});  // 降序 

常用方法

以下均用 this代替当前 Map

函数名功能
put(Integer key, Integer value)插入一个元素进 this
size()返回 this 的长度
containsKey(Integer val)判断 this 中是否有元素 key 为 val
get(Integer key)将 this 中对应的 key 的 value 返回
keySet将 this 中所有元素的 key 作为集合返回

使用案例:

import java.io.PrintWriter;import java.util.HashMap;import java.util.LinkedHashMap;import java.util.Map;import java.util.TreeMap;public class Main {    static PrintWriter out = new PrintWriter(System.out);    static Map map1 = new HashMap<>();    static Map map2 = new LinkedHashMap<>();    static Map map3 = new TreeMap<>();    static Map map4 = new TreeMap<>((x,y)->{return y-x;});    static void put(){  // 将 key 为 1、value 为 1 的元素返回        map1.put(1, 1);    }    static void get(){  // 将 key 为 1 的 value 返回        map1.get(1);    }    static void containsKey(){  // 判断是否有 key 为 1 的键值对        map1.containsKey(1);    }    static void KeySet(){        map1.keySet();    }}

遍历

import java.io.PrintWriter;import java.util.HashMap;import java.util.Map;public class Main {    static PrintWriter out = new PrintWriter(System.out);    static Map map1 = new HashMap<>();    static void print() {        for (int key : map1.keySet()) {            out.println(key + " " + map1.get(key));        }    }}

当然,在面向对象的世界里,你的参数是什么都可以,包括 Collection与自定义类型。

例如 Map也可以定义为:

Map> map = new HashMap<>(); 

泛型为,按第String大小进行降序排序

import java.util.*;public class TreeMap {    public static void main(String[] args) {        // 创建一个泛型为的TreeMap,并自定义Comparator       TreeMap map = new TreeMap<>(new Comparator() {            @Override            public int compare(String s1, String s2) {                return s1.compareTo(s2); // 降序排序            }        });        }    }}

Arrays

Arraysjava.util中对数组操作的一个工具类。方法均为静态方法,可使用类名直接调用。

Arrays.sort()

Arrays.sort()是对数组进行的排序的方法,主要重载方法如下:

import java.util.Arrays;import java.util.Comparator;public class Main {    static int a[] = new int[10];    static Integer b[] = new Integer[10];    static int firstIdx, lastIdx;    public static void main(String[] args) {        Arrays.sort(a);  // 1        Arrays.sort(a, firstIdx, lastIdx);  // 2        Arrays.sort(b, new Comparator() {  // 3            @Override            public int compare(Integer o1, Integer o2) {                return o2 - o1;            }        });        Arrays.sort(b, firstIdx, lastIdx, new Comparator() {  // 4            @Override            public int compare(Integer o1, Integer o2) {                return o2 - o1;            }        });        // 由于 Java 8 后有 Lambda 表达式,第三个重载及第四个重载亦可写为        Arrays.sort(b, (x, y) -> {  // 5            return y - x;        });        Arrays.sort(b, (x, y) -> {  // 6            return y - x;        });    }}

序号所对应的重载方法含义:

  1. 对数组 a 进行排序,默认升序。
  2. 对数组 a 的指定位置进行排序,默认升序,排序区间为左闭右开 [firstIdx, lastIdx)
  3. 对数组 a 以自定义的形式排序,第二个参数 -第一个参数为降序,第一个参数 -第二个参数为升序,当自定义排序比较器时,数组元素类型必须为对象类型。
  4. 对数组 a 的指定位置进行自定义排序,排序区间为左闭右开 [firstIdx, lastIdx),当自定义排序比较器时,数组元素类型必须为对象类型。
  5. 和 3 同理,用 Lambda 表达式优化了代码长度。
  6. 和 4 同理,用 Lambda 表达式优化了代码长度。

Arrays.sort()底层函数:

  1. 当你 Arrays.sort的参数数组元素类型为基本数据类型(byteshortcharintlongdoublefloat)时,默认为 DualPivotQuicksort(双轴快排),复杂度最坏可以达到 。
  2. 当你 Arrays.sort的参数数组元素类型为非基本数据类型时),则默认为 legacyMergeSortTimSort(归并排序),复杂度为。

可以通过如下代码验证:

Codeforces 1646B - Quality vs Quantity

题意概要:有n个数,你需要将其分为 2 组,是否能存在 1 组的长度小于另 1 组的同时和大于它。

例题代码

import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.io.PrintWriter;import java.util.Arrays;import java.util.StringTokenizer;public class Main {    static class FastReader {        StringTokenizer st;        BufferedReader br;        public FastReader() {            br = new BufferedReader(new InputStreamReader(System.in));        }        String next() {            while (st == null || !st.hasMoreElements()) {                try {                    st = new StringTokenizer(br.readLine());                } catch (IOException e) {                    e.printStackTrace();                }            }            return st.nextToken();        }        int nextInt() {            return Integer.parseInt(next());        }        long nextLong() {            return Long.parseLong(next());        }        double nextDouble() {            return Double.parseDouble(next());        }        String nextLine() {            String str = "";            try {                str = br.readLine();            } catch (IOException e) {                e.printStackTrace();            }            return str;        }    }    static PrintWriter out = new PrintWriter(System.out);    static FastReader in = new FastReader();    static void solve() {        int n = in.nextInt();        Integer a[] = new Integer[n + 10];        for (int i = 1; i <= n; i++) {            a[i] = in.nextInt();        }        Arrays.sort(a, 1, n + 1);        long left = a[1];        long right = 0;        int x = n;        for (int i = 2; i < x; i++, x--) {            left = left + a[i];            right = right + a[x];            if (right > left) {                out.println("YES");                return;            }        }        out.println("NO");    }    public static void main(String[] args) {        int t = in.nextInt();        while (t-- > 0) {            solve();        }        out.close();    }}

如果你将以上代码的 a 数组类型由 Integer修改为 int,会导致 TLE。

Arrays.binarySearch()

Arrays.binarySearch()是对数组连续区间进行二分搜索的方法,前提是数组必须有序,时间复杂度为 ,主要重载方法如下:

import java.util.Arrays;public class Main {    static int a[] = new int[10];    static Integer b[] = new Integer[10];    static int firstIdx, lastIdx;    static int key;    public static void main(String[] args) {        Arrays.binarySearch(a, key);  // 1        Arrays.binarySearch(a, firstIdx, lastIdx, key);  // 2    }}

源码如下:

private static int binarySearch0(int[] a, int fromIndex, int toIndex, int key) {        int low = fromIndex;        int high = toIndex - 1;        while (low <= high) {            int mid = (low + high) >>> 1;            int midVal = a[mid];            if (midVal < key)                low = mid + 1;            else if (midVal > key)                high = mid - 1;            else                return mid; // key found        }        return -(low + 1);  // key not found.    }

序号所对应的重载方法含义:

  1. 从数组 a 中二分查找是否存在 key,如果存在,便返回其下标。若不存在,则返回一个负数。
  2. 从数组 a 中二分查找是否存在 key,如果存在,便返回其下标,搜索区间为左闭右开 [firstIdx,lastIdx)。若不存在,则返回一个负数。

Arrays.fill()

Arrays.fill()方法将数组中连续位置的元素赋值为统一元素。其接受的参数为数组、fromIndextoIndex和需要填充的数。方法执行后,数组左闭右开区间 [firstIdx,lastIdx)内的所有元素的值均为需要填充的数。

Collections

Collectionsjava.util中对集合操作的一个工具类。方法均为静态方法,可使用类名直接调用。

Collections.sort()

Collections.sort()底层原理为将其中所有元素转化为数组调用 Arrays.sort(),完成排序后再赋值给原本的集合。又因为 Java 中 Collection的元素类型均为对象类型,所以始终是归并排序去处理。

该方法无法对集合指定区间排序。

底层源码:

default void sort(Comparator c) {        Object[] a = this.toArray();        Arrays.sort(a, (Comparator) c);        ListIterator i = this.listIterator();        for (Object e : a) {            i.next();            i.set((E) e);        }    }

Collections.binarySearch()

Collections.binarySearch()是对集合中指定区间进行二分搜索,功能与 Arrays.binarySearch()相同。

Collections.binarySearch(list, key); 

该方法无法对指定区间进行搜索。

Collections.swap()

Collections.swap()的功能是交换集合中指定二个位置的元素。

Collections.swap(list, i, j);
关键词:

Copyright   2015-2022 南极动漫网版权所有   备案号:粤ICP备2022077823号-13   联系邮箱: 317 493 128@qq.com