排序
1.二分查找
1.1 原理
需求:使用二分查找找出该元素在数组中第一次出现的索引
二分查找的前提:该数组元素必须有序
思想:每一次查找中间的元素,比较大小就能减少一半的元素
最小索引 : 0 ------ minIndex
最大索引 : array.length - 1------maxIndex
中间索引:(最小索引+最大索引)/ 2-----centerIndex
步骤:
查找的时候我们拿着这个元素和中间索引对应的元素进行比较,会出现以下三种情况:
-
要找的元素=中间索引对应的二元素,那么我们就找到了,直接返回中间索引
-
要找的元素比中间索引对应的元素小,那么我们就要改变最大索引,移动最大索引
maxIndex = centerIndex - 1;
-
要找的元素比中间索引对应的元素大,那么我们就要改变最小索引,移动最小索引
minIndex = centerIndex +1;
移动之后返回1,重新计算中间索引,循环找,直到minIndex <= maxIndex停止循环
1.2 实现
public static void main(String[] args) {
int[] array = new int[]{10,20,30,40,50,60};
int index = getIndex(array,50);
System.out.println("该元素出现的索引:" + index);
}
private static int getIndex(int[] array, int element) {
//定义三个索引,最小索引,最大索引,中间索引
int minIndex = 0;
int maxIndex = array.length - 1;
int centerIndex = (maxIndex+minIndex)/2;
while (maxIndex <= maxIndex) {
if(element == array[centerIndex]) {
return centerIndex;
}else if(element < array[centerIndex]) {
maxIndex = centerIndex - 1;
}else if(element > array[centerIndex]){
minIndex = centerIndex + 1;
}
centerIndex = (maxIndex+minIndex)/2;
}
// 如果没有找到,返回-1
return -1;
}
2. 冒泡排序
2.1 原理
原理:数组元素两两比较,交换位置,大元素往后放,那么经过一轮的比较后,最大的元素,就会出现在最大索引处
2.2实现
public static void main(String[] args) {
int[] array = new int[] {24,69,80,57,13};
for(int i = 0;i < array.length - 1;i ++) {
for(int j = 0;j < array.length - i - 1;j ++) {
if(array[j] > array[j +1]) {
//交换位置
int t = array[j];
array[j] = array[j+1];
array[j+1] = t;
}
}
}
System.out.println( Arrays.toString(array));;
}
3. 选择排序
3.1 原理
原理:从0索引初开始,一次和后面的元素进行比较,小的元素往前放,经过一轮比较后,最小的元素就出现在了最小索引出
3.2 实现
public static void main(String[] args) {
int[] array = new int[] {24,69,80,57,13};
for(int i = 0;i < array.length - 1 ;i ++) {
for(int j = i + 1 ;j < array.length;j ++) {
if(array[i] > array[j]) {
int t = array[j];
array[j] = array[i];
array[i] = t;
}
}
}
System.out.println(Arrays.toString(array));
}
4. 直接插入排序
4.1 原理
原理:将记录插入到一个长度为m的有序表中,使之仍保持有序状态
白话:从索引1初开始,将后面的元素,插入到之前的有序列表中使之仍保持有序
4.2 实现
- 实现一:
int[] array = new int[] {24,69,80,57,13};
//外层定义循环轮次
for(int i = 1;i < array.length;i++) {
//里层循环进行比较插入
int j = i;
while (j > 0 && array[j] < array[j - 1]) {
int t = array[j];
array[j] = array[j - 1];
array[j - 1] = t;
j--;
}
}
- 实现二
int[] array = new int[] {24,69,80,57,13};
for (int i = 1; i < array.length; i++) {
for(int j = i;j > 0;j --) {
if(array[j] < array[j - 1]) {
int t = array[j];
array[j] = array[j - 1];
array[j - 1] = t;
}
}
}
5. 希尔排序
5.1原理
希尔排序又称缩小增量排序
- 基本思想:先将原表按增量ht分组,每个子文件按照直接插入法排序。同样,用下一个增量ht/2将文件再分为子文件,再直接插入排序。直到ht=1时整个文件排好序。
- 关键:选择合适的增量
- 希尔排序算法:可以通过三重循环来实现
白话:核心思想就是合理的园区一个增量,经过一轮的排序后,就会让序列大致有序,然后再不断的缩小增量,进行插入排序,直到增量为 1 那整个排序结束。(直接排序其实就是增量为 1 的希尔)
5.2实现
public static void main(String[] args) {
int[] array = {46,55,13,17,94,5,70};
shellSort(array);
System.out.println(Arrays.toString(array));
}
private static void shellSort(int[] array) {
/*//定义一个增量
int h = 4;*/
// 第一种:选取增量为数组长度的一般,然后不断的减半(不太好)
/* for(int h = array.length / 2;h > 0;h --) {
for (int i = h; i < array.length; i++) {
for(int j = i;j > h - 1;j -= h) {
if(array[j] < array[j - h]) {
swap(array,j,j- h);
}
}
}
}*/
// 第一种不太好;我们可以采取一种克努特序列
// int h = 1;
// h = h * 3 + 1;
// 根据克努特序列选取我们第一次的增量
int jiange = 1;
while (jiange <= array.length/3) {
jiange = jiange * 3 + 1;
}
for(int h = jiange;h > 0;h = (h - 1) / 3) {
for (int i = h; i < array.length; i++) {
for(int j = i;j > h - 1;j -= h) {
if(array[j] < array[j - h]) {
swap(array,j,j- h);
}
}
}
}
}
private static void swap(int[] array, int i, int j) {
int t = array[j];
array[j] = array[i];
array[i] = t;
}
6. 快速排序
6.1 原理
-
分治法:
- 从数组中取出一个数,作为基数
- 分区:将比这个数大的或者等于的数全放到他的右边,小于他的数全部放在左边
- 再对左右区间重复第二步,直到个区间只有一个数
-
思路:挖坑填数
- 将基准数挖出形成第一个坑
- 由后向前找比他小的数,找到后挖出此数填到前一个坑中
- 由前向后找比他大的数,找到了也挖出此数填到前一个坑中
- 再冲回复执行2,3步骤
6.2 实现
public static void main(String[] args) {
int[] array = new int[]{24,69,80,57,13};
quickSort(array,0,array.length - 1);
System.out.println(Arrays.toString(array));
}
private static void quickSort(int[] array, int start, int end) {
if(start < end) {
int index = getIndex(array,start,end);
quickSort(array,start,index - 1);
quickSort(array,index+1,end);
}
}
//挖坑填数
private static int getIndex(int[] array, int start, int end) {
int i = start;
int j = end;
int x = array[i];//基准数
while (i < j) {
//由后向前找比他小的数,找到后挖出此数填到前一个坑中
while (i < j && array[j] >= x) {
j--;
}
if(i < j) {
array[i] = array[j];
i++;
}
//由前向后找比他大的数,找到了也挖出此数填到前一个坑中
while (i < j && array[i] <= x) {
i++;
}
if(i < j) {
array[j] = array[i];
j--;
}
}
array[i] = x;
return i;
}
7. 归并排序
7.1 原理
归并排序就是利用归并的思想实现排序的方法
它的原理是假设初始序列有N个记录,则可以看成是N个有序的子序列,每个子序列的长度为1,然后两两归并,得到N/2个长度为2或者1的有序子序列,再两两归并…如此重复,直到得到一个长度为N的有序序列为止,这种排序方法称为2路归并排序。
7.2 实现
public static void main(String[] args) {
//原始数组
int[] array = new int[]{10,30,2,1,0,8,7,5,19,29};
// 我们先给一个左右两边是一个有序的一个数组,先进行归并操作
//int[] array = new int[]{4,5,7,8,1,2,3,6};
// 拆分
chaifen(array,0,array.length - 1);
// 归并
//guibing(array,0,array.length/2-1,array.length - 1);
//输出原数组
System.out.println(Arrays.toString(array));
}
private static void chaifen(int[] array, int startIndex, int endIndex) {
//计算中间索引
int centerIndex = (startIndex+endIndex)/2;
if(startIndex < endIndex) {
chaifen(array,startIndex,centerIndex);
chaifen(array,centerIndex+1,endIndex);
guibing(array,startIndex,centerIndex,endIndex);
}
}
private static void guibing(int[] array, int startIndex, int centerIndex, int endIndex) {
//定义一个临时数组
int[] tempArray = new int[endIndex - startIndex +1];
//定义左边数组的临时起始索引
int i = startIndex;
// 定义右边数组的临时起始索引
int j = centerIndex +1;
//定义临时数组的起始索引
int index = 0;
//比较左右两个数组的元素大小,往临时数组里面放
while (i <= centerIndex && j <= endIndex) {
if(array[i] <= array[j]) {
tempArray[index] = array[i];
i++;
}else {
tempArray[index] = array[j];
j++;
}
index++;
}
//处理剩余元素
while (i <= centerIndex) {
tempArray[index] = array[i];
index++;
i++;
}
while (j <= endIndex) {
tempArray[index] = array[j];
index++;
j++;
}
//System.out.println(Arrays.toString(tempArray));
//将临时数组中的元素取到原数组中
for (int k = 0; k < tempArray.length; k++) {
array[k + startIndex] = tempArray[k];
}
}
8. 基数排序
8.1 原理
基数排序之前所介绍的各类排序
前面介绍的排序方法或多或少的是通过使用比较和移动记录来实现排序,而基数排序的实现不需要进行关键字的比较
只需要对关键字进行“分配”和“收集”即可完成。
8.2 实现
public static void main(String[] args) {
int[] array = new int[] {24,69,80,57,13};
// 获取数组的最大值
// int max = getMax(array);
// 基数排序
sortArray(array);
System.out.println(Arrays.toString(array));
}
private static void sortArray(int[] array) {
// 定义二维数组,放10个桶
int[][] tempArray = new int[10][array.length];
// 定义统计数组
int[] counts = new int[10];
int max = getMax(array);
int len = String.valueOf(max).length();
// 循环轮次
for (int i = 0,n = 1; i < len; i++,n *= 10) {
for (int j = 0; j < array.length; j++) {
int ys = array[j]/n%10;
tempArray[ys][counts[ys]++] = array[j];
}
// 取出桶中的元素
int index = 0;
for (int k = 0; k < counts.length; k++) {
if(counts[k] != 0) {
for (int h = 0;h < counts[k] ;h ++) {
// 从桶中取出元素放回原数组
array[index] = tempArray[k][h];
index++;
}
counts[k] = 0;// 清除上一次统计的个数
}
}
}
}
private static int getMax(int[] array) {
int max = array[0];
for (int i = 1; i < array.length; i++) {
if(array[i] > max) {
max = array[i];
}
}
return max;
}
9. 堆排序
堆排序是利用对这种数据结构而设计的一种排序算法,堆排序是选择排序的一种
9.1 原理
堆排序的基本思想:
- 将待排序序列构造成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点
- 将其与末尾元素进行交换,此时莫为就是最大值
- 然后将剩余n-1个元素重新构造成一个队,这样会得到n个元素的次小值
- 如此反复执行,便能得到一个有序序列了。
9.2 实现
public static void main(String[] args) {
int[] array = new int[] {24,69,80,57,13};
// 调整成大顶堆的方法
// 定义开始调整的位置
int startIndex = (array.length - 1) /2;
// 循环开始调整
for(int i = startIndex; i >= 0;i --) {
toMaxHeap(array,array.length,i);
}
// 经过上面的操作,已经把数组变成了一个大顶堆,把根元素和最后一个元素进行交换
for(int i = array.length - 1;i > 0;i --) {
// 进行调换
int t = array[0];
array[0] = array[i];
array[i] = t;
//换完之后,我们在把剩余元素调成大顶堆
toMaxHeap(array,i,0);
}
System.out.println(Arrays.toString(array));
}
/**
*
* @param array 要排序的数组
* @param size 调整的个数
* @param index 从哪里开始调整
*/
private static void toMaxHeap(int[] array, int size, int index) {
// 获取左右子节点的索引
int leftNodeIndex = index * 2 + 1;
int rightNodeIndex = index * 2 + 2;
// 查找最大节点对应的索引
int maxIndex = index;
if(leftNodeIndex <size && array[leftNodeIndex] > array[maxIndex]) {
maxIndex = leftNodeIndex;
}
if(rightNodeIndex < size && array[rightNodeIndex] > array[maxIndex]) {
maxIndex = rightNodeIndex;
}
// 调换位置
if(maxIndex != index) {
int t = array[maxIndex];
array[maxIndex] = array[index];
array[index] = t;
// 调换完之后,可能会影响到下面的子树,不是大顶堆了,我们还需要再次调换
toMaxHeap(array,size,maxIndex);
}
}