Java数据结构 -- 常见算法分析(查找算法、排序算法)精解详解!!!

简介: Java数据结构 -- 常见算法分析(查找算法、排序算法)精解详解!!!

前言

本文主要讲了常见的查找算法和排序算法,内容通俗易懂,相信各位小伙伴定会有一定的收获。

一、查找算法

1.1基本查找

基本查找也叫顺序查找核心思想是从0下标开始挨个往后查找

public class Test {
    public static void main(String[] args) {
        //算法:基本查找
        //需求:定义一个方法利用基本查找判断一个元素是否存在
        int[] arr = {12,89,24,26,100,215,48,94};
        System.out.println(basicSearch(arr, 12));
    }
    /**
     * 基本查找
     * @param array 数组
     * @param number 要查找的元素
     * @return 元素是否存在
     */
    public static boolean basicSearch(int[] array,int number) {
        for (int i = 0; i < array.length; i++) {
            if(array[i] == number) {
                return true;
            }
        }
        return false;
    }
}

练习1

定义一个方法,利用基本查找,查询某个元素在数组中的索引

要求:不需要考虑数组中元素是否重复

public static int basicSearchIndex(int[] array,int number) {
        for (int i = 0; i < array.length; i++) {
            if(array[i] == number) {
                return i;
            }
        }
        return -1;
    }

练习2

定义一个方法,利用基本查找,查询某个元素在数组中的索引

要求:需要考虑数组中元素是否重复

public static ArrayList<Integer> basicSearchIndex2(int[] array,int number) {
        ArrayList<Integer> list = new ArrayList<>();
        for (int i = 0; i < array.length; i++) {
                if(array[i] == number) {
                    list.add(i);
                }
        }
        return list;
    }

1.2二分查找/折半查找

前提:数组arr中的数据必须是有序的

核心:每次排除一半的查找范围

定义三个变量:min,max,mid

min:表示每次查找的0下标

max:表示每次查找的最大下标

mid:表示每次查找的数组的范围的中间值下标,即mid = (min + max)/2

要查找的数:num

在进行查找时,每次查找将要查找的数与mid下标的元素进行比较

1.若arr[mid] > num,则max = mid - 1,mid = (min + max)/2,继续查找

2.若arr[mid] < num,则min = mid + 1,mid = (min + max)/2,继续查找

直到arr[mid] == num 时查找结束。

查找不到的情况:

