Java学习_14_常用算法和lambda表达式


前言

博客仅记录个人学习进度和一些查缺补漏。
学习内容:BV17F411T7Ao


一、查找算法

基本查找/顺序查找

核心思路:遍历所有索引,依次对比直到查到找该元素或者遍历结束
时间复杂度: o(n)
空间复杂度: o(1)
代码如下:

package my.algorithm.search;

import java.util.ArrayList;

public final class BasicSearchDemo {

    // 整型数组中查找数字,返回所有索引。
    public static ArrayList<Integer> basicSearch(int[] arr, int num){

        ArrayList<Integer> res = new ArrayList<>();
        for (int i = 0; i < arr.length; i++) {
            if(arr[i] == num) {
                res.add(i);
            }
        }

        return res;
    }

}

二分查找/折半查找

特点:数据有序
核心思路:在有序数组中,每次排除一半的查找范围,在剩下的范围重复该算法逻辑。
时间复杂度:o(log(n))
空间复杂度:o(1)、o(log(n))
在这里插入图片描述

代码如下:

   // 整型增序二分查找,输入为增序序列,返回第一个查到的索引。
    public static ArrayList<Integer> binarySearch_Increasing(int[] arr, int num) {
        ArrayList<Integer> res = new ArrayList<>();
        int start = 0;
        int end = arr.length - 1;
        int mid = (start + end) / 2;

        while (start < end) {
            if(arr[mid] == num) {
                res.add(mid);
                break;
            }else if (arr[mid] > num) {
                end = mid - 1;
                mid = (start + end) / 2;
            }else if (arr[mid] < num) {
                start = mid + 1;
                mid = (start + end) / 2;
            }
        }

        return res;
    }
    
    // 递归增查找
    public static int binarySearch_Increasing_Recursion(int[] arr, int start, int end, int  num) {
        if(start > end) {
            return -1;
        }
        int mid = (start + end) / 2;
        if (arr[mid] == num) {
            return mid;
        }else if (arr[mid] > num) {
            return binarySearch_Increasing_Recursion(arr, start, mid - 1, num);
        }else if (arr[mid] < num) {
            return binarySearch_Increasing_Recursion(arr, mid + 1, end, num);
        }
        return -1;
    }

分块查找

特点:数据分块有序,块内无序,块间有序。
分块原则(以递增为例):
1、前一块中最大的数据,小于后一块中所有的数据
2、块数量一般等于数字的个数开根号
核心思路:先确定要查找的元素在哪一块,再挨个顺序查找
代码如下:

package my.algorithm.search;


public class BlockSearchDemo {


    public static int blockSearch_Increasing(Block[] brr, int[] arr, int num) {

        int block_num = findBlock_Increasing(brr, num);
        if(block_num == -1) {
            return -1;
        }else {
            return getIndex(brr, arr, num, block_num);
        }

    }

    public static int getIndex(Block[] brr, int[] arr, int num,  int block_num) {

        //顺序查找
        for (int i = arr[brr[block_num].getStartIndex()]; i < arr[brr[block_num].getEndIndex()]; i++) {
            if(arr[i] == num) {
                return i;
            }
        }
        return -1;
    }

    //顺序查找,其实也可以二分查找,此处不赘述
    public static int findBlock_Increasing(Block[] brr, int num) {
        for (int i = 0; i <brr.length; i++) {
            if(num <= brr[i].getMax()) {
                return i;
            }
        }
        return -1;
    }
}

package my.algorithm.search;

public class Block{
    private int max;
    private int startIndex;
    private int endIndex;

    public Block(int max, int startIndex, int endIndex) {
        this.max = max;
        this.startIndex = startIndex;
        this.endIndex = endIndex;
    }

    public int getMax() {
        return max;
    }

    public void setMax(int max) {
        this.max = max;
    }

    public int getStartIndex() {
        return startIndex;
    }

    public void setStartIndex(int startIndex) {
        this.startIndex = startIndex;
    }

    public int getEndIndex() {
        return endIndex;
    }

    public void setEndIndex(int endIndex) {
        this.endIndex = endIndex;
    }
}

在这里插入图片描述
解决方式:
1.哈希查找,在某一块上进行堆积,类似数据结构中的十字链表
在这里插入图片描述
2.排序树查找
一边增加树高一边进行查找(找不到就插进对应的位置),其实就是二叉排序树。
3.堆查找
大根堆小根堆的使用。

二、排序算法

冒泡排序

将相邻的数据两两比较,判断顺序后将大的(或小的)放到后面
核心思想:根据大小交换最近的两个数据,重复至所有的数据都进行一次对比。
时间复杂度:o(n^2)
空间复杂度:o(1)
代码如下:

    // 整型冒泡增序
    public static void bubbleSort_Increasing (int[] arr) {
        for (int i = 0; i < arr.length; i++) {
            for (int j = 0; j < arr.length - i - 1; j++) {
                int temp = arr[j];
                if (arr[j] > arr[j + 1]) {
                    arr[j] = arr[j + 1];
                    arr[j + 1] = temp;
                }
            }
        }
    }

选择排序

