java查找排序的算法_java数据结构(二)常用简单的查找、排序算法

java数据结构(二)常用简单的查找、排序算法

理解了Java数据结构,还必须要掌握一些常见的基本算法。 理解算法之前必须要先理解的几个算法的概念:

空间复杂度:一句来理解就是,此算法在规模为n的情况下额外消耗的储存空间。

时间复杂度:一句来理解就是,此算法在规模为n的情况下,一个算法中的语句执行次数称为语句频度或时间频度。

稳定性:主要是来描述算法,每次执行完,得到的结果都是一样的,但是可以不同的顺序输入,可能消耗的时间复杂度和空间复杂度不一样。

一、二分查找算法

二分查找又称折半查找,优点是比较次数少,查找速度快,平均性能好,占用系统内存较少;其缺点是要求待查表为有序表,且插入删除困难。这个是基础,最简单的查找算法了。

public static voidmain(String[] args) {int srcArray[] = {3, 5, 11, 17, 21, 23, 28, 30, 32, 50, 64, 78, 81, 95, 101};

System.out.println(binSearch(srcArray,95));

}/*** 二分查找针对的是有序的数组

* 二分查找普通循环实现

*

*@paramsrcArray 有序数组

*@paramkey 查找元素

*@return

*/

public static int binSearch(int srcArray[], intkey) {if (srcArray.length <= 0) {return -1;

}//取出首尾判断key是否在指定的srcArray中

int first = srcArray[0];int end = srcArray[srcArray.length - 1];if (key <= first || key >=end) {return -1;

}int mid = srcArray.length / 2;if (key ==srcArray[mid]) {returnmid;

}//二分核心逻辑

int startIndex = 0;int endIndex = srcArray.length - 1;while (startIndex <=endIndex) {

mid= (endIndex - startIndex) / 2 +startIndex;if (key

endIndex= mid - 1;

}else if (key >srcArray[mid]) {

startIndex= mid + 1;

}else{returnmid;

}

}return -1;

}

二分查找算法如果没有用到递归方法的话,只会影响CPU。对内存模型来说影响不大。时间复杂度log2n,2的开方。空间复杂度是2。一定要牢记这个算法。应用的地方也是非常广泛,平衡树里面大量采用。

二、递归算法

递归简单理解就是方法自身调用自身。

48304ba5e6f9fe08f3fa1abda7d326ab.png

public static void main(String[] args) {

int srcArray[] = {3,5,11,17,21,23,28,30,32,50,64,78,81,95,101};

System.out.println(binSearch(srcArray, 0,15,28));

}

/**

* 二分查找递归实现

*

* @param srcArray 有序数组

* @param start 数组低地址下标

* @param end 数组高地址下标

* @param key 查找元素

* @return 查找元素不存在返回-1

*/

public static int binSearch(int srcArray[], int start, int end, int key) {

int mid = (end - start) / 2 + start;

if (srcArray[mid] == key) {

return mid;

}

if (start >= end) {

return -1;

} else if (key > srcArray[mid]) {

return binSearch(srcArray, mid + 1, end, key);

} else if (key < srcArray[mid]) {

return binSearch(srcArray, start, mid - 1, key);

}

return -1;

}

48304ba5e6f9fe08f3fa1abda7d326ab.png

递归几乎会经常用到,需要注意的一点是:递归不光影响的CPU。JVM里面的线程栈空间也会变大。所以当递归的调用链长的时候需要-Xss设置线程栈的大小。

三、八大排序算法

一、直接插入排序(Insertion Sort)

二、希尔排序(Shell Sort)

三、选择排序(Selection Sort)

四、堆排序(Heap Sort)

五、冒泡排序(Bubble Sort)

六、快速排序(Quick Sort)

七、归并排序(Merging Sort)

八、基数排序(Radix Sort)

1:冒泡排序

基本思想:

冒泡排序(Bubble Sort)是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。

51ea2d095d787a6777019cd20463d5a2.gif

以下是冒泡排序算法复杂度:

平均时间复杂度最好情况最坏情况空间复杂度

O(n²)

O(n)

O(n²)

O(1)

冒泡排序是最容易实现的排序, 最坏的情况是每次都需要交换, 共需遍历并交换将近n²/2次, 时间复杂度为O(n²). 最佳的情况是内循环遍历一次后发现排序是对的, 因此退出循环, 时间复杂度为O(n). 平均来讲, 时间复杂度为O(n²). 由于冒泡排序中只有缓存的temp变量需要内存空间, 因此空间复杂度为常量O(1).

由于冒泡排序只在相邻元素大小不符合要求时才调换他们的位置, 它并不改变相同元素之间的相对顺序, 因此它是稳定的排序算法.

26c9476e2f8bc77a3f9351b0ceb65224.png

48304ba5e6f9fe08f3fa1abda7d326ab.png

/**

* 冒泡排序普通版

* @param arr 待排序的数组

* @param len 数组中的元素个数

*/