若要查找的数在数组中不存在,这时代码的逻辑是min>max(或max

public static int binarySearch(int[] array,int number) {
        //1.定义变量记录范围
        int min = 0;
        int max = array.length - 1;
        //2.利用循环查找数据
        while (true) {
            if(min>max) {
                return -1;
            }
            //3.找到min和max的中间位置mid
            int mid = (min + max) >>> 1;
            //4.比较
            //4.1 number在mid左边
            if(array[mid] > number) {
                max = mid - 1;
                mid = (min + max) >>> 1;
            }
            //4.2 number在mid右边
            else if(array[mid] < number){
                min = mid + 1;
                mid = (min + max) >>> 1;
            }else{
                //4.3 number与mid下标的值一样
                return mid;
            }
        }
    }

1.3插值查找

二分查找是将每次查找的中间值与要查的元素比较确定是否找到和下一次的查找范围,插值查找是二分查找的一种改进方式,插值查找是尽可能将中间值接近要查找的元素。

通过套用公式mid = min + (num - arr[min]) / (arr[max] - arr[min] ) * (max - min)可以使每次得到的中间值更接近要查找的元素。

1.num - arr[min]指的是数组中要查找的值与当前范围最小索引值的距离

2.arr[max] - arr[min]指的是数组中当前范围最大索引值与最小索引值的距离

3.(num - arr[min]) / (arr[max] - arr[min] ) 指的是要查找的元素在数组中位置的占比,乘以数组的长度 (max - min)再加上最小偏移量min得到查找的元素在数组中的位置。

优点:计算中间值mid时效率更高。

缺陷:要保证数组中的数据分布要比较均匀,否则效率会降低。

/**
     * 插值查找
     * @param array
     * @param number
     * @return
     */
    public static int binarySearch2(int[] array,int number) {
        //1.定义变量记录范围
        int min = 0;
        int max = array.length - 1;
        //2.利用循环查找数据
        while (true) {
            if(min>max) {
                return -1;
            }
            //3.找到min和max的中间位置mid
            int mid = (min + max) >>> 1;
            //4.比较
            //4.1 number在mid左边
            if(array[mid] > number) {
                max = mid - 1;
                mid = min + (number - array[min]) / (array[max] - array[min]) * (max - min);
            }
            //4.2 number在mid右边
            else if(array[mid] < number){
                min = mid + 1;
                mid = min + (number - array[min]) / (array[max] - array[min]) * (max - min);
            }else{
                //4.3 number与mid下标的值一样
                return mid;
            }
        }
    }

1.4分块查找

将一组数据分成好几个小块,每个块内的数据都是无序的,但块与块间是有一定顺序的。

分块的原则:

前一个块中的最大数都小于紧相挨着它的下一个块中的所有数(块内无序,块间有序)

块数数量一般等于数字的个数开根号。

在查找时,先确定要找的元素位于哪一块,然后在块内挨个查找。

代码分析:

在书写代码时,要先创建一个类,表示块,需要多少个块则分别创建多少个块的对象,然后将这些块对象放到一个数组中统一管理,这个数组也叫索引表

若要查找某个数,通过索引表中的每个最大值与该数进行比较得出该数在哪一块内。

package com.practice;
/**
 * @Author YJ
 * @Date 2023/7/15 12:06
 * Description: 分块查找
 */
public class BlockSearch {
    public static void main(String[] args) {
        /**
         * 分块查找
         * 块内无序,块间有序
         *
         */
        int[] arr = {16, 5, 9, 12, 21, 18,
                32, 23, 37, 26, 45, 34,
                50, 48, 61, 52, 73, 66};
        //创建3个Block对象
        Block b1 = new Block(21, 0, 5);
        Block b2 = new Block(45, 6, 11);
        Block b3 = new Block(73, 12, 17);
        //定义数组管理三个块的对象(索引表)
        Block[] blocks = {b1, b2, b3};
        //定义一个变量,记录查找的元素
        int num = 50;
        //调用方法  打印
        System.out.println(getIndex(blocks, arr, num));
    }
    public static int getIndex(Block[] blocks, int[] array, int number) {
        //1.确定number在哪一块中
        int indexBlock = findIndexBlock(blocks, number);
        if(indexBlock == -1) {
            return -1;
        }
        //数据在数组中
        //2.获取这一块的起始索引和结束索引
        int startIndex = blocks[indexBlock].getStartIndex();
        int endIndex = blocks[indexBlock].getEndIndex();
        //3.遍历
        for (int i = startIndex; i <= endIndex; i++) {
            if(array[i] == number) {
                return i;
            }
        }
        return -1;
    }
    //定义一个方法,确定number在哪一块中
    public static int findIndexBlock(Block[] blocks, int number) {
        //Block b1 = new Block(21, 0, 5);    -------- 0
        //Block b2 = new Block(45, 6, 11);   -------- 1
        //Block b3 = new Block(73, 12, 17);  -------- 2
        //通过基本查找确定number在哪一块中
        //从0索引遍历blocks数组,如果number小于max,说明number在这一块中
        for (int i = 0; i < blocks.length; i++) {
            if (number <= blocks[i].getMax()) {
                return i;
            }
        }
        return -1;
    }
}
//定义块的类
class Block {
    private int max;//块内最大值
    private int startIndex;//起始索引
    private int endIndex;//结束索引
    public Block() {
    }
    public Block(int max, int startIndex, int endIndex) {
        this.max = max;
        this.startIndex = startIndex;
        this.endIndex = endIndex;
    }
    /**
     * 获取
     *
     * @return max
     */
    public int getMax() {
        return max;
    }
    /**
     * 设置
     *
     * @param max
     */
    public void setMax(int max) {
        this.max = max;
    }
    /**
     * 获取
     *
     * @return startIndex
     */
    public int getStartIndex() {
        return startIndex;
    }
    /**
     * 设置
     *
     * @param startIndex
     */
    public void setStartIndex(int startIndex) {
        this.startIndex = startIndex;
    }
    /**
     * 获取
     *
     * @return endIndex
     */
    public int getEndIndex() {
        return endIndex;
    }
    /**
     * 设置
     *
     * @param endIndex
     */
    public void setEndIndex(int endIndex) {
        this.endIndex = endIndex;
    }
    public String toString() {
        return "Block{max = " + max + ", startIndex = " + startIndex + ", endIndex = " + endIndex + "}";
    }
}

二、排序算法

2.1冒泡排序

规则:相邻的数据两两比较,小的放前面,大的放后面

在进行排序的时候,按照相邻数据比较,小的放左边,大的放右边,当第一轮的数据比较结束后,最大值找到,且最大值在数组的最右边,除了最大值外,前面的其他数据还是无序的,所以还需要接着进行新一轮的排序,在剩余的数据中再次找到最大值,第二轮结束后,找到了次大值,继续再剩余的数据中进行新一轮的比较,再次找到最大值。

进行循环比较时,一轮比较完毕后,后一轮就可以少循环一次,执行的轮数为数组中的数据个数n - 1

package com.practice;
/**
 * @Author YJ
 * @Date 2023/7/15 17:12
 * Description:冒泡排序
 */
public class BubbleSortDemo {
    public static void main(String[] args) {
        //1.定义数组
        int[] arr = {2, 4, 5, 3, 1};
        //2.冒泡排序
        //第一轮:结束后最大值在数组的最右边
        //多轮比较:
        for (int i = 0; i < arr.length - 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 tmp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = tmp;
                }
            }
        }
        //3.遍历数组
        printArr(arr);
    }
    /**
     * 遍历数组
     * @param arr
     */
    public static void printArr(int[] arr) {
        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i] + " ");
        }
        System.out.println();
    }
}

