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

【算法与数据结构】Java实现查找与排序

文章目录

  • 第一部分:查找算法
    • 二分查找
    • 插值查找
    • 分块查找
    • 哈希查找
    • 树表查找
  • 第二部分:排序算法
    • 冒泡排序
    • 选择排序
    • 插入排序
    • 快速排序
  • 总结


第一部分:查找算法

二分查找

也叫做折半查找,属于有序查找算法。

前提条件:数组数据必须有序,从小到大,或者从大到小都是可以的。

如果是无序的,也可以先进行排序。
但是排序之后,会改变原有数据的顺序,查找出来元素位置跟原来的元素可能是不一样的,所以排序之后再查找只能判断当前数据是否在容器当中,返回的索引无实际的意义。

二分
基本思想:用给定值先与中间结点比较。

比较完之后有三种情况:

  • 相等 :说明找到了
  • 要查找的数据比中间节点 : 说明要查找的数字在中间节点左边
  • 要查找的数据比中间节点: 说明要查找的数字在中间节点右边
public static void main(String[] args) {//二分查找/折半查找//核心://每次排除一半的查找范围//需求:定义一个方法利用二分查找,查询某个元素在数组中的索引//数据如下:{7, 23, 79, 81, 103, 127, 131, 147}int[] arr = {7, 23, 79, 81, 103, 127, 131, 147};System.out.println(binarySearch(arr, 150));}public static int binarySearch(int[] arr, int number){//1.定义两个变量记录要查找的范围int min = 0;int max = arr.length - 1;//2.利用循环不断的去找要查找的数据while(true){if(min > max){return -1;}//3.找到min和max的中间位置int mid = (min + max) / 2;//4.拿着mid指向的元素跟要查找的元素进行比较if(arr[mid] > number){//4.1 number在mid的左边//min不变,max = mid - 1;max = mid - 1;}else if(arr[mid] < number){//4.2 number在mid的右边//max不变,min = mid + 1;min = mid + 1;}else{//4.3 number跟mid指向的元素一样//找到了return mid;}}}

插值查找

插值:是在二分查找的基础上,让中间的mid点,尽可能靠近想要查找的元素。
1
修改mid公式即可!

private static int binarySearch2(int[] arr, int num) {int min = 0;int max = arr.length - 1;while (true) {if (min > max) {return -1;}int mid = min + (num - arr[min]) / (arr[max] - arr[min]) * (max - min);//这里改公式if (num > arr[mid]) {min = mid + 1;} else if (num < arr[mid]) {max = mid - 1;} else {return mid;}}
}

细节:
对于表长较大,而关键字分布又比较均匀的查找表来说,插值查找算法的平均性能比折半查找要好的多。
反之,数组中如果分布非常不均匀,那可以选折半二分

分块查找

当数据表中的数据元素很多时,可以采用分块查找。

汲取了顺序查找和折半查找各自的优点,既有动态结构,又适于快速查找

分块查找适用于数据较多,但是数据不会发生变化的情况,如果需要一边添加一边查找,建议使用哈希查找

分块查找的过程:

  1. 需要把数据分成N多小块,块与块之间不能有数据重复的交集。
  2. 给每一块创建对象单独存储到数组当中
  3. 查找数据的时候,先在数组查,当前数据属于哪一块
  4. 再到这一块中顺序查找
public class BlockSearchDemo {public static void main(String[] args) {/*分块查找核心思想:块内无序,块间有序实现步骤:1.创建数组blockArr存放每一个块对象的信息2.先查找blockArr确定要查找的数据属于哪一块3.再单独遍历这一块数据即可*/int[] arr = {16, 5, 9, 12,21, 18,32, 23, 37, 26, 45, 34,50, 48, 61, 52, 73, 66};//创建三个块的对象Block b1 = new Block(21,0,5);Block b2 = new Block(45,6,11);Block b3 = new Block(73,12,17);//定义数组用来管理三个块的对象(索引表)Block[] blockArr = {b1,b2,b3};//定义一个变量用来记录要查找的元素int number = 37;//调用方法,传递索引表,数组,要查找的元素int index = getIndex(blockArr,arr,number);//打印一下System.out.println(index);}//利用分块查找的原理,查询number的索引private static int getIndex(Block[] blockArr, int[] arr, int number) {//1.确定number是在那一块当中int indexBlock = findIndexBlock(blockArr, number);if(indexBlock == -1){//表示number不在数组当中return -1;}//2.获取这一块的起始索引和结束索引   --- 30// Block b1 = new Block(21,0,5);   ----  0// Block b2 = new Block(45,6,11);  ----  1// Block b3 = new Block(73,12,17); ----  2int startIndex = blockArr[indexBlock].getStartIndex();int endIndex = blockArr[indexBlock].getEndIndex();//3.遍历for (int i = startIndex; i <= endIndex; i++) {if(arr[i] == number){return i;}}return -1;}//定义一个方法,用来确定number在哪一块当中public static int findIndexBlock(Block[] blockArr,int number){ //100//从0索引开始遍历blockArr,如果number小于max,那么就表示number是在这一块当中的for (int i = 0; i < blockArr.length; i++) {if(number <= blockArr[i].getMax()){return i;}}return -1;}
}class Block{private int max;//最大值private int startIndex;//起始索引private int endIndex;//结束索引//省略 构造 属性 等 JavaBean
}

哈希查找

树表查找

第二部分:排序算法

冒泡排序

Bubble Sort:

数值之间 两两比较 找到最大的数 放到最后
第二轮开始以此类推 :从0-倒数第二 再开始比较 (即不再管最后一个数[依次找得到最大数])

    private static void bubbleSort() {Random random = new Random();int[] arr = new int[10000];for (int i = 0; i < arr.length; i++) {arr[i] = random.nextInt(1000);}long start = System.currentTimeMillis();for (int i = 0; i < arr.length - 1; i++) {for (int j = 0; j < arr.length - 1 - i; j++) {if (arr[j] > arr[j + 1]) {int temp = arr[j];arr[j] = arr[j + 1];arr[j + 1] = temp;}}}long end = System.currentTimeMillis();System.out.println(end - start);for (int i = 0; i < arr.length; i++) {System.out.print(arr[i] + " ");}}

重复的遍历过要排序的数列,一次比较相邻的两个元素,如果他们的顺序错误就把他们交换过来。

总结步骤:

  1. 相邻的元素两两比较,大的放右边,小的放左边
  2. 第一轮比较完毕之后,最大值就已经确定,第二轮可以少循环一次,后面以此类推
  3. 如果数组中有n个数据,总共我们只要执行n-1轮的代码就可以
public class BubbleDemo {public static void main(String[] args) {/*冒泡排序:核心思想:1,相邻的元素两两比较,大的放右边,小的放左边。2,第一轮比较完毕之后,最大值就已经确定,第二轮可以少循环一次,后面以此类推。3,如果数组中有n个数据,总共我们只要执行n-1轮的代码就可以。*///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 4if(arr[j] > arr[j + 1]){int temp = arr[j];arr[j] = arr[j + 1];arr[j + 1] = temp;}}}printArr(arr);}private static void printArr(int[] arr) {//3.遍历数组for (int i = 0; i < arr.length; i++) {System.out.print(arr[i] + " ");}System.out.println();}
}

选择排序

找到最小的数 排在最前面
第一轮: 0索引位置的数 与后面的数依次比较 找到最小的互换位置
第二轮: 1索引位置的数 与… 以此循环
注意内循环 是从1 开始 所以 arr.length不减1

//for (int i = 0; i < arr.length - 1; i++) {
//    if (arr[0] > arr[i + 1]) {
//        int temp = arr[0];
//        arr[0] = arr[i + 1];
//        arr[i + 1] = temp;
//    }
//}
//
//for (int i = 1; i < arr.length - 1; i++) {
//    if (arr[1] > arr[i + 1]) {
//        int temp = arr[1];
//        arr[1] = arr[i + 1];
//        arr[i + 1] = temp;
//    }
//}// 关于为什么 (j = 1 +i) :
//① i = 0 -> j=  1 ~ 5
//② i = 1 -> j = 2 ~ 5
//...
for (int i = 0; i < arr.length - 1; i++) {for (int j = 1 + i; j < arr.length; j++) {if (arr[i] > arr[j]) {int temp = arr[i];arr[i] = arr[j];arr[j] = temp;}}
}
    private static void selectionSort() {int[] arr = {2, 4, 5, 3, 1};for (int i = 0; i < arr.length - 1; i++) {for (int j = 1 + i; j < arr.length; j++) {if (arr[i] > arr[j]) {int temp = arr[i];arr[i] = arr[j];arr[j] = temp;}}}for (int i = 0; i < arr.length; i++) {System.out.print(arr[i] + " ");}}

总结步骤:

  1. 从0索引开始,跟后面的元素一一比较
  2. 小的放前面,大的放后面
  3. 第一次循环结束后,最小的数据已经确定
  4. 第二次循环从1索引开始以此类推
  5. 第三轮循环从2索引开始以此类推
  6. 第四轮循环从3索引开始以此类推。
public class SelectionDemo {public static void main(String[] args) {/*选择排序:1,从0索引开始,跟后面的元素一一比较。2,小的放前面,大的放后面。3,第一次循环结束后,最小的数据已经确定。4,第二次循环从1索引开始以此类推。*///1.定义数组int[] arr = {2, 4, 5, 3, 1};//2.利用选择排序让数组变成 1 2 3 4 5/* //第一轮://从0索引开始,跟后面的元素一一比较。for (int i = 0 + 1; i < arr.length; i++) {//拿着0索引跟后面的数据进行比较if(arr[0] > arr[i]){int temp = arr[0];arr[0] = arr[i];arr[i] = temp;}}*///最终代码://外循环:几轮//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;}}}printArr(arr);}private static void printArr(int[] arr) {//3.遍历数组for (int i = 0; i < arr.length; i++) {System.out.print(arr[i] + " ");}System.out.println();}
}

插入排序

  1. 首先分为 有序的部分 无序的部分 数据

  2. 无序的部分第一个数 依次 与有序的数据 倒序进行对比 , 然后将无序的部分插入到有序的适当位置

  3. 依次循环 每个无序的第一个数 与有序的倒序 对比 加入到有序的部分 中

  4. 其实就是 跟它的前一个数对比 交换位置

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;break;}
}//2.遍历无序 (可以用while 或者 for) 用while 比较清晰
for (int i = startIndex; i < arr.length; i++) {//int j = i;////while (j > 0 && arr[j] < arr[j - 1]) {//    int temp = arr[j];//    arr[j] = arr[j - 1];//    arr[j - 1] = temp;//    j--;//}for (int j = i; j > 0 && arr[j] < arr[j - 1]; j--) {int temp = arr[j];arr[j] = arr[j - 1];arr[j - 1] = temp;}
}

插入排序在插入的时候,有优化算法,在遍历有序序列找正确位置时,可以采取二分查找

   private static void insertionSorting() {int[] arr = {3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48};//1.找到无序的部分的索引头int index = findOrderPart(arr);for (int i = index; i < arr.length; i++) {for (int j = i; j > 0 && arr[j] < arr[j - 1]; j--) {int temp = arr[j];arr[j] = arr[j - 1];arr[j - 1] = temp;}}for (int i = 0; i < arr.length; i++) {System.out.print(arr[i] + " ");}}private static int findOrderPart(int[] arr) {for (int i = 0; i < arr.length; i++) {if (arr[i] > arr[i + 1]) {return i + 1;}}return -1;}
public class InsertDemo {public static void main(String[] args) {/*插入排序:将0索引的元素到N索引的元素看做是有序的,把N+1索引的元素到最后一个当成是无序的。遍历无序的数据,将遍历到的元素插入有序序列中适当的位置,如遇到相同数据,插在后面。N的范围:0~最大索引*/int[] arr = {3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48};//1.找到无序的哪一组数组是从哪个索引开始的。  2int startIndex = -1;for (int i = 0; i < arr.length; i++) {if(arr[i] > arr[i + 1]){startIndex = i + 1;break;}}//2.遍历从startIndex开始到最后一个元素,依次得到无序的哪一组数据中的每一个元素for (int i = startIndex; i < arr.length; i++) {//问题:如何把遍历到的数据,插入到前面有序的这一组当中//记录当前要插入数据的索引int j = i;while(j > 0 && arr[j] < arr[j - 1]){//交换位置int temp = arr[j];arr[j] = arr[j - 1];arr[j - 1] = temp;j--;}}printArr(arr);}private static void printArr(int[] arr) {//3.遍历数组for (int i = 0; i < arr.length; i++) {System.out.print(arr[i] + " ");}System.out.println();}}

快速排序

  1. 定义两个索引:从前往后 寻找 比基准数大的 // 从后往前 寻找 比基准数小的 --> 找到两个索引交换位置
  2. 直到 两个索引交汇在同一个数值索引位置上 为止
  3. 之后 这个交汇位置的值 与 基准数 进行交换
  4. 以上为一轮交换逻辑。
  5. 循环使用递归方法进行排序, 以基准数为分界线,分为两部分,两次递归 重复排序
    213
    private static void quickSort(int[] arr, int i, int j) {int start = i;int end = j;if (start > end) {return;}int baseNum = arr[i];while (start != end) {while (true) {if (start >= end || arr[end] < baseNum) {break;}end--;}while (true) {if (start >= end || arr[start] > baseNum) {break;}start++;}int temp = arr[start];arr[start] = arr[end];arr[end] = temp;}int temp = arr[i];arr[i] = arr[start];arr[start] = temp;quickSort(arr, i, start - 1);quickSort(arr, start + 1, j);}

总结步骤:

  1. 从数列中挑出一个元素,一般都是左边第一个数字,称为 “基准数”;
  2. 创建两个指针,一个从前往后走,一个从后往前走。
  3. 先执行后面的指针,找出第一个比基准数小的数字
  4. 再执行前面的指针,找出第一个比基准数大的数字
  5. 交换两个指针指向的数字
  6. 直到两个指针相遇
  7. 将基准数跟指针指向位置的数字交换位置,称之为:基准数归位。
  8. 第一轮结束之后,基准数左边的数字都是比基准数小的,基准数右边的数字都是比基准数大的。
  9. 把基准数左边看做一个序列,把基准数右边看做一个序列,按照刚刚的规则递归排序
public class QuickSortDemo {public static void main(String[] args) {System.out.println(Integer.MAX_VALUE);System.out.println(Integer.MIN_VALUE);/*快速排序:第一轮:以0索引的数字为基准数,确定基准数在数组中正确的位置。比基准数小的全部在左边,比基准数大的全部在右边。后面以此类推。*/int[] arr = {6, 1, 2, 7, 9, 3, 4, 5, 10, 8};//int[] arr = new int[1000000];/* Random r = new Random();for (int i = 0; i < arr.length; i++) {arr[i] = r.nextInt();}*/long start = System.currentTimeMillis();quickSort(arr, 0, arr.length - 1);long end = System.currentTimeMillis();System.out.println(end - start);//149System.out.println(Arrays.toString(arr));//课堂练习://我们可以利用相同的办法去测试一下,选择排序,冒泡排序以及插入排序运行的效率//得到一个结论:快速排序真的非常快。/* for (int i = 0; i < arr.length; i++) {System.out.print(arr[i] + " ");}*/}/**   参数一:我们要排序的数组*   参数二:要排序数组的起始索引*   参数三:要排序数组的结束索引* */public static void quickSort(int[] arr, int i, int j) {//定义两个变量记录要查找的范围int start = i;int end = j;if(start > end){//递归的出口return;}//记录基准数int baseNumber = arr[i];//利用循环找到要交换的数字while(start != end){//利用end,从后往前开始找,找比基准数小的数字//int[] arr = {1, 6, 2, 7, 9, 3, 4, 5, 10, 8};while(true){if(end <= start || arr[end] < baseNumber){break;}end--;}System.out.println(end);//利用start,从前往后找,找比基准数大的数字while(true){if(end <= start || arr[start] > baseNumber){break;}start++;}//把end和start指向的元素进行交换int temp = arr[start];arr[start] = arr[end];arr[end] = temp;}//当start和end指向了同一个元素的时候,那么上面的循环就会结束//表示已经找到了基准数在数组中应存入的位置//基准数归位//就是拿着这个范围中的第一个数字,跟start指向的元素进行交换int temp = arr[i];arr[i] = arr[start];arr[start] = temp;//确定6左边的范围,重复刚刚所做的事情quickSort(arr,i,start - 1);//确定6右边的范围,重复刚刚所做的事情quickSort(arr,start + 1,j);}
}

总结

2.1

相关文章:

  • TPU编程竞赛系列|第八届集创赛“算能杯“报名开启!
  • 阿里云服务器配置选择之线下IDC直接映射
  • 【备战蓝桥杯】吃奶酪问题 / 超硬核,文附template拓展知识!
  • 位运算的规则(算法村第十一关青铜挑战)
  • ❤ Uniapp使用四( 高阶使用配置和各种实现篇)
  • 【CC++】为什么 scanf 函数在读取字符串时不需要用取地址运算符
  • git中合并分支时出现了代码冲突怎么办
  • 可狱可囚的爬虫系列课程 12:在网站中寻找 API 接口(补充)(王者荣耀英雄信息抓取)
  • 【AUTOSAR】RTE 接口类型、应用场景差别及实例讲解
  • docker使用指南疑难杂症
  • Flink启动Yarn Session报错:Couldn‘t deploy Yarn session cluster
  • LeetCode 2744. 最大字符串配对数目
  • 【cmake/makefile】C程序编译过程分析
  • vue3-表单输入绑定
  • 谷达冠楠科技:抖音开网店新手小白可以卖的产品
  • -------------------- 第二讲-------- 第一节------在此给出链表的基本操作
  • 【comparator, comparable】小总结
  • 【笔记】你不知道的JS读书笔记——Promise
  • 0x05 Python数据分析,Anaconda八斩刀
  • ES学习笔记(10)--ES6中的函数和数组补漏
  • FastReport在线报表设计器工作原理
  • Laravel Mix运行时关于es2015报错解决方案
  • Linux编程学习笔记 | Linux IO学习[1] - 文件IO
  • React 快速上手 - 06 容器组件、展示组件、操作组件
  • 区块链分支循环
  • 三栏布局总结
  • 深入浅出Node.js
  • 深入体验bash on windows,在windows上搭建原生的linux开发环境,酷!
  • 事件委托的小应用
  • 用quicker-worker.js轻松跑一个大数据遍历
  • 最近的计划
  • MyCAT水平分库
  • 进程与线程(三)——进程/线程间通信
  • $con= MySQL有关填空题_2015年计算机二级考试《MySQL》提高练习题(10)
  • (02)Hive SQL编译成MapReduce任务的过程
  • (04)odoo视图操作
  • (C语言)逆序输出字符串
  • (Redis使用系列) SpringBoot中Redis的RedisConfig 二
  • (zhuan) 一些RL的文献(及笔记)
  • (搬运以学习)flask 上下文的实现
  • (动手学习深度学习)第13章 计算机视觉---图像增广与微调
  • (二)hibernate配置管理
  • (附源码)springboot 个人网页的网站 毕业设计031623
  • (附源码)springboot炼糖厂地磅全自动控制系统 毕业设计 341357
  • (切换多语言)vantUI+vue-i18n进行国际化配置及新增没有的语言包
  • (亲测成功)在centos7.5上安装kvm,通过VNC远程连接并创建多台ubuntu虚拟机(ubuntu server版本)...
  • (一)硬件制作--从零开始自制linux掌上电脑(F1C200S) <嵌入式项目>
  • (转)Sublime Text3配置Lua运行环境
  • (转)重识new
  • (转载)微软数据挖掘算法:Microsoft 时序算法(5)
  • .NET Conf 2023 回顾 – 庆祝社区、创新和 .NET 8 的发布
  • .NET 跨平台图形库 SkiaSharp 基础应用
  • .Net8 Blazor 尝鲜
  • .NET国产化改造探索(三)、银河麒麟安装.NET 8环境
  • .net之微信企业号开发(一) 所使用的环境与工具以及准备工作