当前位置: 首页 > news >正文

Java中查找与排序算法探究

二分查找

二分查找又叫折半查找,要求待查找的序列有序。每次取中间位置的值与待查关键字比较,如果中间位置
的值比待查关键字大,则在前半部分循环这个查找的过程,如果中间位置的值比待查关键字小,
则在后半部分循环这个查找的过程。直到查找到了为止,否则序列中没有待查的关键字。代码如下

public class BinarySearch {// 二分查找方法public static int binarySearch(int[] array, int target) {int left = 0;int right = array.length - 1;while (left <= right) {int mid = left + (right - left) / 2; // 防止溢出// 检查中间元素if (array[mid] == target) {return mid; // 找到目标元素,返回索引}// 如果目标大于中间元素,忽略左半边if (array[mid] < target) {left = mid + 1;} else { // 如果目标小于中间元素,忽略右半边right = mid - 1;}}return -1; // 未找到目标元素}public static void main(String[] args) {int[] sortedArray = {1, 3, 5, 6, 7, 11, 13, 15};int target = 7;int result = binarySearch(sortedArray, target);if (result != -1) {System.out.println("元素 " + target + " 在数组中的索引为: " + result);} else {System.out.println("元素 " + target + " 不在数组中。");}}
}

算法解析

int mid = left + (right - left) / 2;

基本概念

  • left:当前搜索范围的左边界索引。
  • right:当前搜索范围的右边界索引。
  • mid:当前搜索范围的中间索引。

防止溢出
在某些编程语言中(尤其是在处理大整数时),(left + right) 可能会超出整数的最大值,导致溢出。虽然在 Java 中这种情况不太常见,但在某些情况下(如大型数组和极端边界值)仍然可能发生。
为了防止这种情况,使用 left + (right - left) / 2。这个公式可以避免直接相加两个大数,而是先计算 right - left,再除以 2,最后加上 left。
公式拆解

  • right - left:是计算整个区间的长度
  • (right - left) / 2:找到当前区间长度的一半,这就是偏移量。
  • left +偏移量:将偏移量加到左边界上,就得到了中间位置。

时间复杂度和空间复杂度
时间复杂度:O(log n)

  • 原因:二分查找每次将搜索区间缩小一半,因此它的时间复杂度是对数级别的。假设数组长度为 n,每一步操作后,区间缩小为原来的一半,所以最多需要进行 log₂n 次比较即可找到目标元素或确认元素不存在。

空间复杂度:O(1)

  • 原因:二分查找只需要几个额外的变量(如 left、right、mid)来保存索引信息,不需要额外的存储空间与输入规模成比例增加,因此其空间复杂度是常数级别的。

冒泡排序

冒泡排序的工作原理:

  • 它比较相邻的两个元素,如果它们的顺序错误就交换它们。
  • 每一轮都能把当前未排序部分中最大的元素“冒泡”到正确的位置。
  • 优化:如果某一轮没有发生任何交换,则说明数组已经排序好了,可以提前终止。

代码如下:

public class BubbleSort {public static void main(String[] args) {int[] arr = {1, 9, 5, 3, 7, 18, 13, 15};System.out.println("排序前");printArray(arr);//冒泡排序思想  比较前后相邻两个数据  如果第一个个数据大于第二个数据 将这两个数据交换boolean swapped;int length = arr.length;for (int i = 0; i < length; i++) {swapped = false;for (int j = 0; j < length - i - 1; j++) {// 交换 arr[j] 和 arr[j+1]if (arr[j] > arr[j + 1]) {int temp = arr[j];arr[j] = arr[j + 1];arr[j + 1] = temp;swapped = true;}}// 如果没有发生交换,数组已经是有序的 跳出循环if (swapped == false) break;}System.out.println("排序后");printArray(arr);}public static void printArray(int[] arr) {for (int value : arr) {System.out.print(value + " ");}System.out.println();}
}

算法解析
冒泡排序的思想主要通过两层遍历(循环)来实现,其中每一层循环分别代表不同的逻辑:

