排序查找与Lambda、正则表达式
● 导图
一、 基础算法
1.排序
1.1 冒泡排序
冒泡排序: 相邻的数据两两比较,小的放前面,大的放后面。
核心思想:
- 相邻的元素两两比较,大的放右边,小的放左边。
- 第一轮比较完毕之后,最大值就已经确定,第二轮可以少循环一次,后面以此类推。
- 如果数组中有n个数据,总共我们只需要执行n - 1 轮的代码就可以。
冒泡排序的应用
现在有一个数组:{2,4,5,3,1};想从小到大排序,这里就可以使用冒泡排序。
代码实现
public static void main(String[] args) {
//1.定义数组
int[] arr = {2,4,5,3,1};
//2.利用冒泡排序将数组中的数据变成 1 2 3 4 5
//外循环:表示要执行多少轮,如果有n个数据,那么就执行 n - 1 轮。
for (int i = 0; i < arr.length - 1; i++) {
//内循环:每一轮循环中比较数据并找到当前的最大值
//-1:为了防止索引越界
//-i:提高效率,每一轮执行的次数应该比上一轮少一次。
for (int j = 0; j < arr.length - 1 - i; j++) {
//i 依次表示数组中的每一个索引:0 1 2 3 4
if (arr[j] > arr[j + 1]){
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
1.2 选择排序
选择排序:从0索引开始,拿着每一个索引上的元素跟后面的元素依次比较,小的放前面,大的放后面,以此类推。
选择排序的实现:
- 从0索引开始,跟后面的元素一一比较。
- 小的放前面,大的放后面。
- 第一轮循环结束后,最小的数据已经确定。
- 第二轮循环从1索引开始以此类推。
- 第三轮循环从2索引开始以此类推。
- 第四轮循环从3所以开始以此类推。
选择排序的应用
还是以冒泡排序的题为例子:有一个数组:{2,4,5,3,1};想从小到大排序。
代码实现
public static void main(String[] args) {
//1.定义数组
int[] arr = {2,4,5,3,1};
//外循环:表示循环几轮
//i:表示这一轮中,拿着i索引上的数据跟后面的元素进行比较并交换
for (int i = 0; i < arr.length - 1; i++) {
//内循环:每一轮拿着i跟i后面的数据进行比较交换
for (int j = i + 1; j < arr.length; j++) {
if(arr[i] > arr[j]){
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
}
}
2. 查找
2.1 基础查找
从索引值0开始挨个往后查找,若查到想要的元素值,则停止。
public static void main(String[] args) {
int[] arr = {131,127,147,81,103,23,7,79};
find(arr,127);
}
public static int find(int[] arr,int num){
for (int i = 0; i < arr.length; i++) {
if(num == arr[i]){
System.out.println("索引值为:"+i);
return 1;
}
}
System.out.println("没有查找到该元素");
return 0;
}
假设我们要查找的元素是79,如果是基本查找的话,只能从0索引开始一个一个往后找,但是如果元素比较多,需要查找的元素比较靠后的话,这样查找的此处就比较多。性能比较差
2.2 二分查找
二分查找的主要特点是,每次查找能排除一半元素,这样效率明显提高。但是二分查找要求比较苛刻,它要求元素必须是有序的,否则不能进行二分查找。
现在有如下一个需求:
定义一个方法利用二分查找,查询某个元素在数组中的索引,数组如下: {7,23,79,81,103,127,131,147}
代码实现
public static void main(String[] args) {
int[] arr = {7,23,79,81,103,127,131,147};
System.out.println("查找元素的索引为:"+Index(arr,131));;
}
public static int Index(int[] arr,int num){
//1.定义两个变量记录查找范围
int max = arr.length-1;
int min = 0;
//2.利用循环不断地去找要查找的数据
while (true) {
if(min > max){
return - 1;
}
//3.找到min和max的中间值
int mid = (max + min) / 2;
//4.拿着mid指向的元素跟要查找的元素进行比较
if(arr[mid] > num){
//4.1 num在mid的左边
//min不变,max = mid -1;
max = mid - 1;
}else if(arr[mid] < num){
//4.2 num在mid的右边
//max不变,min = mid + 1;
min = mid + 1;
}else{
return mid;
}
}
}
二分查找小结:
1.二分查找的优势。
提高查找效率
2.二分查找的前提条件。
数据必须是有序的,如果数据是乱的,先排序再用二分查找得到的索引就没有实际意义,只能确定当前数字在数组中是否存在,因为排序后数字的位置可能会发生改变。
3.二分查找的过程
- min和max表示当前要查找的范围
- mid是在min和max中间的
- 如果要查找的元素在mid的左边,缩小范围时,min不变,max等于mid减1
- 如果要查找的元素在mid的右边,缩小范围时,max不变,min等于mid加1
- 如果要查找的元素恰好等于mid,那么就返回。
二、Lambda表达式
1)初识Lambda
以下有段代码,使用Arrays中的sort方法来对数组进行排序。
Integer[] arr = {2,3,4,1,5,7,8,9,6};
//使用匿名内部类来实现排序
Arrays.sort(arr,new Comparator<Integer>(){
@Override
public int compare(Integer o1,Integer o2){
return o1 - o2;
}
});
//使用Lambda表达式来优化这段代码
Arrays.sort(arr, (Integer o1 - Integer o2)->{
return o1 - o2;
}
);
2)函数式编程
面向对象:先找对象,让对象做事。
函数式编程:是一种思想特点。
函数式编程思想,是忽略面向对象的复杂语法, 强调做什么,而不是谁去做。 而Lambda表达式就是函数式思想的体现。
3).Lambda表达式的标准格式
() -> {
}
- () 对应着方法的形参
- -> 固定格式
4)Lambda的注意事项
- Lambda表达式可以用来简化匿名内部类的书写
- Lambda表达式只能简化函数式接口的匿名内部类的写法
- 函数式接口: 有且仅有一个抽象方法的接口叫做函数式接口,接口上方可以加@Functionallnterface注解
5)Lambda表达式的省略写法
- 参数类型可以省略不写
- 如果只有一个参数,参数类型可以省略,同时()也可以省略
- 如果Lambda表达式的方法只有一行,大括号,分号,return可以省略不写,注意:大括号,分号,return需要同时省略。
6)Lambda表达式简化Comparator接口的匿名形式
看以下的一个需求:
定义数组并存储一些字符串,利用Arrays中的sort方法进行排序。
要求:
按照字符串的长度进行排序,短的在前面,长的再后面。(暂时不比较字符串里的内容)
代码实现
String[] arr = {"a","aaaa","aaa","aa"}
//如果要把数组中的数据按照指定的方式进行排序,就需要用到sort方法,而且要指定排序的规则
//匿名内部类解决
//Arrays.sort(arr,new Comparator<String>){
// @Override
// public int comapre(String o1,String o2){
// //字符串的长度进行排序
// return o1.length() - o2.length();
// }
//});
//打印
System.out.println(Arrays.toString(arr));
//Lambda完整格式
Arrays.sort(arr,(String o1,String o2)->{
return o1.length() - o2.length();
}
);
//Lambda简写格式
//小括号:数据类型可以省略,如果参数只有一个,小括号可以省略
//大括号:如果方法体只有一行,return 分号,大括号都可以省略
Arrays.sort(arr,(o1,o2) -> o1.length()-o2.length());
Lambda小结
1.Lambda的基本作用?
简化函数式接口的匿名内部类的写法。
2.Lambda表达式有什么使用前提?
必须是接口的匿名内部类,接口中只能有一个抽象方法。
3.Lambda的好处?
Lambda是一个匿名函数,我们可以把Lambda表达式理解为是一段可以传递的代码,它可以写出更简洁、更灵活的代码,作为一种更紧凑的代码风格,使java语言表达能力得到了提升。
三、正则表达式
正则表达式的规则
字符类(只匹配一个字符)
预定义字符(只匹配一个字符)
数量词
● 正则表达式的使用
字符类(只匹配一个字符)的使用
public static void main(String[] args) {
//public boolean matches(String regex);判断是否与正则表达式匹配,匹配则返回true
//只能是a b c
System.out.println("-------1-------");
System.out.println("a".matches("[abc]")); //true //只能是abc中的一个
System.out.println("z".matches("[abc]")); //false //z不属于abc
System.out.println("ab".matches("[abc]"));//false //[]一个中括号只能判断一个字符
System.out.println("ab".matches("[abc][abc]")); //true //两个[] 就可以判断两个字符
//不能出现a b c(除abc外的其他值)
System.out.println("-------2-------");
System.out.println("a".matches("[^abc]")); //fase //a属于abc
System.out.println("z".matches("[^abc]")); //true //z不属于abc
System.out.println("zz".matches("[^abc]")); //false
System.out.println("zz".matches("[^abc][^abc]")); //true
//a-z A-z(包括头尾的范围)
System.out.println("-------3-------");
System.out.println("a".matches("[a-zA-z]")); //true //a属于a-z
System.out.println("z".matches("[a-zA-z]")); //true //z属于a-z
System.out.println("aa".matches("[a-zA-z]")); //false //一个[]只能判断一个字符
System.out.println("zz".matches("[a-zA-z]")); //false
System.out.println("zz".matches("[a-zA-z][a-zA-Z]")); //true
System.out.println("0".matches("[a-zA-z]")); //false
System.out.println("0".matches("[a-zA-Z0-9]")); //true
System.out.println("0".matches("[0-9]")); //true
//[a-d[m-p]] a到d,或m到p
System.out.println("-------4-------");
System.out.println("a".matches("[a-d[m-p]]")); //true
System.out.println("d".matches("[a-d[m-p]]")); //true
System.out.println("m".matches("[a-d[m-p]]")); //true
System.out.println("p".matches("[a-d[m-p]]")); //true
System.out.println("e".matches("[a-d[m-p]]")); //false
System.out.println("0".matches("[a-d[m-p]]")); //false
//[a-z &&[def]] a-z和def的交集,为:d,e,f
System.out.println("-------5-------");
System.out.println("a".matches("[a-z &&[def]]")); //false
System.out.println("d".matches("[a-z &&[def]]")); //true
System.out.println("0".matches("[a-z &&[def]]")); //false
//注意:如果要求两个范围的交集,那么需要写符号 &&
//如果写成了一个&,那么此时&表示就不是交集了,而是一个简简单单的&符号
System.out.println("a".matches("[a-z &&[def]]")); //true
//[a-z &&[^bc]] a-z和非bc的交集(等同于[ad-z])
System.out.println("-------6-------");
System.out.println("a".matches("[a-z &&[^bc]]")); //true
System.out.println("b".matches("[a-z &&[^bc]]")); //flase
System.out.println("0".matches("[a-z &&[^bc]]")); //false
//[a-z &&[^m-p]] a-z 和 非m-p的交集(等同于[a-lq-z])
System.out.println("-------7-------");
System.out.println("a".matches("[a-z &&[^m-p]]")); //true
System.out.println("m".matches("[a-z &&[^m-p]]")); //false
System.out.println("0".matches("[a-z &&[^m-p]]")); //false
}
预定义字符(只匹配一个字符)
使用预定义字符正则表达式的时候,先要注意几个点:
- 在Java中,
\
代表转义字符,意义为:改变后面那个字符原本的含义。 "
在Java中表示字符串的开头或者结尾。- 而
"
与\
连用成:\"
,此时\
表示转义字符,改变了后面那个双引号原本的含义,把它变成了一个普普通通的双引号。 - 而在Java中想要表示
\
,则需要在\
前再加上一个反斜杠,:\\
,改变后面\
原本的含义,把它变成一个普普通通的\
。
预定义字符的使用
public static void main(String[] args) {
// .表示任意一个字符
System.out.println("你".matches("..")); //false
System.out.println("你a".matches(".."));//true
// \\d只能是任意的一位数字
//简单来记:\\ 表示 \
System.out.println("a".matches("\\d")); //false
System.out.println("3".matches("\\d")); //true
System.out.println("333".matches("\\d")); //false
// \\w只能是一位单词字符 [a-zA-Z_0-9]
System.out.println("z".matches("\\w")); //true
System.out.println("2".matches("\\w")); //true
System.out.println("21".matches("\\w\\w")); //true
System.out.println("你".matches("\\w")); //false
// \\W非单词字符
System.out.println("你".matches("\\W")); //true
}
数量词
public static void main(String[] args) {
//数量词: 必须都是数字、字母、下划线,至少6位
System.out.println("2442fsfsf".matches("\\w{6,}")); //true
System.out.println("244f".matches("\\w{6,}")); //false
//必须是数字和字符 必须是4位
System.out.println("23dF".matches("[a-zA-Z0-9]{4}")); //true
System.out.println("23_F".matches("[a-zA-Z0-9]{4}")); //false
System.out.println("23dF".matches("[\\w &&[^_]]{4}")); //true
System.out.println("23_F".matches("[\\w &&[^_]]{4}")); //false
}