在这里插入图片描述
核心思想:第i轮都确定第i个元素的索引正确。
时间复杂度:o(n^2)
空间复杂度:o(1)
代码如下:

    // 选排增序
    public static void selectSort_Increasing(int[] arr) {
        for (int i = 0; i < arr.length - 1; i++) {
            for (int j = i; j < arr.length; j++) {
                if (arr[i] > arr[j]){
                    swap(arr, i, j);
                }
            }
        }
    }

    public static void swap(int[] arr, int i, int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }

插入排序

在这里插入图片描述
将38向前面有序的数组进行位置确定,从后往前去遍历,大于则交换,最终找到自己的位置。
时间复杂度:o(n^2)
空间按复杂的:o(1)
细节:升序的时候从大向小比较,降序的时候从小到大比较,否则就要遍历该轮所有数据。
代码如下:

package my.algorithm.sort;

public final class InsertSortDemo {

    public static void insertSort_Increasing(int[] arr) {

        for (int i = 1; i < arr.length; i++) {
            int j = i - 1;
            while(j >= 0 && arr[j] > arr[j + 1]){
                swap(arr, j, j + 1);
                j--;
            }
        }
    }

    public static void swap(int[] arr, int i, int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
}

快速排序

经典的考研算法之一,看到大题是数组数组基本先手撸一遍快排稳定2分。
核心思想:选定一个数据为轴,先确定轴心位置,将数组分为左右两部分,再分别找轴心,直到所有数据有序。
时间复杂度:o(n*log(n))
其中一共进行logn轮,每轮遍历n个索引。
空间复杂度:o(log(n))
递归栈为logn层。

代码如下:

package my.algorithm.sort;

public final class QuickSortDemo {
    public static void quickSort_Increasing(int[] arr, int start, int end){

        if(start < end) {
            // 确定这一轮的基轴
            int pivotIndex = partition(arr, start, end);
            // 找到左边的基轴
            quickSort_Increasing(arr, start, pivotIndex - 1);
            // 找到右边的基轴
            quickSort_Increasing(arr, pivotIndex + 1, end);
        }

    }

    public static int partition(int[] arr, int start, int end) {

        // 先保存作为基轴的值
        int pivot = arr[start];
        while(start < end) {
            // 确保右边的所有数字都比基轴要大
            while (pivot < arr[end] && start < end) {
                end --;
            }
            // 遇到第一个比基轴小的数,丢到左边去
            arr[start] = arr[end];
            // 确保左边所有的数字都比基轴要小
            while (pivot > arr[start] && start < end) {
                start ++;
            }
            // 遇到第一个比基轴大的数,丢到右边去
            arr[end] = arr[start];
        }
        // 至此,所有左边的数字都比基轴小,所有右边的数字都比基轴大,则基轴位置一定在start = end 的此处。
        arr[start] = pivot;
        return start;

    }

}

三、Arrays

在这里插入图片描述
在算法题中经常会遇到需要将数组拼接成字符串进行粘连或输出,或是需要快速排序、查找,临时写一个快排二分都不如直接调用Arrays工具类。节省大量时间可以思考更多的代码逻辑。
toString实现
在二分查找的时候,是一边查找一边查看可以插入的位置,所以会有插入点的概念。
在这里插入图片描述
因为java中数组本身就是引用参数,类似指针,一旦更改数组中内容就很难还原,拷贝数组的需求日益见长。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
底层原理:
利用插入排序+二分查找的方式进行排序。
默认把0索引的数据当成是有序序列,以后的数据都是无序序列。
遍历无序序列得到里面的每一个元素,假设当前遍历得到的元素是A元素。
把A元素王有序序列中插入,在插入的时候,利用二分查找确定A元素的插入点,确定插入点的比较方法就是compare函数体中所定义的方法。
其中两个整型o1 o2分别就是A和对应的元素进行相应的规则比较。
如果返回值是负数,就把A拿到左边去继续二分,如果返回值是正数,就把A拿到右边的数组去继续二分
直到最终确定A的位置。

compare详解:
o1,来自无序元素中的每一个元素,也就是A。
o2,来自有序元素的每一个元素,也就是有序序列。
返回值为正数:表示当前插入的位置对于正确位置来说靠左,继续往右边找。
返回值为负数:表示当前插入的位置对于正确位置来说靠右,继续往左边找。
例如:
return o1 - o2;
即A - 插入点,正数就说明大数往右插,升序。
return o2 - o1;
相反说明大数往左插,降序。

四、Lambda

在上一节,我们使用匿名内部类来填充compare类型的数据。
使用lamdba表达式简化如下:
在这里插入图片描述
在这里插入图片描述
函数式编程可以忽略面向对象的复杂语法,强调要做什么而不是谁去做

在这里插入图片描述
在这里插入图片描述
仅能替代来自接口的方法中的匿名内部类

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

总结

排序查找的基本算法都是考研期间就猛猛练过了,特别是快排那真是信手拈来。
拉姆达表达式确实不是很熟悉,还需要慢慢学着去用,确实能够很节省代码时间和长度。
arrays的各种方法对算法题的提升很大,以前还要慢慢手写,现在就得学起来加快解题速度。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值