目录:
一、常见的排序
1、简单的桶排序
2、冒泡排序
3、选择排序
4、 插入排序
5、希尔排序
6、快速排序
7、堆排序
二、二分查找和折半插入
1、二分查找
2、折半插入
三、递归算法
1、案例1:遍历文件夹下的所有指定后缀名的文件,返回List集合
2、案例2:获取json 中所有的key和value,返回map
1、简单的桶排序:
假设有5个学生,得分在0--10之间,现在分数分别为5 3 5 2 8 。。
想办法使得从大到小或者从小到大排序输出?
public static void main(String[] args){
int[] arr = {5,3,5,2,8};
//new 一个数组,每个元素代表一种成绩,,11个元素代表0-10
int[] duiArr = new int[11];//初始化为0,即没有成绩,,一次为1,两次为2....
for (int i = 0;i
运算结果:
2、冒泡排序
时间复杂度:o(n^2)
在上面简单版的堆排序中,有很多问题,比如:太浪费空间,如果排序的元素范围很大,就需要0-很大这么多个桶来存储!再比如:如果比较的是小数,也不行!冒泡排序则不涉及这么多问题
基本思想:每次比较相邻的元素,如果它们的顺序错误,就把它们交换过来。。(毕竟顺序要么a-b要么b-a)每一趟的末尾元素一定是一定是最小的或者最大的。
算法步骤:
1)比较相邻的元素。如果第一个比第二个大,就交换他们两个。
2)对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。
3)针对所有的元素重复以上的步骤,除了最后一个。
4)持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
假如,我们要从大到小排列35 12 99 18 76个数。。基于此思想:
第一趟:
第一次:比较第一位和第二位的数:35和12,35 > 12,所以交换,得到
12 35 99 18 76
第二次:比较第二位和第三位的数: 35和99,35 < 99,所以不交换,得到
12 35 99 18 76
第三次: 比较第三位和第四位的数:99和18,99 > 18,所以交换,得到
12 35 18 99 76
第四次:比较第四位和第五位的数:99和76,99 > 76,所以交换,得到
12 35 18 76 99
可以发现,最大的数99,已经排到最后了!!到这里,就已经将一个数字的位置确定了,我们称之为归位。
也称之为一趟。。
第二趟:也是从第一个数开始,将最大的数往后排!不难推断,5个数的排列,需要遍历(5-1)趟。。每一趟需要从第一个数比较到最后一个尚未归位的数。根据你设置的换位条件。
代码:
/**
* 冒泡排序
*
* @param arr 数组(乱序)
* @return
*/
public static int[] bubbleSort(int[] arr) {
int n = arr.length;
for (int i = 0; i < n - 1; i++) { //0 1 2 3,四趟
for (int j = 0; j < n - i - 1; j++) { //i=0时,比较4次;i=1时,比较3次.....i=3时,比较1次
if (arr[j] > arr[j + 1]) {
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
System.out.println(Arrays.toString(arr));
}
return arr;
}
测试:
public static void main(String[] args) {
int[] arr = {35, 12, 99, 18, 76};
System.out.println("排序中:");
int[] bubbleSort = bubbleSort(arr);
System.out.println("排序后:");
System.out.println(Arrays.toString(bubbleSort));
}
运行结果:
不难看出,冒泡循环嵌套循环,有人尝试改进,很可惜冒泡除了名字以及有趣的现象外,效率并不怎么理想~
3、选择排序
时间复杂度:O(N^2)
在乱序数组中,假设第一位数最小,依次让后面的数与之比较,若遇到比它小/大的数就交换位置,一趟下来第一个数就是序列中最小/最大的数,然后从第二个数开始重复操作。
算法步骤:
1)首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置
2)再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。
3)重复第二步,直到所有元素均排序完毕。
代码:
/**
* 选择排序算法
*
* @param arr 数组(乱序)
* @return
*/
public static int[] selectSort(int[] arr) {
int length = arr.length;
for (int i = 0; i < length; i++) {
int j = i + 1;
for (; j < length; j++) {
if (arr[j] < arr[i]) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
}
return arr;
}
测试:
public static void main(String[] args) {
int[] arr = {9, 1, 12, 5, 3, 11, 7, 8};
System.out.println(Arrays.toString(selectSort(arr)));
}
运行结果:
接下来,介绍最常用的排序方法:快速排序。。
4、插入排序
时间复杂度:o(n^2)
算法步骤:
1)将第一待排序序列第一个元素看做一个有序序列,把第二个元素到最后一个元素当成是未排序序列。
2)从头到尾依次扫描未排序序列,将扫描到的每个元素插入有序序列的适当位置。(如果待插入的元素与有序序列中的某个元素相等,则将待插入元素插入到相等元素的后面。)
代码:
/**
* 插入排序
*
* @param arr
* @return
*/
public static int[] insertSort(int[] arr) {
int length = arr.length;
for (int i = 1; i < length; i++) {
int temp = arr[i];
int j = i - 1;
for (; j >= 0 && arr[j] > temp; j--) { //j >=0 是为了排序插入的第一个元素,如果如果写>0就会掠过第一个元素
if (arr[j] > temp) {
}
arr[j + 1] = arr[j];
}
arr[j + 1] = temp;
}
return arr;
}
测试:
public static void main(String[] args) {
int[] arr = {9, 1, 12, 5, 3, 11, 7, 8};
System.out.println(Arrays.toString(insertSort(arr)));
}
运行结果:
5、希尔排序
时间复杂度:O(n^(1+§))排序,§是介于0和1之间的常数,下界是n*log2n
希尔算法在最坏的情况下和平均情况下执行效率相差不是很多,与此同时快速排序在最坏的情况下执行的效率会非常差。
希尔排序是基于插入排序的以下两点性质而提出改进方法的:1、插入排序在对几乎已经排好序的数据操作时,效率高,即可以达到线性排序的效率。2、但插入排序一般来说是低效的,因为插入排序每次只能将数据移动一位。 本质上讲,希尔排序算法是直接插入排序算法的一种改进,减少了其复制的次数,速度要快很多。 原因是,当n值很大时数据项每一趟排序需要移动的个数很少,但数据项的距离很长。当n值减小时每一趟需要移动的数据增多,此时已经接近于它们排序后的最终位置。 正是这两种情况的结合才使希尔排序效率比插入排序高很多。
先取一个小于n的整数d1作为第一个增量,把全部记录分组。所有距离为d1的倍数的记录放在同一个组中。先在各组内进行直接插入排序;然后,取第二个增量d2 算法步骤: 1)选择一个增量序列t1,t2,…,tk,其中ti>tj,tk=1; 2)按增量序列个数k,对序列进行k 趟排序; 3)每趟排序,根据对应的增量ti,将待排序列分割成若干长度为m 的子序列,分别对各子表进行直接插入排序。仅增量因子为1 时,整个序列作为一个表来处理,表长度即为整个序列的长度。 代码: 测试: 运行结果: 6、快速排序(效率不错,但是不稳定) 时间复杂度:平均为O(nlogn),最好为O(nlogn),最差为O(logn2) 算法步骤: 1 )从数列中挑出一个元素,称为 “基准”(pivot), 2 )重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作。 3) 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。递归的最底部情形,是数列的大小是零或一,也就是永远都已经被排序好了。虽然一直递归下去,但是这个算法总会退出,因为在每次的迭代(iteration)中,它至少会把一个元素摆到它最后的位置去。 这里我们使用数组的方式(即下标移动),至于链表方式,有机会补充。假如,我们要对6 1 2 7 9 3 4 5 10 8 进行从大到小排序。首先在这个序列中选择一个基准数(做参照用),为了解释方便,我们娶一个最左边的数:6,作为基准数把。接下来,将比6小的放在6的右边,将比6大的放在6的左边。怎么做,才能效率高呢? 方法其实很简单,从数组两端开始,先从右往左找一个比6大的数,从左往右找一个比6小的数,然后这两个数交换。。可以用两个变量i,j分别指向数组的最左边和最右边,我们为它们分别起个名字“哨兵i”和“哨兵j”,刚开始,让哨兵i指向数组最左边(i=0)的位置,指向的值为6,哨兵j指向数组最右边的位置(j=n-1),指向的值为8。。现在,哨兵j从右往左找比6大的数(注意,必须是哨兵j先走,即j--,这是因为我们的基准值设在左边,原因自己体会),满足要求后,停下,,接下来,哨兵i从右往左找比6小的数,满足要求,停下来,,此时两者的值进行交换。。当哨兵j与哨兵i相遇时,一轮结束,,什么情况下相遇呢?比如哨兵j一直j--,指到了3,然后停下,,哨兵i找啊找,找到3后,还是没有找到比6大的,此时,一轮探测就结束了,,需要将最左侧的6与3互换位置。。。 第一次交换7 5 交换,得到: 6 1 2 5 9 3 4 7 10 8 第二次9 4 交换,得到: 6 1 2 5 4 3 9 7 10 8 第三次:没有第三次了,,哨兵j指向3,但是哨兵i已经没有指向了,,会与哨兵j相撞!之后,将6 和 3 互换,得到: 3 1 2 5 4 6 9 7 10 8 然后,在6的左侧,取3为基准,重复上面的操作,,在6的右侧,以8为基准,重复上面的操作。。细心的同学可能已经发现,快速排序的每一轮处理,其实就是将这一轮的基准值归位,直到所有的值都归位为止,排序就结束了! 代码: 测试: 运行结果: 7、堆排序 时间复杂度:平均时间复杂度为Ο(nlogn) 算法步骤: 1)创建一个堆H[0..n-1] 2)把堆首(最大值)和堆尾互换 3)把堆的尺寸缩小1,并调用shift_down(0),目的是把新的数组顶端数据调整到相应位置 4) 重复步骤2,直到堆的尺寸为1 这里的堆是一种数据结构,它可以被视为一种完全二叉树,即树里面除了最后一层其他层都是填满的。值得注意的是,这里的左子节点不需要一定小于右子节点,因为我们只需要保证子节点的父节点一直是三者中的最大值就可以了。在这个类似完全二叉树的结构里,里面每个节点的子女和双亲节点的序号都可以根据当前节点的序号直接求出。Parent(i)=i/2;Left(i)=2*i;Right(i)=2*i+1堆排序里有最大堆和最小堆的概念,即根节点是最大值或者是最小值。。如果想要的是升序那就使用最大堆,反之使用最小堆。 这里分析最大堆的方式,关键地方为两部分,一是构建最大堆、二是进行堆排序。 (1)构建最大堆 现在有一个数组A,大小是n,假设其中元素按照完全二叉树的方式排列。 我们定义如下操作MaxNode(i):将以i位置节点为根的子树改造成最大堆。对于每个节点i,如果他比某个子女节点小,则将他与子女节点中最大的那个互换位置,然后在相应的子女节点位置重复操作,直到到达堆的叶子节点或者考察的位置比子女节点的值都要大为止。由此可知我们构造最大堆buildmaxheap的过程就是在每个非叶子节点上调用MaxNode过程,依次到树的根部,此时其左右子树都是最大堆,即完成了最大堆的构造。 (2)堆排序: 从根节点开始操作,因为在构造最大堆完成后根节点是这个数组中最大的元素,因此我们将其于数组中最后一个元素对换(排序后,最大元素应该在最后)将heapsize减1,然后再在根节点出调用maxify过程将新的堆重新最大堆化。依次循环n,我们每次都能将现有堆中最大的元素放到堆末尾。最后就完成了整个排序过程。 堆排序时的图片讲解见:https://blog.csdn.net/u011068702/article/details/52767187 代码: 测试: 运行结果: (1)直接打印数组内容(分层打印) (2)执行初始化(构造)最大堆 (3)初始化最大堆后,进行堆排序(这里省略初始化最大堆的log显示) 1、二分查找 二分查找又称折半查找,它是一种效率较高的查找方法。但是,折半查找要求线性表必须采用顺序存储结构,而且表中元素按关键字有序排列。算法复杂度为:最好的情况是当匹配的位置刚好是二分位置即,O(log2n),最坏的情况是O(n)。 假如一个递增的int数组,现在想要确定v alue在数组中的位置,首先获取middle下表,再获取arr[middle]的值,如果value小于中间值,则在前半部分寻找;如果value大雨中间值,则在后半部分寻找。 (1)while循环方式: (2)递归嵌套方式: (3)测试: (4)运行结果: 2、折半插入 时间复杂度:和二分查找一样,最好的情况是O(log2n),最坏的情况是O(n)。 将i 一个乱序的int数组进行排序:把n个待排序的元素看成一个有序表和一个无序表,开始时有序表中只有一个元素,无序表中有n-1个元素;排序过程即每次从无序表中取出第一个元素,将它插入到有序表中,使之成为新的有序表,重复n-1次完成整个排序过程。与直接插入算法的区别在于:在有序表中寻找待排序数据的正确位置时,使用了折半查找/二分查找。而传统的直接插入排序,则是移动,即必须从最后一个记录开始,向后移动一位,再移动倒数第2位,直到要插入的位置的记录移后一位。 (1)代码: (2)测试: (3)运行结果: 递归算法的概念就不多说了,虽然效率很低,但是挺常用的 案例1、遍历文件夹下的所有指定后缀名的文件,返回List集合 案例2、获取json 中所有的key和value,返回map /**
* 希尔排序
*
* @param arr 数组(乱序)
* @return
*/
public static int[] hillSort(int[] arr) {
int d = arr.length;//d为增量
while (true) {
d = d / 2;
for (int x = 0; x < d; x++) {
for (int i = x + d; i < arr.length; i = i + d) {
int temp = arr[i];
int j;
for (j = i - d; j >= 0 && arr[j] > temp; j = j - d) {
arr[j + d] = arr[j];
}
arr[j + d] = temp;
}
}
System.out.println(Arrays.toString(arr));
if (d == 1) {
break;
}
}
return arr;
}
public static void main(String[] args) {
int[] arr = {9, 1, 12, 5, 3, 11, 7, 8};
System.out.println("排序中:");
int[] hillSort = hillSort(arr);
System.out.println("排序后:");
System.out.println(Arrays.toString(hillSort));
}
/**
* 快速排序
*
* @param arr 数组(乱序)
* @param left 数组左端下标
* @param right 数组右端下标
* @return
*/
public static int[] quickSort(int[] arr, int left, int right) {
int i, j, t, temp;
if (left > right) {
return arr;
}
System.out.println(Arrays.toString(arr));
temp = arr[left];//temp即基准数
i = left;
j = right;
while (i != j) {
//顺序很重要,先从右往左找
while (arr[j] >= temp && i < j) {
j--;
}
while (arr[i] <= temp && i < j) {
i++;
}
if (i < j) {//哨兵i与j没有相遇时
t = arr[i];
arr[i] = arr[j];
arr[j] = t;
}
}
//基数归位
// temp = arr[left];
arr[left] = arr[i];
arr[i] = temp;
//递归
quickSort(arr, left, i - 1);
quickSort(arr, i + 1, right);
return arr;
}
public static void main(String[] args) {
int[] arr = {6, 1, 2, 7, 9, 3, 4, 5, 10, 8};
System.out.println("排序中:");
int[] quickSort = quickSort(arr, 0, 9);
System.out.println("排序后:");
System.out.println(Arrays.toString(quickSort));
}
首先我们知道最大堆的每个子树都符合最大堆的性质(根节点值大于所有子节点)。同时序号为[(n/2+1),n]的元素都是叶子节点,由于叶子节点没有子节点,所以叶子节点一定是里三者之间的最大值(已满足最大堆性质)。因此我们构建最大堆的操作就在序号为[1,n/2]的元素内进行。package me.ele;
import java.util.Arrays;
/**
* @author LZJ
* @create 2018-09-29 12:58
**/
public class MaxHeapSort {
int[] heap;
private int heapsize;
public MaxHeapSort(int[] array) {
this.heap = array;
this.heapsize = heap.length;
}
/**
* 构造最大堆
*/
public void BuildMaxHeap() {
for (int i = heapsize / 2; i > 0; i--) {
System.out.println("------------");
System.out.println("非子节点位置:" + (i));
//从最后一个非叶子节点开始,依次向上将当前子树最大堆化
MaxNode(i);
}
}
/**
* 堆排序
*/
public void HeapSort() {
for (int i = 1; i <= heap.length; i++) {
//执行n次,将每个当前最大的值放到堆末尾
int tmp = heap[0];
heap[0] = heap[heapsize - 1];
heap[heapsize - 1] = tmp;
heapsize--;
//从跟节点开始
MaxNode(1);
}
}
/**
* 递归exchange 最大值和i位置的值
*
* @param i 第几个元素,i-1对应数组heap里的下标,比如根节点为i=1 heap对应的是0
*/
public void MaxNode(int i) {
System.out.println("根节点位置:" + i + ",值:" + heap[i - 1]);
int left = getLeftPosition(i);
System.out.println("左子节点位置:" + (left));
int right = getRightPosition(i);
System.out.println("右子节点位置:" + (right));
int largest_position;
if (left <= heapsize && heap[left - 1] > heap[i - 1]) {
largest_position = left;
} else {
largest_position = i;
}
if (right <= heapsize && heap[right - 1] > heap[largest_position - 1]) {
largest_position = right;
}
if (largest_position == i || largest_position > heapsize) {//如果largest等于i说明i是最大元素 largest超出heap范围说明不存在比i节点大的子女
System.out.println(i + "位置,已经不存在比自己值还大的子节点了,最大值就是他自己。。。");
return;
} else {
System.out.println("三个节点中的最大值的位置:" + (largest_position) + ",开始交换位置");
}
//交换i与largest对应的元素位置,在largest位置递归调用MaxNode
exChange(heap, i - 1, largest_position - 1);
System.out.println("-------------");
MaxNode(largest_position);
}
/**
* 获取 i 节点的副节点位置
*
* @param i
* @return
*/
private int parent(int i) {
return (i) / 2;
}
/**
* 获取 i 节点的左子节点的位置
*
* @param i
* @return
*/
private int getLeftPosition(int i) {
return 2 * (i);
}
/**
* 获取 i 节点的右子节点的位置
*
* @param i
* @return
*/
private int getRightPosition(int i) {
return 2 * (i) + 1;
}
/**
* 交换 数组中元素
*
* @param arr
* @param position1
* @param position2
*/
private void exChange(int[] arr, int position1, int position2) {
int temp = arr[position1];
arr[position1] = arr[position2];
arr[position2] = temp;
}
/**
* 打印 树的结构
*
* @param array
*/
public void printHeapTree(int[] array) {
int length = array.length;
for (int i = 1; i < length; i = i * 2) {
int v = (int) (Math.log(i) / Math.log(2)) + 1;
for (int k = i - 1; k < 2 * i - 1 && k < length; k++) {
System.out.print(array[k] + "\t");
}
System.out.println();
}
}
public void printHeap(int[] array) {
System.out.println(Arrays.toString(array));
}
}
package me.ele;
/**
* @author LZJ
* @create 2018-09-29 14:59
**/
public class MaxHeapTest {
public static void main(String[] args) {
int[] array = new int[]{70, 60, 12, 40, 30, 8, 10};
MaxHeapSort heap = new MaxHeapSort(array);
System.out.println("================== 执行构建最大堆前堆的结构:==================");
heap.printHeapTree(heap.heap);
//构造最大堆
System.out.println("================== 构建最大堆前堆 :==================");
heap.BuildMaxHeap();
System.out.println("================== 执行构建最大堆后堆的结构:==================");
heap.printHeapTree(heap.heap);
//堆排序
System.out.println("================== 堆排序 :==================");
heap.HeapSort();
System.out.println("================== 执行堆排序后数组的内容 :==================");
heap.printHeap(heap.heap);
}
}
二、二分查找和折半插入
/**
* 二分查找_while
*
* @param arr 数组(已经排好序)
* @param value 目标值
* @return 目标值在数组中的下标 -1表示不存在
*/
public static int binarySearch(int[] arr, int value) {
int start = 0;
int end = arr.length - 1;
while (start < end) {
int middleIndex = (start + end) / 2;
if (value == arr[middleIndex]) {
return middleIndex;
} else if (value < arr[middleIndex]) {
end = middleIndex - 1;
} else {
start = middleIndex + 1;
}
}
return -1;
}
/**
*
* @param arr 数组(已经排好序)
* @param value 目标值
* @param start arr的起始位置
* @param end 。。。
* @return 目标值在数组中的下标 -1表示不存在
*/
public static int binarySearchByRecursive(int[] arr, int value, int start, int end) {
int middleIndex = (start + end) / 2;
if (value < arr[start] || value > arr[end] || start > end) {
return -1;
}
if (value == arr[middleIndex]) {
return middleIndex;
}
if (value < arr[middleIndex]) {
end = middleIndex - 1;
} else if (value > arr[middleIndex]) {
start = middleIndex + 1;
}
return binarySearchByRecursive(arr, value, start, end);
}
public static void main(String[] args) {
int[] arr = {1, 3, 4, 5, 6, 7, 9, 11};
System.out.println(binarySearch(arr, 5));
System.out.println(binarySearch(arr, 2));
System.out.println("===========");
System.out.println(binarySearchByRecursive(arr, 5, 0, arr.length - 1));
System.out.println(binarySearchByRecursive(arr, 2, 0, arr.length - 1));
}
/**
* 折半插入排序
*
* @param arr
* @return
*/
public static int[] binaryInsertSort(int[] arr) {
int number = arr.length;
int i, j;
for (i = 1; i < number; i++) {
//temp为本次循环待插入有序列表中的数
int temp = arr[i];
int low = 0;
int high = i - 1;
//使用二分查找法寻找temp插入有序列表的正确位置
while (low <= high) {
//有序数组的中间坐标,此时用于二分查找,减少查找次数
int mid = (low + high) / 2;
if (arr[mid] > temp) {
high = mid - 1;
} else {
low = mid + 1;
}
}
for (j = i - 1; j >= low; j--) {
//元素整体后移,为插入temp做准备
arr[j + 1] = arr[j];
}
//插入temp
arr[low] = temp;
//打印每次循环的结果
System.out.println(Arrays.toString(arr));
}
//打印排序结果
System.out.println(Arrays.toString(arr));
return arr;
}
public static void main(String[] args) {
int[] arr1 = {9, 1, 12, 5, 3, 11, 7, 8};
System.out.println("排序中:");
int[] binaryInsertSort = binaryInsertSort(arr1);
System.out.println("排序后:");
System.out.println(Arrays.toString(binaryInsertSort));
}
三、递归算法
/**
* 获取某文件夹下的所有文件
* @param file
* @param allfilelist
* @return
*/
public static List
public static void main(String[] args) {
//test
List
/**
* @param jsonStr 字符串
* @return 原生JsonObject 验证字符串是否满足json格式
*/
public boolean isJsonObject(String jsonStr) {
boolean isJson = true;
try {
JSONObject jsonObject = JSONObject.fromObject(jsonStr);
if (jsonObject.isNullObject()) {
isJson = false;
}
} catch (Exception e) {
isJson = false;
}
return isJson;
}
/**
* 获取json串,如args中所有的key和value
* @param jsonObject 获取json中某个key对应值的集合
*/
public Map