java 查找排序_查找与排序算法(Java实现)

1、二分查找算法

package other;

public class BinarySearch {

/*

* 循环实现二分查找算法arr 已排好序的数组x 需要查找的数-1 无法查到数据

*/

public static int binarySearch(int[] arr, int x) {

int low = 0;

int high = arr.length-1;

while(low <= high) {

int middle = (low + high)/2;

if(x == arr[middle]) {

return middle;

}else if(x

high = middle - 1;

}else {

low = middle + 1;

}

}

return -1;

}

//递归实现二分查找

public static int binarySearch(int[] dataset,int data,int beginIndex,int endIndex){

int midIndex = (beginIndex+endIndex)/2;

if(data dataset[endIndex]||beginIndex>endIndex){

return -1;

}

if(data

return binarySearch(dataset,data,beginIndex,midIndex-1);

}else if(data>dataset[midIndex]){

return binarySearch(dataset,data,midIndex+1,endIndex);

}else {

return midIndex;

}

}

public static void main(String[] args) {

int[] arr = { 6, 12, 33, 87, 90, 97, 108, 561 };

System.out.println("循环查找:" + (binarySearch(arr, 87) + 1));

System.out.println("递归查找"+binarySearch(arr,3,87,arr.length-1));

}

}

时间复杂度

比如:总共有n个元素,每次查找的区间大小就是n,n/2,n/4,…,n/2^k(接下来操作元素的剩余个数),其中k就是循环的次数。

由于n/2^k取整后>=1,即令n/2^k=1,

可得k=log2n,(是以2为底,n的对数),所以时间复杂度可以表示O()=O(logn)

2、归并排序

归并排序(Merge)是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列。

归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。 将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为2-路归并。

归并排序算法稳定,数组需要O(n)的额外空间,链表需要O(log(n))的额外空间,时间复杂度为O(nlog(n)),算法不是自适应的,不需要对数据的随机读取。

工作原理:

1、申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列

2、设定两个指针,最初位置分别为两个已经排序序列的起始位置

3、比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置

4、重复步骤3直到某一指针达到序列尾

5、将另一序列剩下的所有元素直接复制到合并序列尾

public class MergeSortTest {

public static void main(String[] args) {

int[] data = new int[] { 5, 3, 6, 2, 1, 9, 4, 8, 7 };

print(data);

mergeSort(data);

System.out.println("排序后的数组:");

print(data);

}

public static void mergeSort(int[] data) {

sort(data, 0, data.length - 1);

}

public static void sort(int[] data, int left, int right) {

if (left >= right)

return;

// 找出中间索引

int center = (left + right) / 2;

// 对左边数组进行递归

sort(data, left, center);

// 对右边数组进行递归

sort(data, center + 1, right);

// 合并

merge(data, left, center, right);

print(data);

}

/**

* 将两个数组进行归并,归并前面2个数组已有序,归并后依然有序

*

* @param data

* 数组对象

* @param left

* 左数组的第一个元素的索引

* @param center

* 左数组的最后一个元素的索引,center+1是右数组第一个元素的索引

* @param right

* 右数组最后一个元素的索引

*/

public static void merge(int[] data, int left, int center, int right) {

// 临时数组

int[] tmpArr = new int[data.length];

// 右数组第一个元素索引

int mid = center + 1;

// third 记录临时数组的索引

int third = left;

// 缓存左数组第一个元素的索引

int tmp = left;

while (left <= center && mid <= right) {

// 从两个数组中取出最小的放入临时数组

if (data[left] <= data[mid]) {

tmpArr[third++] = data[left++];

} else {

tmpArr[third++] = data[mid++];

}

}

// 剩余部分依次放入临时数组(实际上两个while只会执行其中一个)

while (mid <= right) {

tmpArr[third++] = data[mid++];

}

while (left <= center) {

tmpArr[third++] = data[left++];

}

// 将临时数组中的内容拷贝回原数组中

// (原left-right范围的内容被复制回原数组)

while (tmp <= right) {

data[tmp] = tmpArr[tmp++];

}

}

public static void print(int[] data) {

for (int i = 0; i < data.length; i++) {

System.out.print(data[i] + "\t");

}

System.out.println();

}

}

static void merge_sort_recursive(int[] arr, int[] result, int start, int end) {

if (start >= end)

return;

int len = end - start, mid = (len >> 1) + start;

int start1 = start, end1 = mid;

int start2 = mid + 1, end2 = end;

merge_sort_recursive(arr, result, start1, end1);

merge_sort_recursive(arr, result, start2, end2);

int k = start;

while (start1 <= end1 && start2 <= end2)

result[k++] = arr[start1] < arr[start2] ? arr[start1++] : arr[start2++];

while (start1 <= end1)

result[k++] = arr[start1++];

while (start2 <= end2)

result[k++] = arr[start2++];

for (k = start; k <= end; k++)

arr[k] = result[k];

}