2.2选择排序

选择排序:从0索引开始,拿着每一个索引上的元素跟后面的元素一次比较,小的放前面,大的放后面。

跟冒泡排序类似,选择排序从0索引开始,跟后面的元素一一比较,小的放前面,大的放后面,每一轮循环结束后,最小值就可以确定,接着以此从剩下的元素进行循环比较。

package com.practice;
/**
 * @Author YJ
 * @Date 2023/7/15 18:15
 * Description:
 */
public class SelectionDemo {
    public static void main(String[] args) {
        //1.定义数组
        int[] arr = {2, 4, 5, 3, 1};
        //开始比较
        //i:表示这一轮中,哪个索引上的数据跟后面的数据进行比较并交换
        for (int i = 0; i < arr.length - 1; i++) {
            for (int j = i + 1; j < arr.length; j++) {
                if(arr[i] > arr[j]) {
                    int tmp = arr[i];
                    arr[i] = arr[j];
                    arr[j] = tmp;
                }
            }
        }
        printArr(arr);
    }
    /**
     * 遍历数组
     * @param arr
     */
    public static void printArr(int[] arr) {
        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i] + " ");
        }
        System.out.println();
    }
}

2.3插入排序

将0索引的元素到N索引的元素看作是有序的,把N+1索引的元素到最后一个当成是无序的。遍历无序的数据,将遍历到的元素插入到有序序列中适当的位置,如遇到相同数据,插在后面。

N的范围:0~最大索引

代码分析:在进行插入排序时,首先遍历无序的一组数据,拿到无序数据中的第一个,将这个元素与有序数据中倒着依次比较,并进行插入。

/**
 * @Author YJ
 * @Date 2023/7/16 6:54
 * Description:
 */
public class InsertSortDemo {
    public static void main(String[] args) {
        //数组
        int[] arr = {3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48};
        //1.找到无序的那一组数据是从哪个索引开始的
        int startIndex = -1;
        for (int i = 0; i < arr.length; i++) {
            if (arr[i] > arr[i + 1]) {
                startIndex = i + 1;//表示有序的数据到i下标就结束了
                break;
            }
        }
        //2.遍历从startIndex开始到最后一个元素,依次得到无序的那一组数据中的每一个数据
        for (int i = startIndex; i < arr.length; i++) {
            //记录当前要插入数据的索引
            int cur = i;
            while (cur > 0 && arr[cur] < arr[cur - 1]) {
                //交换位置
                int tmp = arr[cur];
                arr[cur] = arr[cur - 1];
                arr[cur - 1] = tmp;
                cur--;
            }
        }
        printArr(arr);
    }
    private static void printArr(int[] array) {
        for (int i = 0; i < array.length; i++) {
            System.out.print(array[i] + " ");
        }
        System.out.println();
    }
}