  • 外层循环:
    作用:控制整个排序过程进行的轮数。
    具体说明:对于一个长度为 n 的数组,最多需要进行 n-1 轮比较(实际上可能会更少,如果提前检测到数组已经有序)。在每一轮中,将最大或最小的元素移动到未排序部分的末尾。
    它确保每一轮结束后,至少有一个元素被放置在其最终位置。
  • 内层循环:
    作用:在当前轮次中,进行相邻元素的比较和交换。
    具体说明:它在每一轮中逐个比较数组中的相邻元素,并根据需要交换它们,以确保当前未排序部分最大的元素“冒泡”到未排序部分的末尾。
    每次遍历都会将未排序部分的最大值放到最后,从而逐步减少需要比较的元素数量。

时间复杂度和空间复杂度
时间复杂度:

  • 最坏情况时间复杂度:O(n²)
    在最坏的情况下(例如,数组是按降序排列的),冒泡排序需要进行 (n-1) 轮比较,每一轮都需要进行 (n-i-1) 次比较和交换操作(其中 (i) 是当前的轮次)。因此,总的比较和交换次数约为 ((n-1) + (n-2) + … + 1 = \frac{n(n-1)}{2}),这就是 O(n²)。
    平均情况时间复杂度:O(n²)

  • 在平均情况下,元素是随机分布的,冒泡排序仍然需要进行大量的比较和交换。平均下来,其时间复杂度仍为 O(n²)。

  • 最好情况时间复杂度:O(n)
    在最好的情况下(例如,数组已经是有序的),冒泡排序只需要进行一次遍历来确认数组是否已经排序。在这种情况下,只需进行 (n-1) 次比较,无需任何交换。因此时间复杂度为 O(n)。
    空间复杂度

空间复杂度:O(1)
冒泡排序是一种原地排序算法,意味着它不需要额外的存储空间来存放临时数据(除了用于交换的一个临时变量)。因此,它的空间复杂度为 O(1)。

插入排序:

通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应的位置并插入。
插入排序非常类似于整扑克牌。在开始摸牌时,左手是空的,牌面朝下放在桌上。接着,一次从
桌上摸起一张牌,并将它插入到左手一把牌中的正确位置上。为了找到这张牌的正确位置,要将
它与手中已有的牌从右到左地进行比较。无论什么时候,左手中的牌都是排好序的。

  • 如果输入数组已经是排好序的话,插入排序出现最佳情况,运行时间是输入规模的一个线性函数。
  • 如果输入数组是逆序排列的,将出现最坏情况。平均情况与最坏情况一样,其时间代价是(n2)。

代码如下

public class InsertionSort {// 主方法,用于测试public static void main(String[] args) {int[] array = {12, 11, 13, 5, 6};System.out.println("未排序的数组:");printArray(array);insertionSort(array);System.out.println("\n已排序的数组:");printArray(array);}// 插入排序函数public static void insertionSort(int[] array) {int n = array.length;for (int i = 1; i < n; ++i) {//待插入的数据int key = array[i];int j = i - 1;//此循环操作 将key与左边已经排好序的队列比对  将比key大的元素逐个向右移动  直到移动到最佳位置while (j >= 0 && array[j] > key) {array[j + 1] = array[j];j = j - 1;}array[j + 1] = key; // 插入key到正确位置}}// 打印数组的方法public static void printArray(int[] arr) {for (int value : arr) {System.out.print(value + " ");}System.out.println();}
}

算法解析

  • 初始状态:
    将第一个元素视为已经排序的序列,其余元素视为未排序的序列。
  • 遍历未排序序列:
    从第二个元素开始,依次将每个元素插入到前面已排好序的部分中。
  • 插入过程:
    对于每一个待插入的元素,从已排序部分的末尾开始向前扫描。
    比较待插入元素与已排序元素,如果已排序元素比待插入元素大,则将其右移一位。
    重复上述过程,直到找到一个不大于待插入元素的位置,将待插入元素放到该位置。
  • 重复上述步骤:
    依次处理剩余的未排序元素,直到整个数组有序。

举例说明
假设我们有一个数组 [12, 11, 13, 5, 6],使用插入排序进行排序:
初始状态:[12] | [11, 13, 5, 6]