public static void sort(Integer [] arr, int len){

if(len <= 1){

return;

}

// 数组中有len个元素,进行len次冒泡

for (int i = 0; i < len; i++) {

for (int j = 0; j < len - i - 1; j++) {

if(arr[j] > arr[j + 1]){

int tmp = arr[j];

arr[j] = arr[j + 1];

arr[j + 1] = tmp;

}

}

}

}

48304ba5e6f9fe08f3fa1abda7d326ab.png

如果说在一次冒泡中,没有发生相邻元素的交换,那说明待排序序列已经有序了,不管后面还剩下多少次冒泡,我们都不需要再进行冒泡下去了。这样是不是就减少冒泡的次数了呢

改良版:

48304ba5e6f9fe08f3fa1abda7d326ab.png

/**

* 冒泡排序改良版

* @param arr 待排序的数组

* @param len 数组中的元素个数

*/

public static void sort(Integer [] arr, int len){

if(len <= 1){

return;

}

// 优化标识

// 如果在某一次的冒泡过程中,没有位置交换说明已经排好序,直接break

boolean flag = false;

// 数组中有len个元素,进行len次冒泡

for (int i = 0; i < len; i++) {

// 如果有位置交换就重置标识为false

flag = false;

for (int j = 0; j < len - i - 1; j++) {

if(arr[j] > arr[j + 1]){

int tmp = arr[j];

arr[j] = arr[j + 1];

arr[j + 1] = tmp;

flag = true;

}

}

// 每次冒泡结束检查是否发生了数据交换

// 如果没有发生数据交换,说明序列已经有序,不需要再继续冒泡了

System.out.println("第【" + (i + 1) + "】次冒泡");

if(!flag){

break;

}

}

}

48304ba5e6f9fe08f3fa1abda7d326ab.png

2:快速排序

f7010b0ae4ac4232e55a0add5025b8ee.gif

快速排序使用分治策略来把一个序列(list)分为两个子序列(sub-lists)。步骤为:

①. 从数列中挑出一个元素,称为”基准”(pivot)。

②. 重新排序数列,所有比基准值小的元素摆放在基准前面,所有比基准值大的元素摆在基准后面(相同的数可以到任一边)。在这个分区结束之后,该基准就处于数列的中间位置。这个称为分区(partition)操作。

③. 递归地(recursively)把小于基准值元素的子数列和大于基准值元素的子数列排序。

递归到最底部时,数列的大小是零或一,也就是已经排序好了。这个算法一定会结束,因为在每次的迭代(iteration)中,它至少会把一个元素摆到它最后的位置去。

代码实现:

用伪代码描述如下:

①. i = L; j = R; 将基准数挖出形成第一个坑a[i]。

②.j--,由后向前找比它小的数,找到后挖出此数填前一个坑a[i]中。

③.i++,由前向后找比它大的数,找到后也挖出此数填到前一个坑a[j]中。

④.再重复执行②,③二步,直到i==j,将基准数填入a[i]中。

快速排序采用“分而治之、各个击破”的观念,此为原地(In-place)分区版本。

d741c327387c50020b8d5b2f3399985b.png

48304ba5e6f9fe08f3fa1abda7d326ab.png

/**

* 快速排序(递归)

*

* ①. 从数列中挑出一个元素,称为"基准"(pivot)。

* ②. 重新排序数列,所有比基准值小的元素摆放在基准前面,所有比基准值大的元素摆在基准后面(相同的数可以到任一边)。在这个分区结束之后,该基准就处于数列的中间位置。这个称为分区(partition)操作。

* ③. 递归地(recursively)把小于基准值元素的子数列和大于基准值元素的子数列排序。

* @param arr 待排序数组

* @param low 左边界

* @param high 右边界

*/

public static void quickSort(int[] arr, int low, int high){

if(arr.length <= 0) return;

if(low >= high) return;

int left = low;

int right = high;

int temp = arr[left]; //挖坑1:保存基准的值

while (left < right){

while(left < right && arr[right] >= temp){ //坑2:从后向前找到比基准小的元素,插入到基准位置坑1中

right--;

}

arr[left] = arr[right];

while(left < right && arr[left] <= temp){ //坑3:从前往后找到比基准大的元素,放到刚才挖的坑2中

left++;

}

arr[right] = arr[left];

}

arr[left] = temp; //基准值填补到坑3中,准备分治递归快排

System.out.println("Sorting: " + Arrays.toString(arr));

quickSort(arr, low, left-1);

quickSort(arr, left+1, high);

}

48304ba5e6f9fe08f3fa1abda7d326ab.png

以下是快速排序算法复杂度:

平均时间复杂度最好情况最坏情况空间复杂度

O(nlog₂n)

O(nlog₂n)

O(n²)

O(1)(原地分区递归版)

快速排序排序效率非常高。 虽然它运行最糟糕时将达到O(n²)的时间复杂度, 但通常平均来看, 它的时间复杂为O(nlogn), 比同样为O(nlogn)时间复杂度的归并排序还要快. 快速排序似乎更偏爱乱序的数列, 越是乱序的数列, 它相比其他排序而言, 相对效率更高.

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值