2.4快速排序

在进行快速排序前,我们还要用到递归算法

递归指的是方法中调用方法本身的现象。

注意:递归一定要有出口,否则就会出现内存溢出。

作用:把一个复杂的问题层层转换为一个与原问题相似的规模较小的问题来求解。

递归只需少量的程序就可描述出解题过程中所需要的多次重复计算。

核心

1.找出口:什么时候不再调用方法。

2.找规则:如何把大问题变成规模较小的问题。

2.4.1递归求和

求1-100之间的和

/**
 * @Author YJ
 * @Date 2023/7/16 7:22
 * Description:
 */
public class RecursionDemo {
    public static void main(String[] args) {
        //把大问题拆成小问题
        //1-100之间的和 = 100 + (1-99之间的和)
        //1-99之间的和 = 99 + (1-98之间的和)
        //...
        //1-2之间的和 + 2 + (1-1之间的和)
        //1-1之间的和 = 1(递归的出口)
        System.out.println(getSum(100));
    }
    public static int getSum(int number) {
        if(number == 1) {
            return 1;
        }
        return number + getSum(number - 1);
    }
}

2.4.2快速排序

第一轮:把0索引的数字作为基准数,确定基准数在数组中正确的位置。

结束后比基准数小的全部在左边,比基准数大的全部在右边。



要想实现上面的效果,要将6作为基准数,定义start和end作为数组的起始和结束位置,从start开始与基准数6依次比较,找出比6大的数,从end开始往后与基准数6比较,找出比6小的数,分别找到后将这两个数交换位置,然后继续找,当start和end指向同一数时,此时的位置就是基准数应存入的位置,这个也叫基准数归位。

/**
 * @Author YJ
 * @Date 2023/7/16 7:42
 * Description:快速排序
 */
public class QuickSort {
    public static void main(String[] args) {
        //数组
        int[] arr = {6, 1, 2, 7, 9, 3, 4, 5, 10, 8};
        quickSort(arr, 0, arr.length - 1);
        //打印结果
        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i] + " ");
        }
        System.out.println();
    }
    /**
     * 快速排序
     *
     * @param arr 要排序的数组
     * @param i   起始索引
     * @param j   结束索引
     */
    public static void quickSort(int[] arr, int i, int j) {
        //1.定义变量记录要查找的范围
        int start = i;
        int end = j;
        if(start > end) {
            return;
        }
        //2.定义变量记录基准数
        int baseNumber = arr[i];
        //3.利用循环找到要交换的数据
        while (start != end) {
            //利用end,从后往前找比基准数小的数
            while (true) {
                if(end <= start || arr[end] < baseNumber) {
                    break;
                }
                end--;
            }
            //利用start,从前往后找比基准数大的数
            while (true) {
                if(start >= end || arr[start] > baseNumber) {
                    break;
                }
                start++;
            }
            //交换
            int tmp = arr[start];
            arr[start] = arr[end];
            arr[end] = tmp;
        }
        //当start和end指向了同一个元素时,循环结束
        //表示已经找到了基准数在数组中应存入的位置
        //基准数归位
        int tmp = arr[i];
        arr[i] = arr[start];
        arr[start] = tmp;
        //确定6左边的数,重复排序
        quickSort(arr,i,start - 1);
        //确定6右边的数,重复排序
        quickSort(arr,start + 1,j);
    }
}

总结

经过这一番讲解,我们学到了最基本的查找和排序算法,欢迎各位的持续关注!!!


