八大排序算法整理(Java)

关系

在这里插入图片描述

复杂度

在这里插入图片描述

插入排序

原理

记录数组中的一个数,从i = 0开始到i = arr.length - 1结束,每次遍历将它插入到它前面的序列的对应位置上。

实现

import java.util.Arrays;
import java.util.Random;

public class InsertSort {
    public static void main(String[] args) {
        Random random = new Random();
        int[] arr = new int[11];
        for (int i = 0; i < arr.length; i++) {
            arr[i] = random.nextInt(100);
        }
        System.out.println("排序前");
        System.out.println(Arrays.toString(arr));
        sort(arr);
        System.out.println("排序后");
        System.out.println(Arrays.toString(arr));
    }
    private static void sort(int[] arr){
        for (int i = 1; i < arr.length; i++) {
            int now = arr[i];
            int j = i - 1;
            for (;j >= 0; j--) {
                if(now < arr[j]){
                    arr[j + 1] = arr[j];
                }else {
                    break;
                }
            }
            arr[++j] = now;
        }
    }
}

选择排序

原理

先设置一个为最小(大)值的元素的下标,遍历它后面的所有元素,找到一个最小(大)的值,遍历完以后做交换,再进行下一轮遍历。

实现

import java.util.Arrays;

public class SelectSort {
    public static void main(String[] args) {
        int[] arr = new int[]{4351,66,7,678,8,89,34,1,6,658,567,345,3};
        System.out.print("排序前:");
        print(arr);
        sort(arr);
        System.out.print("排序后:");
        print(arr);
    }

    public static void sort(int[] arr){
        int n = 1;
        int max;
        for (int i = 0; i < arr.length; i++) {
            //设置一个最小值的下标
            max = i;
            for (int j = i + 1; j < arr.length; j++) {
                if(arr[j] < arr[max]){
                    //遍历后面的,找的最小的
                    max = j;
                }
            }
            //交换
            int temp = arr[i];
            arr[i] = arr[max];
            arr[max] = temp;
            System.out.printf("第%d次排序:",n++);
            print(arr);
        }
    }
    
    private static void print(int[] arr){
        System.out.println(Arrays.toString(arr));
    }
}

冒泡排序

自己的理解

每一轮通过依次比较并交换挑出一个最大或最小的数放到上一轮确定的数之前(上一轮放到最后的数不参与下一轮的比较)。

时间复杂度分析

每一轮比较n-1,n-2,n-3…,1次,一共进行n-1轮,总共比较n(n-1)/2=n^2/2 - n/2次,时间复杂度为O(n^2)。

原理

①比较相邻的元素。如果第一个比第二个大,就交换他们两个。
②针对所有的元素重复以上的步骤,除了最后一个。
③持续每次对越来越少的元素重复上面步骤,直到没有任何一对数字需要比较
在这里插入图片描述

实现

import java.util.Arrays;
import java.util.Random;

public class BubbleSort {
    public static void main(String[] args) {
        Random random = new Random();
        int[] arr = new int[11];
        for (int i = 0; i < arr.length; i++) {
            arr[i] = random.nextInt(100);
        }
        System.out.print("排序前:");
        print(arr);
        sort(arr);
        System.out.print("排序后:");
        print(arr);
    }

    public static void sort(int[] arr){
        int n = arr.length;
        for (int i = 0; i < arr.length - 1; i++) {
            for (int j = 0; j < arr.length - 1 - i; j++) {
                if(arr[j] > arr[j + 1]){
                    int temp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = temp;
                }
            }
            n--;
            System.out.printf("第%d次排序:",arr.length - n);
            print(arr);
        }
    }

    private static void print(int[] arr){
        System.out.println(Arrays.toString(arr));
    }
}

快速排序

自己的理解

先选出第一个数,然后往后面遍历,比它大的放一边,比它小的放另一边,分为两个集合,这就确定了这个数的位置,然后进行下一轮,两个集合各选出一个数,使这两个数和上一轮一样排序并各选出两个集合,以此类推,直到把所有数的位置都确定。

时间复杂度分析

每一轮确定的数据的数量都是加倍的,每一轮遍历n个数,总共进行logn轮,所以操作数为nlogn次,时间复杂度为O(nlogn)。

原理

通过一趟排序将序列分成左右两部分,其中左半部分的的值均比右半部分的值小,然后再分别对左右部分的记录进行排序,直到整个序列有序。
在这里插入图片描述

实现

数组实现

import java.util.Arrays;