public static void merge_sort(int[] arr) {

int len = arr.length;

int[] result = new int[len];

merge_sort_recursive(arr, result, 0, len - 1);

}

3、快速排序

算法概述/思路

快速排序一般基于递归实现。其思路是这样的:

1.选定一个合适的值(理想情况中值最好,但实现中一般使用数组第一个值),称为“枢轴”(pivot)。

2.基于这个值,将数组分为两部分,较小的分在左边,较大的分在右边。

3.可以肯定,如此一轮下来,这个枢轴的位置一定在最终位置上。

4.对两个子数组分别重复上述过程,直到每个数组只有一个元素。

5.排序完成。

public static void quickSort(int[] arr){

qsort(arr, 0, arr.length-1);

}

private static void qsort(int[] arr, int low, int high){

if (low < high){

int pivot=partition(arr, low, high); //将数组分为两部分

qsort(arr, low, pivot-1); //递归排序左子数组

qsort(arr, pivot+1, high); //递归排序右子数组

}

}

private static int partition(int[] arr, int low, int high){

int pivot = arr[low]; //枢轴记录

while (low

while (low=pivot) --high;

arr[low]=arr[high]; //交换比枢轴小的记录到左端

while (low

arr[high] = arr[low]; //交换比枢轴小的记录到右端

}

//扫描完成,枢轴到位

arr[low] = pivot;

//返回的是枢轴的位置

return low;

}

算法性能/复杂度

可以看出,每一次调用partition()方法都需要扫描一遍数组长度(注意,在递归的时候这个长度并不是原数组的长度n,而是被分隔出来的小数组,即n*(2^(-i))),其中i为调用深度。而在这一层同样长度的数组有2^i个。那么,每层排序大约需要O(n)复杂度。而一个长度为n的数组,调用深度最多为log(n)层。二者相乘,得到快速排序的平均复杂度为O(n ㏒n)。

通常,快速排序被认为是在所有同数量级的排序方法中,平均性能最好。

从代码中可以很容易地看出,快速排序单个栈的空间复杂度不高,每次调用partition方法时,其额外开销只有O(1)。所以,最好情形下快速排序空间复杂度大约为O(㏒n)。

算法优化

上面这个快速排序算法可以说是最基本的快速排序,因为它并没有考虑任何输入数据。但是,我们很容易发现这个算法的缺陷:这就是在我们输入数据基本有序甚至完全有序的时候,这算法退化为冒泡排序,不再是O(n㏒n),而是O(n^2)了。

究其根源,在于我们的代码实现中,每次只从数组第一个开始取。如果我们采用“三者取中”,即arr[low],arr[high],arr[(low+high)/2]三者的中值作为枢轴记录,则可以大大提高快速排序在最坏情况下的性能。但是,我们仍然无法将它在数组有序情形下的性能提高到O(n)。还有一些方法可以不同程度地提高快速排序在最坏情况下的时间性能。

此外,快速排序需要一个递归栈,通常情况下这个栈不会很深,为log(n)级别。但是,如果每次划分的两个数组长度严重失衡,则为最坏情况,栈的深度将增加到O(n)。此时,由栈空间带来的空间复杂度不可忽略。如果加上额外变量的开销,这里甚至可能达到恐怖的O(n^2)空间复杂度。所以,快速排序的最差空间复杂度不是一个定值,甚至可能不在一个级别。

为了解决这个问题,我们可以在每次划分后比较两端的长度,并先对短的序列进行排序(目的是先结束这些栈以释放空间),可以将最大深度降回到O(㏒n)级别。

算法稳定性

快速排序并不是稳定的。这是因为我们无法保证相等的数据按顺序被扫描到和按顺序存放。

算法适用场景

快速排序在大多数情况下都是适用的,尤其在数据量大的时候性能优越性更加明显。但是在必要的时候,需要考虑下优化以提高其在最坏情况下的性能。

快排的非递归实现

按照通常的理论,我们知道递归算法一般比较直观自然,容易理解和书写;而非递归算法一般更为晦涩,但是性能比递归算法更优良,因为其省去了大量的函数调用开销。快速排序肯定有非递归实现的版本,例如这篇博客。有趣的是,这位作者认为快速排序的非递归实现比递归还要慢,并做出了分析。而下面的这位博主则写了另一篇博文,证明“非递归算法总要比响应(应为"相应"--本博作者注)的递归算法速度快”,并认为前面的现象是由于Windows 下的STL效率比较低。

快速排序的Java非递归实现当然有,通常都是用自己实现的栈来模拟递归操作(实际上,前面两位使用C++的同学也是如此做的)。但是我并不认为它们比递归的方式有极大的性能提升,反而丢失了可读性,晦涩难懂。因此,我个人不提倡使用非递归方式。

4、动态规划

动态规划其实和分治策略是类似的,也是将一个原问题分解为若干个规模较小的子问题,递归的求解这些子问题,然后合并子问题的解得到原问题的解。区别在于这些子问题会有重叠,一个子问题在求解后,可能会再次求解,于是我们想到将这些子问题的解存储起来,当下次再次求解这个子问题时,直接拿过来就是。其实就是说,动态规划所解决的问题是分治策略所解决问题的一个子集,只是这个子集更适合用动态规划来解决从而得到更小的运行时间。即用动态规划能解决的问题分治策略肯定能解决,只是运行时间长了。因此,分治策略一般用来解决子问题相互对立的问题,称为标准分治,而动态规划用来解决子问题重叠的问题。

动态规划一般由两种方法来实现,一种为自顶向下的备忘录方式,用递归实现,一种为自底向上的方式,用迭代实现。

5、堆排序

堆(二叉堆)可以视为一棵完全的二叉树,完全二叉树的一个“优秀”的性质是,除了最底层之外,每一层都是满的,这使得堆可以利用数组来表示(普通的一般的二叉树通常用链表作为基本容器表示),每一个结点对应数组中的一个元素。

如下图,是一个堆和数组的相互关系

heap-and-array.png堆和数组的相互关系

对于给定的某个结点的下标 i,可以很容易的计算出这个结点的父结点、孩子结点的下标:

Parent(i) = floor(i/2),i 的父节点下标

Left(i) = 2i,i 的左子节点下标

Right(i) = 2i + 1,i 的右子节点下标

heap-and-array-parent-children.png

package com.genesis.arithmetic;

import java.util.Arrays;

public class HeapSort {

private int[] arr;

public HeapSort(int[] arr){

this.arr = arr;

}

/**

* 堆排序的主要入口方法,共两步。

*/

public void sort(){

/*

* 第一步:将数组堆化

* beginIndex = 第一个非叶子节点。

* 从第一个非叶子节点开始即可。无需从最后一个叶子节点开始。

* 叶子节点可以看作已符合堆要求的节点,根节点就是它自己且自己以下值为最大。

*/

int len = arr.length - 1;

int beginIndex = (len - 1) >> 1;

for(int i = beginIndex; i >= 0; i--){

maxHeapify(i, len);

}

/*

* 第二步:对堆化数据排序

* 每次都是移出最顶层的根节点A[0],与最尾部节点位置调换,同时遍历长度 - 1。

* 然后从新整理被换到根节点的末尾元素,使其符合堆的特性。

* 直至未排序的堆长度为 0。

*/

for(int i = len; i > 0; i--){

swap(0, i);

maxHeapify(0, i - 1);

}

}

private void swap(int i,int j){

int temp = arr[i];

arr[i] = arr[j];

arr[j] = temp;

}

/**

* 调整索引为 index 处的数据,使其符合堆的特性。

*

* @param index 需要堆化处理的数据的索引

* @param len 未排序的堆(数组)的长度

*/

private void maxHeapify(int index,int len){

int li = (index << 1) + 1; // 左子节点索引

int ri = li + 1; // 右子节点索引

int cMax = li; // 子节点值最大索引,默认左子节点。

if(li > len) return; // 左子节点索引超出计算范围,直接返回。

if(ri <= len && arr[ri] > arr[li]) // 先判断左右子节点,哪个较大。

cMax = ri;

if(arr[cMax] > arr[index]){

swap(cMax, index); // 如果父节点被子节点调换,

maxHeapify(cMax, len); // 则需要继续判断换下后的父节点是否符合堆的特性。

}

}

/**

* 测试用例

*

* 输出:

* [0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9]

*/

public static void main(String[] args) {

// int[] arr = new int[]{3,5,3,0,8,6,1,5,8,6,2,4,9,4,7,0,1,8,9,7,3,1,2,5,9,7,4,0,2,6};

int[] arr = new int[]{16, 7, 3, 20, 17, 8};

new HeapSort(arr).sort();

System.out.println(Arrays.toString(arr));

}

}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/554538.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java dos 菜单栏_学习java之电脑的常用快捷键和DOS窗口下的常用命令

学习java之电脑的常用快捷键和DOS窗口下的常用命令电脑一些常用的快捷键win快捷键&#xff1a;单独按Windows&#xff1a;显示或隐藏 “开始”功能表WindowsBREAK&#xff1a;显示“系统属性” 对话框WindowsD&#xff1a;显示桌面或恢复桌面WindowsM&#xff1a;最小化所有窗口…

java queue 最大值_[剑指offer题解]队列的最大值/滑动窗口的最大值

前言众所周知&#xff0c;《剑指offer》是一本“好书”。为什么这么说&#xff1f;因为在技术面试中&#xff0c;它里面罗列的算法题在面试中出现的频率是非常非常高的。有多高&#xff0c;以我目前不多的面试来看&#xff0c;在所有遇到的面试算法题中&#xff0c;出现原题的概…

java jvm 加载类的顺序_由经典面试题看java中类的加载机制

原标题&#xff1a;由经典面试题看java中类的加载机制1.概述类加载是Java程序运行的第一步&#xff0c;研究类的加载有助于了解JVM执行过程&#xff0c;并指导开发者采取更有效的措施配合程序执行&#xff0c;对理解java虚拟机的连接模型和java语言的动态性都有很大帮助。由于J…

php数据库可转java数据库,php转java 系列2 Spring boo 链接数据库jdbc

php转java 系列2 Spring boo 链接数据库jdbcJDBC首先创建一个新项目&#xff0c;在创建项目时要注意导入依赖&#xff0c;在项目创建成功后就会看到在 pom.xml 文件中找到&#xff0c;但是如果在创建项目的时候没有导入&#xff0c;就要在pom.xml中手动的添加依赖&#xff1b;o…

php连接excel表格数据类型,TP5 中使用PHPExcel 导出导入数据到excel表格

将下载好的PHPExcel文件夹放置在框架中的Vendor目录下&#xff0c;注意目录的结构&#xff0c;按照我的代码来基本上直接引用就能使用&#xff0c;如下图&#xff1a;导出数据代码如下//导出数据表 开始public function product(){$listdb(product)->select();vendor("…

oracle导致的负载高,Oracle 11g 数据库服务器CPU、IO负载高的故障排除流程

使用top查看负载高的进程~ [OSMSRDB]$ toptop - 11:02:43 up 12 days, 17:15, 4 users, load average: 1.50, 0.93, 0.36Tasks: 363 total, 1 running, 362 sleeping, 0 stopped, 0 zombieCpu(s): 1.2%us, 0.3%sy, 0.0%ni, 68.4%id,30.1%wa, 0.0%hi, 0.0%si, 0.0…

清理linux 服务器的命令行,使用Linux上的Magic SysRq键修复冻结的X服务器,清理重新启动并运行其他低级命令 | MOS86...

在Linux中&#xff0c;魔术SysRq键可以直接发送命令到Linux内核。您可以使用它来恢复冻结或干净地重新启动系统&#xff0c;即使没有任何反应。魔术SysRq键是作为Linux键盘驱动程序的一部分实现的 - 只要Linux内核仍在运行&#xff0c;它将会工作。只有内核崩溃才能禁用此组合键…

上海电力大学c语言程序设计章测试,上海电力学院c语言报告:实验8 指针.doc

上海电力学院c语言报告&#xff1a;实验8 指针.doc下载提示(请认真阅读)1.请仔细阅读文档&#xff0c;确保文档完整性&#xff0c;对于不预览、不比对内容而直接下载带来的问题本站不予受理。2.下载的文档&#xff0c;不会出现我们的网址水印。3、该文档所得收入(下载内容预览)…

python c语言 数据类型转换,Python类型转换,数据类型转换函数大全

虽然 Python 是弱类型编程语言&#xff0c;不需要像 Java 或 C 语言那样还要在使用变量前声明变量的类型&#xff0c;但在一些特定场景中&#xff0c;仍然需要用到类型转换。比如说&#xff0c;我们想通过使用 print() 函数输出信息“您的身高&#xff1a;”以及浮点类型 heigh…

新闻发布系统C语言,资讯 | 助力期末之c语言专场圆满结束!

原标题&#xff1a;资讯 | "助力期末"之c语言专场圆满结束&#xff01;大一生活如白驹过隙&#xff0c;一转眼便来到了期末&#xff0c;相信很多同学都对c语言期末考很在意&#xff0c;但是又不知道考点在哪里&#xff0c;感觉很多知识点都还不太清楚。确实&#xff…

android创建空文件,ADT 更新 eclipse srclayout 文件夹创建时候为空

今天是14-3-9&#xff0c;发现SDK Manager有更新&#xff0c;手贱点了一下。。。然后新建项目src和layout文件夹就成空的了。。。虽然可以手动建立内容&#xff0c;但是感觉很不爽&#xff0c;到处找解决方法&#xff0c;最后在贴吧找到了&#xff0c;内容链接到了eoe&#xff…

html语言循环语句,Scala @功能/语言的HTML文档 - for循环,如果语句等

我正在一个控制器的视图(HTML页面)中&#xff0c;我试图匹配复选框中的“选中”值的列表中。Scala 功能/语言的HTML文档 - for循环&#xff0c;如果语句等我从模型中抓取选定的复选框值(列表A)&#xff0c;将它们在控制器中抓取并发送到视图。我还从模型中获取复选框选择/选项/…

百度自动推送html5,百度暂停 JS 代码自动推送功能,代码是否需要删除?

本月上旬末的时候百度站长平台就推送了一则关于&#xff1a;搜索资源平台自动推送暂停使用通知。说到由于升级维护&#xff0c;搜索资源平台链接提交-自动提交-自动推送(JS 代码推送)功能暂时停止使用。并且会在恢复使用时再次通知。由于这条通知是百度通过搜索资源平台发送的站…

html中表格布局还是div布局,Table布局 VS Div+CSS布局,选哪个?

Table布局 和 DivCSS布局的争论&#xff0c;过了7-8年了&#xff0c;今天看到一篇文章《为什么我们不建议用Table布局》&#xff0c;又想发表下自己的见解&#xff0c;供初学《为什么我们不建议用Table布局》节选&#xff1a;Table要比其它html标记占更多的字节。(延迟下载时间…

计算机桌面锁写快捷,电脑如何锁屏幕 锁屏快捷键是什么

锁屏幕是用户们日常生活中的必备玩法,很多玩家们却不知道游戏中应该如何锁屏幕,那么具体应该如何锁屏幕呢?下面小编带大家一起来看下电脑如何锁屏幕吧!电脑怎么锁屏幕?电脑如何锁屏幕?1、首先自然是需要为电脑创建一个密码。点击开始按钮&#xff0c;然后找到控制面板&#…

计算机在军事的应用论文,信息技术论文3000字【军事信息技术论文】

信息技术是20世纪科学技术发展最重大&#xff0c;最辉煌的成就之一&#xff0c;下面是小编为大家精心推荐的军事信息技术论文&#xff0c;希望能够对您有所帮助。军事信息技术论文篇一漫谈信息技术【摘 要】本文研究了信息技术的发展历程&#xff0c;阐述了信息技术的涵义及研究…

计算机组成原理环境配置,计算机组成原理课程设计

《计算机组成原理》实验大纲适用专业&#xff1a;计算机科学与技术(本科)、网络工程(本科)实验学时&#xff1a;16学时学分&#xff1a;0.5学分一、实验课程目的与要求(1) 进一步融会贯通教材内容&#xff0c;掌握计算机各功能模块的工作原理、相互联系和来龙去脉&#xff0c;完…

计算机学业水平测试初中生操作题,初中学业水平考试信息技术考试操作题常见题型及作答方法...

初中信息技术考试操作题常见题型及作答方法牛角寨乡初级中学信息技术考试已经有两年了&#xff0c;通过这两年的考试情况来看&#xff0c;我认为&#xff0c;要想在信息技术考试中取得一个好的成绩&#xff0c;操作题是一个很重要的环节&#xff0c;从这两年的情况来看&#xf…

什么不是预防计算机病毒的方法,预防计算机病毒的方法是什么

对于电脑安全防护&#xff0c;最好的方法是将病毒扼杀在摇篮中&#xff0c;这就需要积极预防病毒&#xff0c;提前发现并查杀病毒。下面就让学习啦小编给大家说说预防计算机病毒的方法是什么吧。预防计算机病毒的方法第一、及时为Windows打补丁为Windows打补丁是很重要的&#…

手机在线测试黄疸软件,在家怎么用手机测黄疸

文章导读新生婴儿遭遇的问题中最普遍的便是黄疸&#xff0c;新生儿黄疸绝大多数都归属于生理性的&#xff0c;不需要独特医治&#xff0c;还可以在医师具体指导下服食用药治疗&#xff0c;在平常一定要多给宝宝晒太阳。有的病人出世以后黄疸指数不比较严重&#xff0c;可是住院…