相关文章
|
17天前
|
数据采集 机器学习/深度学习 算法
别急着上算法,咱先把数据整明白:大数据分析的5个基本步骤,你都搞对了吗?
别急着上算法,咱先把数据整明白:大数据分析的5个基本步骤,你都搞对了吗?
51 4
|
1月前
|
存储 监控 算法
员工行为监控软件中的 Go 语言哈希表算法:理论、实现与分析
当代企业管理体系中,员工行为监控软件已逐步成为维护企业信息安全、提升工作效能的关键工具。这类软件能够实时记录员工操作行为,为企业管理者提供数据驱动的决策依据。其核心支撑技术在于数据结构与算法的精妙运用。本文聚焦于 Go 语言中的哈希表算法,深入探究其在员工行为监控软件中的应用逻辑与实现机制。
61 14
|
2月前
|
自然语言处理 算法 安全
境内深度合成服务算法备案通过名单分析报告
本报告基于《境内深度合成服务算法备案通过名单》,分析了2023年6月至2025年3月公布的10批备案数据,涵盖属地分布、行业应用及产品形式等多个维度。报告显示,深度合成算法主要集中于经济发达地区,如北京、广东、上海等地,涉及教育、医疗、金融、娱乐等多行业。未来趋势显示技术将向多模态融合、行业定制化和安全合规方向发展。建议企业加强技术研发、拓展应用场景、关注政策动态,以在深度合成领域抢占先机。此分析旨在为企业提供参考,助力把握技术发展机遇。
境内深度合成服务算法备案通过名单分析报告
|
2月前
|
供应链 算法 搜索推荐
从公布的前十一批其他算法备案通过名单分析
2025年3月12日,国家网信办发布算法备案信息,深度合成算法通过395款,其他算法45款。前10次备案中,深度合成算法累计3234款,其他类别647款。个性化推送类占比49%,涵盖电商、资讯、视频推荐;检索过滤类占31.53%,用于搜索优化和内容安全;调度决策类占9.12%,集中在物流配送等;排序精选类占8.81%,生成合成类占1.55%。应用领域包括电商、社交媒体、物流、金融、医疗等,互联网科技企业主导,技术向垂直行业渗透,内容安全和多模态技术成新增长点。未来大模型检索和多模态生成或成重点。
从公布的前十一批其他算法备案通过名单分析
|
2月前
|
存储 算法 Java
算法系列之数据结构-二叉树
树是一种重要的非线性数据结构,广泛应用于各种算法和应用中。本文介绍了树的基本概念、常见类型(如二叉树、满二叉树、完全二叉树、平衡二叉树、B树等)及其在Java中的实现。通过递归方法实现了二叉树的前序、中序、后序和层次遍历,并展示了具体的代码示例和运行结果。掌握树结构有助于提高编程能力,优化算法设计。
72 9
 算法系列之数据结构-二叉树
|
2月前
|
算法 Java
算法系列之数据结构-Huffman树
Huffman树(哈夫曼树)又称最优二叉树,是一种带权路径长度最短的二叉树,常用于信息传输、数据压缩等方面。它的构造基于字符出现的频率,通过将频率较低的字符组合在一起,最终形成一棵树。在Huffman树中,每个叶节点代表一个字符,而每个字符的编码则是从根节点到叶节点的路径所对应的二进制序列。
77 3
 算法系列之数据结构-Huffman树
|
2月前
|
算法 Java
算法系列之数据结构-二叉搜索树
二叉查找树(Binary Search Tree,简称BST)是一种常用的数据结构,它能够高效地进行查找、插入和删除操作。二叉查找树的特点是,对于树中的每个节点,其左子树中的所有节点都小于该节点,而右子树中的所有节点都大于该节点。
93 22
|
2月前
|
人工智能 自然语言处理 供应链
从第十批算法备案通过名单中分析算法的属地占比、行业及应用情况
2025年3月12日,国家网信办公布第十批深度合成算法通过名单,共395款。主要分布在广东、北京、上海、浙江等地,占比超80%,涵盖智能对话、图像生成、文本生成等多行业。典型应用包括医疗、教育、金融等领域,如觅健医疗内容生成算法、匠邦AI智能生成合成算法等。服务角色以面向用户为主,技术趋势为多模态融合与垂直领域专业化。
|
3月前
|
存储 机器学习/深度学习 算法
C 408—《数据结构》算法题基础篇—链表(下)
408考研——《数据结构》算法题基础篇之链表(下)。
118 29
|
3月前
|
存储 缓存 监控
企业监控软件中 Go 语言哈希表算法的应用研究与分析
在数字化时代,企业监控软件对企业的稳定运营至关重要。哈希表(散列表)作为高效的数据结构,广泛应用于企业监控中,如设备状态管理、数据分类和缓存机制。Go 语言中的 map 实现了哈希表,能快速处理海量监控数据,确保实时准确反映设备状态,提升系统性能,助力企业实现智能化管理。
53 3