public class QuickSort {
    public static void main(String[] args) {
        int[] arr = new int[]{12,3,34,2500,4,457,65,234,238,2768,2999,1};
        System.out.print("排序前:");
        print(arr);
        sort(arr,0,arr.length - 1);
        System.out.println();
        System.out.print("排序后:");
        print(arr);
    }

    private static void sort(int[] arr, int head, int tail){
        if(head >= tail){
            return;
        }
        int i = head;
        int j = tail;

        //选中的基准数是数组是中间那个数
        int base = arr[(i + j) / 2];
        System.out.println();
        System.out.println("中间数:" + base);
        System.out.println("头:" + head);
        System.out.println("尾:" + tail);

        while (i < j) {
            while (arr[i] < base){
                i++;
            }
            while (arr[j] > base) {
                j--;
            }
             
            if(i < j){//找到一大一小的数据进行交换
                System.out.println("i="+ i + " 值=" + arr[i] + " 和 " + "j=" + j + " 值=" + arr[j] + " 交换");
                int temp = arr[i];
                arr[i] = arr[j];
                arr[j] = temp;
                i++;
                j--;
                print(arr);
            }else if(i == j){//特殊情况,i=j使i++
                i++;
            }
        }
        //递归,变成两个集合
        sort(arr,head,j);
        sort(arr,i,tail);
    }

    private static  void print(int[] arr){
        System.out.println(Arrays.toString(arr));
    }
}

链表实现

public String sort(Node link){
        if(link == null){
            return "";
        }
        Node flag = link;
        //比flag小的数据
        Node linkpre = null;
        //比flag大的数据
        Node linknext = null;
        link = link.next;
        while(link != null){
            //如果该数小于基准数,头插法将所有小的数合并到一起
            if(link.value < flag.value){
                Node n = new Node();
                n.value = link.value;
                n.next = linkpre;
                linkpre = n;
            }else{ //如果该数大于基准数,头插法将所有大的数合并到一起
                Node n = new Node();
                n.value = link.value;
                n.next = linknext;
                linknext = n;
            }
            link = link.next;
        }
        String result = flag.value;
        if(linkpre != null){
            result = sort(linkpre) + "," + result;
        }
        if(linknext != null){
            result = result + "," + sort(linknext);
        }
        return result;
    }
    
//链表头插法使数组变为链表    
public Node getlink(int[] arr){
        Node link = null;
        for(int i = 0;i < arr.length;i++){
            Node n = new Node();
            n.value = arr[i];
            n.next = link;
            link = n;
        }
        return link;
    }
    
//定义链表结构体 
class Node{
        public int value;
        public Node next;
 }

基数排序

原理

设置最长的数的位数数量的桶,使各个数先比较个位->十位->百位…,每次比较完毕之后按该位上的数放入桶中后拿出来放到原始数组中,直到最后数组就会成有序的。

实现

import java.util.Arrays;
import java.util.Random;

public class BaseSort {
    public static void main(String[] args) {
        Random random = new Random();
        int[] arr = new int[15];
        for (int i = 0; i < arr.length; i++) {
            arr[i] = random.nextInt(1000000);
        }
        System.out.print("排序前:");
        System.out.println(Arrays.toString(arr));
        sort(arr);
        System.out.print("排序后:");
        System.out.println(Arrays.toString(arr));
    }

    public static void sort(int[] arr) {
        //定义n为最大值的长度
        int n = getMaxLength(arr);
        //辅助数组
        int[] indexArr = new int[10];
        //定义10个桶
        int[][] baseArr = new int[10][arr.length];
        int mod = 1;
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < arr.length; j++) {
                //计算arr[j]的第X位的数(个位->十位->百位......)
                int t = arr[j] / mod % 10;
                //计算得到的t后,将arr[j]放到对应的t桶中,indexArr[t]表示t桶中数的数量,t桶每放一个arr[j],indexArr[t]+1
                baseArr[t][indexArr[t]++] = arr[j];
            }
            //帮助计算arr[j]下一位上的数
            mod *= 10;
            int k = 0;
            //循环从桶中拿出数据放到原始数组中
            for (int l = 0; l < baseArr.length; l++) {
                for (int j = 0; j < indexArr[l]; j++) {
                    arr[k++] = baseArr[l][j];
                }
            }
            //全部还原为0,进行下一次循环,放下一个位数的数据(也可以循环遍历归0)
            indexArr = new int[10];
            baseArr = new int[10][arr.length];
        }
    }

    //找出最大值的长度
    public static int getMaxLength(int[] arr) {
        int max = arr[0];
        for(int i : arr){
            if(max < i){
                max = i;
            }
        }
        //先转换为字符串,返回长度
        return String.valueOf(max).length();
    }
}

希尔排序

原理