  • 第一轮(i=1):将 11 插入到 [12] 中:
    比较 11 和 12,因为 11 < 12,所以交换位置。
    数组变为 [11, 12] | [13, 5, 6]
  • 第二轮(i=2):将 13 插入到 [11, 12] 中:
    13 > 12,直接追加到已排序部分。
    数组变为 [11, 12, 13] | [5, 6]
  • 第三轮(i=3):将 5 插入到 [11, 12, 13] 中:
    从后向前比较,发现 5 < 13, 5 < 12, 5 < 11。
    将 5 插入到最前面。
    数组变为 [5, 11, 12, 13] | [6]
  • 第四轮(i=4):将 6 插入到 [5, 11, 12, 13] 中:
    从后向前比较,发现 6 < 13, 6 < 12, 6 > 5。
    将 6 插入到 5 后面。

最终数组为 [5, 6, 11, 12, 13]

时间复杂度和空间复杂度

时间复杂度

  • 最坏情况时间复杂度:O(n²)
    在最坏情况下(例如,数组是按降序排列的),每个元素都需要与之前的所有元素进行比较和移动。对于第 (i) 个元素,这意味着需要进行 (i) 次比较。因此,总的比较和移动次数约为 (\frac{n(n-1)}{2}),这就是 O(n²)。
  • 平均情况时间复杂度:O(n²)
    在平均情况下,插入排序仍然需要进行大量的比较和移动,具体操作次数与最坏情况类似,因此平均时间复杂度也为 O(n²)。
  • 最好情况时间复杂度:O(n)
    在最好的情况下(例如,数组已经是有序的),每个元素只需与前一个元素比较一次即可确认其位置,无需移动。因此,只需进行 (n-1) 次比较,时间复杂度为 O(n)。

空间复杂度

  • 空间复杂度:O(1)
    插入排序是一种原地排序算法,它通过在数组内部交换元素来实现排序,不需要额外的存储空间来存放临时数据(除了用于插入的临时变量)。因此,它的空间复杂度为 O(1)。

http://www.mrgr.cn/news/66337.html

相关文章:

  • C++ 的 new 操作符与 C 语言的 malloc 函数的区别
  • MoonBit 双周报 Vol.59:新增编译器常量支持,改进未使用警告,支持跨包函数导入...多个关键技术持续优化中!
  • 容器迭代器
  • 数据结构————链表
  • 鸿蒙进阶-AlphabetIndexer组件
  • Zabbix监控架构
  • 阿里云服务器 篇十(加更):自动定时备份CSDN博客内容:优化内存和解决图片展示等问题
  • 5分钟上手 Kubernetes:精简实用的 Kubectl 命令速查宝典!
  • 【ESP32+MicroPython】热点模式及网页控制
  • 产品增长之付费推广
  • 光伏设计软件如何快速上手?
  • 【万字详文介绍】:迭代扩张卷积神经网络(IDCNN)
  • 模拟实现C库函数~
  • 【OJ题解】在字符串中查找第一个不重复字符的索引
  • 华为HarmonyOS借助AR引擎帮助应用实现虚拟与现实交互的能力5-识别平面语义
  • 【LeetCode】【算法】146. LRU缓存
  • Python学习笔记-生成器的应用与原理
  • 好看的超清4K视频素材去哪儿找?下载素材资源网站推荐
  • AI大模型重塑软件开发:流程、优势、挑战与展望
  • 「C/C++」C/C++标准库 之 #include<cctype> 字符分类处理库
  • 牛客周赛 66 F 小苯的字符提前
  • 进程的调度(超详细解读)
  • Day 49 || 1143.最长公共子序列、1035.不相交的线、 53. 最大子序和 、392.判断子序列
  • Java入门(8)--反射机制
  • 零基础学习Spring AI Java AI SpringBoot AI调用大模型OpenAi Ollama集成大模型
  • HarmonyOS开发 - Ability往页面(Pages)中传递数据