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);
    }
}

总结

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


相关文章
|
2月前
|
监控 算法 网络协议
Java 实现局域网电脑屏幕监控算法揭秘
在数字化办公环境中,局域网电脑屏幕监控至关重要。本文介绍用Java实现这一功能的算法,涵盖图像采集、数据传输和监控端显示三个关键环节。通过Java的AWT/Swing库和Robot类抓取屏幕图像,使用Socket进行TCP/IP通信传输图像数据,并利用ImageIO类在监控端展示图像。整个过程确保高效、实时和准确,为提升数字化管理提供了技术基础。
84 15
|
3月前
|
算法 数据处理 C语言
C语言中的位运算技巧,涵盖基本概念、应用场景、实用技巧及示例代码,并讨论了位运算的性能优势及其与其他数据结构和算法的结合
本文深入解析了C语言中的位运算技巧,涵盖基本概念、应用场景、实用技巧及示例代码,并讨论了位运算的性能优势及其与其他数据结构和算法的结合,旨在帮助读者掌握这一高效的数据处理方法。
88 1
|
14天前
|
存储 机器学习/深度学习 算法
C 408—《数据结构》算法题基础篇—链表(下)
408考研——《数据结构》算法题基础篇之链表(下)。
76 29
|
14天前
|
存储 算法 C语言
C 408—《数据结构》算法题基础篇—链表(上)
408考研——《数据结构》算法题基础篇之链表(上)。
72 25
|
11天前
|
存储 算法 Java
解锁“分享文件”高效密码:探秘 Java 二叉搜索树算法
在信息爆炸的时代,文件分享至关重要。二叉搜索树(BST)以其高效的查找性能,为文件分享优化提供了新路径。本文聚焦Java环境下BST的应用,介绍其基础结构、实现示例及进阶优化。BST通过有序节点快速定位文件,结合自平衡树、多线程和权限管理,大幅提升文件分享效率与安全性。代码示例展示了文件插入与查找的基本操作,适用于大规模并发场景,确保分享过程流畅高效。掌握BST算法,助力文件分享创新发展。
|
14天前
|
存储 人工智能 算法
C 408—《数据结构》算法题基础篇—数组(通俗易懂)
408考研——《数据结构》算法题基础篇之数组。(408算法题的入门)
58 23
|
24天前
|
存储 人工智能 算法
解锁分布式文件分享的 Java 一致性哈希算法密码
在数字化时代,文件分享成为信息传播与协同办公的关键环节。本文深入探讨基于Java的一致性哈希算法,该算法通过引入虚拟节点和环形哈希空间,解决了传统哈希算法在分布式存储中的“哈希雪崩”问题,确保文件分配稳定高效。文章还展示了Java实现代码,并展望了其在未来文件分享技术中的应用前景,如结合AI优化节点布局和区块链增强数据安全。
|
25天前
|
算法 安全 Java
Java线程调度揭秘:从算法到策略,让你面试稳赢!
在社招面试中,关于线程调度和同步的相关问题常常让人感到棘手。今天,我们将深入解析Java中的线程调度算法、调度策略,探讨线程调度器、时间分片的工作原理,并带你了解常见的线程同步方法。让我们一起破解这些面试难题,提升你的Java并发编程技能!
65 16
|
1月前
|
运维 监控 算法
企业局域网监控软件中 Java 优先队列算法的核心优势
企业局域网监控软件是数字化时代企业网络安全与高效运营的基石,犹如一位洞察秋毫的卫士。通过Java实现的优先队列算法,它能依据事件优先级排序,确保关键网络事件如异常流量、数据泄露等被优先处理,保障系统稳定与安全。代码示例展示了如何定义网络事件类并使用PriorityQueue处理高优先级事件,尤其在面对疑似风险时迅速启动应急措施。这一核心技术助力企业在复杂网络环境中稳健前行,护航业务腾飞。
65 32
|
1月前
|
存储 监控 算法
剖析基于Java算法驱动的智能局域网管控之道
本文探讨了基于Java语言的局域网控制方案,结合链表数据结构与令牌桶算法,解决设备管理和流量调度难题。通过链表灵活存储网络设备信息,实现高效设备管理;令牌桶算法则精准控制流量,确保网络平稳运行。二者相辅相成,为校园、企业等局域网提供稳固高效的控制体系,保障业务连续性和数据安全。

热门文章

最新文章