分割成多个数组并分别对每个数组进行插入排序,待每个数组都基本有序时,再合并并对整个数组进行插入排序。

实现

import java.util.Arrays;
import java.util.Random;

public class ShellSort {
    public static void main(String[] args) {
        Random random = new Random();
        int[] arr = new int[10];
        for (int i = 0; i < arr.length; i++) {
            arr[i] = random.nextInt(100);
        }
        System.out.println("排序前:" + Arrays.toString(arr));
        int n = arr.length;
        int gap = n / 2;
        sort(arr,n,gap);
        System.out.println("排序后:" + Arrays.toString(arr));
    }
    
    private static void sort(int[] arr,int n,int gap) {//gap = 1就是插入排序
        while (gap >= 1){
            for (int i = gap;i < n;i++) {
                int now = arr[i];
                int j = i - gap;
                for (;j >= 0; j-=gap) {
                    if(now < arr[j]){
                        arr[j + gap] = arr[j];
                    }else{
                        break;
                    }
                }
                j += gap;
                arr[j] = now;
            }
            System.out.println("gap:" + gap + " 排序后:" + Arrays.toString(arr));
            gap /= 2;
        }
    }
}

归并排序

自己的理解

第一轮,遍历每个数据,每次挑出两个数进行比较排序,直到最后,如果最后只剩一个数,则不用管它,
后面每一轮,每两个有序序列进行合并(两个有序序列头部和头部比较,选出来大的或者小的移动到第三个序列中,让下一个数移动到头部,直到一个序列为空,另一个序列直接放到第三个序列中,每次对比都可以确定至少一个数,每次对比只需要计算一次,总共计算次数y < (n = k+m),时间复杂度为O(n)

时间复杂度分析

第一轮是n/2组,第二轮是4/n组…直到最后是两个有序序列合并为一组,每一轮计算量<O(n),总共经过logn轮,时间复杂度为O(nlogn)。

原理

①把有序表划分成元素个数尽量相等的两半
②把两半元素分别排序
③把两个有序表合并成一个
在这里插入图片描述

实现

递归实现

import java.util.Arrays;
import java.util.Random;

public class MergeSort {
    public static void main(String[] args) {
        Random random = new Random();
        int[] arr = new int[11];
        for (int i = 0; i < arr.length; i++) {
            arr[i] = random.nextInt(100);
        }
        System.out.print("排序前:");
        System.out.println(Arrays.toString(arr));
        sort(arr,0,arr.length - 1);
        System.out.print("排序后:");
        System.out.println(Arrays.toString(arr));
    }
    
    //归并排序
    public static void sort(int[] arr,int low,int high){
        int mid = (low + high) / 2;
        if(low < high){
            //左边排序
            sort(arr,low,mid);
            //右边排序
            sort(arr,mid + 1,high);
            //合并
            merge(arr, low, mid, high);
        }
    }

    //两个有序序列的合并
    private static void merge(int[] arr, int low, int mid, int high) {
        int [] temp = new int[high - low + 1];
        int i = low;
        int j = mid + 1;
        int k = 0;
        //合并
        while (i <= mid && j <= high){
            if(arr[i] < arr[j]){
                temp[k++] = arr[i++];
            }else {
                temp[k++] = arr[j++];
            }
        }
        //右边先跑完
        while (i <= mid){
            temp[k++] = arr[i++];
        }
        //左边先跑完
        while (j <= high){
            temp[k++] = arr[j++];
        }
        //将新数组的值放到原来的数组里
        for (int l = 0; l < temp.length; l++) {
            arr[low + l] = temp[l];
        }
    }
}

非递归实现

public class MergeSort {
    public static void main(String[] args) {
        int [] arr = {5,2,7,1,4,6,2,7,9,1};
        //用来存储每一次合并的结果
        int [][] arr1 = new int[arr.length][1];
        //第一次,一个数一组
        for (int i = 0; i < arr.length; i++) {
            arr1[i][0] = arr[i];
        }
        //当结果只有一组的时候代表排序完毕
        while (arr1.length != 1){
            int length;
            if(arr1.length % 2 == 0){
                length = arr1.length / 2;
            }else{//奇数长度,需要留一个位置存放多余的那个
                length = arr1.length / 2 + 1;
            }
            int [][] arr2 = new int[length][];
            for (int i = 0; i < length; i++) {
            	//当到多余的那个的时候,直接放入,不需要合并
                if (2 * i + 1 == arr1.length) {
                    arr2[i] = arr1[2 * i];
                } else {
                    arr2[i] = merge(arr1[2 * i], arr1[2 * i + 1]);
                }
            }
            arr1 = arr2;
        }
        for (int i = 0; i < arr1[0].length; i++) {
            System.out.print(arr1[0][i] + " ");
        }
    }
    //合并两个有序数组
    public static int[] merge(int arr1[],int arr2[]){
        int [] result = new int[arr1.length + arr2.length];
        int index1 = 0;
        int index2 = 0;
        int index3 = 0;
        while (index1 < arr1.length && index2 < arr2.length) {
            if (arr1[index1] < arr2[index2]) {
                result[index3++] = arr1[index1++];
            } else {
                result[index3++] = arr2[index2++];
            }
        }
        if(index1 < arr1.length){
            for (int i = index1; i < arr1.length; i++){
                result[index3++] = arr1[i];
            }
        }
        if(index2 < arr2.length){
            for (int i = index2; i < arr2.length; i++){
                result[index3++] = arr2[i];
            }
        }
        return result;
    }
}

堆排序(使用到了完全二叉树)

自己的理解

假如有一亿个数,找前100大(小),我们可以构建100个元素的小(大)顶堆,作为前100大(小),遍历一亿个数,如果有数大于(小于)堆顶元素,它就有资格进入前100大(小),将堆顶元素去掉,再选择最小(大)的元素放在堆顶,继续遍历,直到找到100个数。
完全树和满树:
完全树和满树并不是有序树。
完全树和满树可以完全利用数组来表示。
完全二叉树中根结点索引的2倍+1是左孩子索引,2倍+2是右孩子索引。
可以根据索引确定是左孩子还是右孩子(奇数为左孩子,偶数为右孩子),还可以确定根结点索引和值。
堆排序:
大顶堆:arr[i] >= arr[2i+1] && arr[i] >= arr[2i+2]
小顶堆:arr[i] <= arr[2i+1] && arr[i] <= arr[2i+2]

堆并不是有序树。
每一层每一层地放值,每一层都要排满,先将第一个数放到根结点,然后放下一个数到下一个位置,如果下一个数比父结点的数大,则两者换位置并继续和新的父结点比较,直到比父结点小,直到全部数据放完为止。
从根结点拿走数据放到序列中,拿走一个以后,将最后一个位置的数拿到根结点位置并和孩子结点比较,选择最大的和根结点交换位置,并继续和孩子结点比较,直到全部数据都被拿出来,最后形成的序列就是有序的。

时间复杂度分析

放入数据时:
第一层20个数,第二层有21个数…,一共有logn层。
一共n个数,每一个数比较次数y<logn,总的比较次数Y<nlogn。
时间复杂度为O(nlogn)。
拿出数据时:
比较次数y<logn,总的比较次数Y<nlogn。
时间复杂度为O(nlogn)。
总体:
总的时间复杂度为O(2nlogn) = O(nlogn)。

原理

假设序列有n个元素先将这n个数建成大顶堆,然后取堆顶元素,与序列第n个元素交换,然后调整前n-1元素,使其重新成为堆,再取堆顶元素,与第n-1个元素交换,再调整前n-2个元素…直至整个序列有序。

实现

import java.util.Arrays;

public class HeapSort {
    public static void main(String[] args) {
        int[] arr = new int[]{3,2,6,1,8,4,9,5};
        sort(arr);
        System.out.println(Arrays.toString(arr));
    }
    public static void sort(int[] arr){
         //1.构建大顶堆
        for (int i = arr.length / 2 - 1; i >= 0; i--) {
            adjustHeap(arr,i,arr.length);
        }
        
        //2.调整堆结构+交换堆顶元素与末尾元素
        for (int j = arr.length - 1; j > 0; j--) {
            //将堆顶元素与末尾元素进行交换
            swap(arr,0,j);
            //重新对堆进行调整
            adjustHeap(arr,0,j);
        }
    }
    
    //调整大顶堆
    public static void adjustHeap(int[] arr,int i,int length){
        //先取出非叶子结点i
        int t = arr[i];
        //从i结点的左子结点开始,也就是2i+1处开始
        for (int k = i * 2 + 1; k < length; k = k * 2 + 1) {
            //如果左子结点小于右子结点,k指向右子结点
            if(k + 1 < length && arr[k] < arr[k + 1]){
                k++;
            }
            //如果子结点大于i结点,将子节点值赋给i结点(不用进行交换)
            if(arr[k] > t){
                arr[i] = arr[k];
                i = k;
            }else{
                break;
            }
        }
        //将t的值放到最终的位置
        arr[i] = t;
    }
    
    //交换元素
    public static void swap(int[] arr, int a,int b) {
        int t = arr[a];
        arr[a] = arr[b];
        arr[b] = t;
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值