排序算法
1.直接插入排序
1.1思路
直接插入排序是以当前节点为起始点,向前遍历,并找到自己自己预期的位置。
1.2代码实现
package sort;
import java.util.Arrays;
/**
* Created with IntelliJ IDEA.
* Description:直接插入排序
* User:吴博
* Date:2021 06 20
* Time:13:39
*/
public class DirectInsertionSort {
public static void main(String[] args) {
int[] num = {1,2,3,4,5,6,7,8,9};
func(num);
System.out.println(Arrays.toString(num));
}
//直接插入排序(从大到小)
public static void func(int[] arr){
//从下标为1的元素开始,因为我们要往前插入我们选定大小的值
for (int i = 1; i < arr.length; i++) {
int tmp = arr[i];
int j = i - 1;
for (; j >= 0; j--) {
//我们实现从大到小的排序
//所以每次比较i和j位置的值,将大值放到j位置
//修改此位置“<”,“>”可以改变排序的顺序
if(tmp < arr[j]){
//j + 1的位置就是i的位置
//每次都时两两交换
arr[j + 1] = arr[j];
}else{
//因为直接插入排序是一次一次插入的,所以一旦发现两个位置的大小顺序是对的,那就说明在此之前的大小顺序都是对的
break;
}
}
//将tmp放入j + 1的位置,因为是j的位置是最后一个比他小的值,判断完后循环退出,所以要放在j+1的位置
arr[j + 1] = tmp;
}
}
}
1.3复杂度分析
时间复杂福 | 空间复杂度 | ||
---|---|---|---|
最好 | 平均 | 最坏 | |
O(1) | O(n^2) | O(n^2) | O(1) |
数据有序 | 数据逆序 |
1.4稳定性
直接插入排序是稳定的排序
2.希尔排序
2.1思路
希尔排序与直接插入排序很像,只不过希尔排序使用了增量数组,意义是在指定的长度内调整数组,使数组逐渐趋于有序。
具体来说:先选定一个整数,把待排序文件中所有记录分成个组,所有距离为的记录分在同一组内,并对每一组内的记录进行排序。然后,取,重复上述分组和排序的工作,当到达=1时,所有记录在统一组内排好序。
- 希尔排序是对直接插入排序的优化。
- 当gap > 1时都是预排序,目的是让数组更接近于有序。当gap == 1时,数组已经接近有序的了,这样就会很快。这样整体而言,可以达到优化的效果。我们实现后可以进行性能测试的对比。
2.2代码实现
package sort;
import java.util.Arrays;
/**
* Created with IntelliJ IDEA.
* Description:希尔排序
* User:吴博
* Date:2021 06 20
* Time:14:15
*/
public class ShellSort {
public static void main(String[] args) {
int[] num = {1,2,3,4,5,6,7,8,9};
func(num);
System.out.println(Arrays.toString(num));
}
//希尔排序
public static void func(int[] arr){
//定义增量数组,最后一个元素必须是1
int[] group = {5,3,1};
for (int i = 0; i < group.length; i++) {
shellSort(arr,group[i]);
}
}
//希尔排序中的直接插入排序操作
private static void shellSort(int[] arr, int group) {
for (int i = group; i < arr.length; i++) {
int tmp = arr[i];
int j = i - group;
for(; j >=0 ;j = j - group){
if(tmp < arr[j]){
//比他小的元素后移
arr[j + group] = arr[j];
}else {
break;
}
}
//将大的元素插入到预期的位置
arr[j + group] = tmp;
}
}
}
2.3复杂度分析
时间复杂福 | 空间复杂度 | ||
---|---|---|---|
最好 | 平均 | 最坏 | |
O(n) | O(n^1.3) | O(n^2) | O(1) |
数据有序 | 比较难构造(增量数组) |
2.4稳定性
希尔排序是不稳定的,因为出现了跳跃式的交换。
3.直接选择排序
3.1思路
直接选择排序是遍历数组以当前元素的位置为起点,寻找之后元素中的最大值,并将其与该位置为的元素交换,每一个位置都重复此操作。
3.2代码实现
package sort;
import java.util.Arrays;
/**
* Created with IntelliJ IDEA.
* Description:
* User:吴博
* Date:2021 06 20
* Time:14:31
*/
public class SelectSort {
public static void main(String[] args) {
int[] num = {1,2,3,4,5,6,7,8,9};
func(num);
System.out.println(Arrays.toString(num));
}
//直接选择排序(每次从后面选择最大的值放入当前位置)
public static void func(int[] arr) {
for (int i = 0; i < arr.length; i++) {
//每次从当前位置,往后去找到最大值放入当前位置
for (int j = i; j < arr.length; j++) {
if(arr[i] < arr[j]){
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
}
}
}
}
3.3复杂度分析
时间复杂度 | 空间复杂度 |
---|---|
O(n^2) | O(1) |
数据不敏感 | 数据不敏感 |
3.4稳定性
直接选择排序是不稳定的,发生了跳跃式的交换。
4.冒泡排序
4.1思路
冒泡排序以从大到小举例,是两两交换,将大的元素往前移,小的元素往后移,一次循环过后,可以确定最后一个元素的位置,下次循环可以确定倒数第二个元素的位置,依次类推,所以需要循环数组长度/数组长度-1次,为什么二者都可以,因为最后一次循环判断是无效的,前面的所有循环已经将除了第一个元素外的所有元素都排序好了,剩下的最后一不用调整了,只剩他,他肯定就在那个位置。
4.2代码实现
package sort;
import java.util.Arrays;
/**
* Created with IntelliJ IDEA.
* Description:冒泡排序(从大到小)
* User:吴博
* Date:2021 06 20
* Time:15:58
*/
public class BubbleSort {
public static void main(String[] args) {
int[] num = {1,2,3,4,5,6,7,8,9};
func1(num);
System.out.println(Arrays.toString(num));
}
//冒泡排序优化
public static void func1(int[] arr){
//大的循环,保证每个为位置都被判断过
//这里i的最终值可以为arr.length - 1,可以少判断一次,因为最后一次只判断第一个和第二个元素,经过前面一步调整,他俩是有序的,所以可以省略最后一次
for (int i = 0; i < arr.length; i++) {
//内部循环,进行优化,减少判断元素的个数
//因为每经过一次大循环都会确认一个元素的位置,第一次为最后一个,第二次为倒数第二个,以此类推
//每次都会使判断数组长度-1,都会将一个在指定数组长度中最小的值放入正确的位置
for (int j = 0; j < arr.length - i - 1; j++) {
//注意这里可控制元素的下标一定是j不是i,i知识控制循环次数的
if(arr[j] < arr[j + 1]){
int tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
}
}
}
}
//冒泡排序
public static void func(int[] arr) {
//最多循环count次就绝对有序
int count = arr.length - 1;
//每经过一次while循环都会有一个最小的元素的确认它的位置,第一次确认最后一个,第二次确认倒数第二个
while(count-- != 0){
for (int i = 0; i < arr.length - 1; i++) {
//如果当前元素小于它的下一个元素,则交换位置,保证较大的元素都放在前面
if(arr[i] < arr[i + 1]){
int tmp = arr[i];
arr[i] = arr[i + 1];
arr[i + 1] = tmp;
}
}
}
}
}
4.3复杂度
时间复杂福 | 空间复杂度 | ||
---|---|---|---|
最好 | 平均 | 最坏 | |
O(n) | O(n^2) | O(n^2) | O(1) |
数据有序 | 数据逆序 |
4.4稳定性
冒泡排序是稳定的排序。
5.堆排序
5.1思路
用到向下调整
5.2代码实现
package sort;
import java.util.Arrays;
/**
* Created with IntelliJ IDEA.
* Description:堆排序(升序建立大堆,降序建立小堆)
* User:吴博
* Date:2021 06 20
* Time:16:31
*/
public class HeapSort {
static int[] arr;
static int heapSize = 0;
public static void main(String[] args) {
HeapSort sort = new HeapSort();
int[] num = {27,15,19,18,28,34,65,49,25,37};
createHeap(num);
heapSort(num);
show();
}
public static void heapSort(int[] num){
int end = arr.length - 1;
while(end > 0){
//交换首尾元素
int tmp = arr[0];
arr[0] = arr[end];
arr[end] = tmp;
adjustDown(0,end);
end--;
}
}
//向下调整,建立小堆,走升序
public static void adjustDown(int parent,int heapSize){
int child = 2 * parent + 1;
while(child < heapSize){
if(child + 1 < heapSize && arr[child] > arr[child + 1]){
child++;
}
if(arr[parent] > arr[child]){
int tmp = arr[parent];
arr[parent] = arr[child];
arr[child] = tmp;
parent = child;
child = 2 * parent + 1;
}else {
break;
}
}
}
//初始化堆:先将数组数据存入arr,在进行向下调整
public static void createHeap(int[] num){
for (int i = 0; i < num.length; i++) {
arr[i] = num[i];
heapSize++;
}
//进行向下调整
for (int i = (heapSize - 1 - 1 ) / 2; i >= 0; i--){
//从最后一个结点的父节点算起,一个一个的调整
adjustDown(i,heapSize);
}
}
//打印堆
public static void show(){
System.out.println(Arrays.toString(arr));
}
//初始化
public HeapSort(){
arr = new int[10];
}
//判断是否空
public boolean isEmpty(){
return heapSize == 0;
}
//判断是否满
public boolean ifFull(){
return arr.length == heapSize;
}
}
5.3复杂度分析
时间复杂度 | 空间复杂度 |
---|---|
O(n*log(n)) | O(1) |
数据不敏感 | 数据不敏感 |
5.4稳定性
堆排序是不稳定的。
6.快速选择排序
6.1思路
6.2代码实现
package sort;
import java.util.Arrays;
/**
* Created with IntelliJ IDEA.
* Description:快速排序(从大到小)
* User:吴博
* Date:2021 06 20
* Time:17:49
*/
public class QuickSort {
public static void main(String[] args) {
int[] num = {1,2,3,4,5,6,7,8,9};
quickSort(num);
System.out.println(Arrays.toString(num));
}
private static void quickSort(int[] num) {
quick(num,0,num.length - 1);
}
private static void quick(int[] num, int qStart, int qEnd) {
if(qStart < qEnd){
//找基准,找到后说明piv位置就确定了,不用去调整了,接下来去调整基准的左右(分别再找其左右递归实现)
int piv = pivot(num,qStart,qEnd);
//找左边的基准
quick(num,qStart,piv - 1);
//找右边的基准
quick(num,piv + 1,qEnd);
}
}
private static int pivot(int[] num, int start, int end) {
//记录初始值
int tmp = num[start];
//只要start<end就一直走
while(start < end){
//保证下标合法的前提下,实现升序,从后找比tmp大的值
while(start < end && num[end] <= tmp){
end--;
}
//找到后将其赋值到start位置
num[start] = num[end];
保证下标合法的前提下,实现升序,从前找比tmp小的值
while(start < end && num[start] >= tmp){
start++;
}
//end
num[end] = num[start];
}
//循环结束说明,start与end相遇了。这个位置就是tmp应该放的位置。
num[start] = tmp;
return start;
}
}
6.3复杂度分析
时间复杂度 | 空间复杂度 | ||||
---|---|---|---|---|---|
最好 | 平均 | 最坏 | 最好 | 平均 | 最坏 |
O(n * log(n)) | O(n * log(n)) | O(n^2) | O(log(n)) | O(log(n)) | O(n) |
6.4稳定性
快排是不稳定的。
7.归并排序
7.1代码实现
package sort;
import java.util.Arrays;
/**
* Created with IntelliJ IDEA.
* Description:归并排序
* User:吴博
* Date:2021 06 20
* Time:18:29
*/
public class MergeSort {
public static void main(String[] args) {
int[] arr = {4,5,2,1,3,9,8,7,6};
mergeSort(arr);
System.out.println(Arrays.toString(arr));
}
public static void merge(int[] array , int start , int mid , int end){
int[] tmp = new int[end - start + 1];
int k = 0;
int s1 = start;
int e1 = mid ;
int s2 = mid + 1;
int e2= end;
while(s1 <= e1 && s2 <= e2){
if(array[s1] <= array[s2]) {
tmp[k++] = array[s1++];
}else{
tmp[k++] = array[s2++];
}
}
while(s1 <= e1){
tmp[k++] = array[s1++];
}
while (s2 <= e2){
tmp[k++] = array[s2++];
}
for(int i = 0 ;i < tmp.length ; i++){
array[i + start] = tmp[i];
}
}
public static void mergeSortRec(int[] array , int start , int end ){
if(start >= end){
return;
}
int mid = (start + end)/2;
mergeSortRec(array , start , mid);
mergeSortRec(array , mid+1 , end);
merge(array , start , mid , end);
}
public static void mergeSort(int[] array){
mergeSortRec(array , 0 , array.length-1);
}
}