七大排序算法

目录

1.交换排序--冒泡排序

2.交换排序--快速排序

3.选择排序--简单选择排序

4.选择排序--堆排序

5.插入排序--简单插入排序

6.插入排序--希尔排序

7.归并排序


 1.交换排序--冒泡排序

从要排序的第一个元素开始,一次比较相邻元素的值,发现逆序则交换,将值较大的元素逐渐从前向后移动。

public void bubbleSort(int[] arr){
    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 t = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = t;
            }
        }
    }
}

 优化:如果某次排序中,没有发生交换,则可以结束排序

public void bubbleSort(int[] arr){
    boolean flag = false;//表示没有发生过交换
    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 t = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = t;
                flag = true;//发生交换
            }
        }
        if (!flag)
            break;
        else
            flag = false;//重置flag,进行下次判断
    }
}

时间复杂度:O(n^2)

2.交换排序--快速排序

快速排序(Quicksort)是对冒泡排序的一种改进。基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列

public static void quickSort(int[] arr, int left, int right) {
    int l = left;
    int r = right;
    int pivot = arr[(left + right) / 2];
    //while循环是为了让比pivot小的值放其左边,比pivot大的值放其右边
    while (l < r) {
        while (arr[l] < pivot)//直到找到比pivot小的数
            l++;
        while (arr[r] > pivot)//直到找到比pivot大的数
            r--;
        if (l >= r)//说明比pivot小的值都在其左边,比pivot大的值都在其右边
            break;
        //找到pivot左边比其大的元素,右边比其小的元素;进行交换
        int t = arr[l];
        arr[l] = arr[r];
        arr[r] = t;
    }
    //如果l==r,则要l++、r--;否则会出现栈溢出
    if (l == r) {
        l++;
        r--;
    }
    //向左递归
    if (left < r)
        quickSort(arr, left, r);
    //向右递归
    if (right > l)
        quickSort(arr, l, right);
}

时间复杂度:O(nlog2n)

3.选择排序--简单选择排序

基本思想:

第一次从 arr[0]~arr[n-1]中选取最小值,与 arr[0]交换
第二次从 arr[1]~arr[n-1]中选取最小值,与 arr[1]交换
第三次从 arr[2]~arr[n-1]中选取最小值,与 arr[2] 交换…
第 i 次从 arr[i-1]~arr[n-1]中选取最小值,与 arr[i-1]交换…
第 n-1 次从 arr[n-2]~arr[n-1]中选取最小值,与 arr[n-2]交换
总共通过 n-1 次,得到一个按排序码从小到大排列的有序序列

public void selectSort(int[] arr){
    for (int i = 0; i < arr.length - 1; i++) {
        int min = arr[i];//假设当前值最小
        int minIndex = i;//记录最小值的索引
        for (int j = i + 1; j < arr.length; j++) {
            if (arr[j] < min) {
                min = arr[j];
                minIndex = j;
            }
        }
        if (minIndex != i) {
            arr[minIndex] = arr[i];
            arr[i] = min;
        }
    }
}

时间复杂度:O(n^2)

4.选择排序--堆排序

堆排序是利用这种数据结构而设计的一种排序算法,堆排序是一种选择排序,它的最坏,最好,平均时间复杂度均为O(nlogn),它也是不稳定排序

什么是堆
堆是具有以下性质的完全二叉树

每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆(没有要求结点的左孩子的值和右孩子的值的大小关系)

arr[i] >= arr[2*i+1] && arr[i] >= arr[2*i+2]  //i对应第几个节点,i从0开始编号

我们对堆中的结点按层进行编号,映射到数组中就是下面这个样子:

 每个节点的值都小于或等于其左右孩子节点的值,成为小顶堆

arr[i] <= arr[2*i+1] && arr[i] <= arr[2*i+2]  //i对应第几个节点,i从0开始编号

 堆排序基本思想
1. 将待排序序列构造成一个大顶堆
2. 此时,整个序列的最大值就是堆顶的根节点。
3. 将其与末尾元素进行交换,此时末尾就为最大值。
4. 然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值。如此反复执行,便能得     到一个有序序列了。

一般升序采用大顶堆,降序采用小顶堆

5.插入排序--简单插入排序

基本思想:把 n 个待排序的元素看成为一个有序表和一个无序表,开始时 有序表 中只包含一个元素,无序表中包含有 n-1 个元素,排序过程中每次从无序表中取出第一个元素,与有序表中的元素进行比较,将它插入到有序表中的适当位置,使之成为新的有序表

public void InserSort(int[] arr){
    for (int i = 1; i < arr.length; i++) {
        int insertVal = arr[i];//待插入左边有序表的数
        int left_index = i - 1;//左边有序表的索引,初值为有序表最右数的索引
        while (left_index >= 0 && insertVal < arr[left_index]) {//不断遍历左边的有序表进行比较
            arr[left_index + 1] = arr[left_index];//比较过的值右移
            left_index--;//索引-1,继续比较
        }
        if (left_index + 1 != i)
            arr[left_index + 1] = insertVal;
    }
}

时间复杂度:O(n^2)

6.插入排序--希尔排序

简单的插入排序可能存在的问题:当需要插入的数是较小的数时,后移的次数明显增多,对效率有影响

# 比如数组 arr = {2,3,4,5,6,1} 这时需要插入的数 1(最小),这样的过程是
{2,3,4,5,6,6}
{2,3,4,5,5,6}
{2,3,4,4,5,6}
{2,3,3,4,5,6}
{2,2,3,4,5,6}
{1,2,3,4,5,6}
1
2
3
4
5
6
7
为了改进,提出了希尔排序算法:

希尔排序是希尔(Donald Shell)于 1959 年提出的一种排序算法。希尔排序也是一种插入排序,它是简单插入排序经过改进之后的一个更高效的版本,也称为缩小增量排序

//交换式
public void shellSort(int[] arr){
    for(int gap=arr.length/2;gap>0;gap/=2){ //步长gap逐次递减
        for(int i=gap;i<arr.length;i++){
            for(int j=i-gap;j>=0;j-=gap){
                if(arr[j]>arr[j+gap]){
                    int t=arr[j];
                    arr[j]=arr[j+gap];
                    arr[j+gap]=t;
                }
            }
        }
    }
}

//改进成移位法
public void shellSort(int[] arr){
    for (int gap = arr.length / 2; gap > 0; gap /= 2) {//步长gap逐次递减
        for (int i = gap; i < arr.length; i++) {
            int left_index = i - gap;//左边有序表的索引,初值为有序表最右数的索引
            int insertVal = arr[i];//要插入的值
            while (left_index >= 0 && insertVal < arr[left_index]) {
                arr[left_index + gap] = arr[left_index];//比较过的值右移
                left_index -= gap;
            }
            arr[left_index + gap] = insertVal;
        }
    }
}

时间复杂度:O(nlog2n)

7.归并排序

归并排序(MERGE-SORT)是利用归并的思想实现的排序方法,该算法采用经典的分治策略(divide-and-conquer)

分治法将问题分(divide)成一些小的问题然后递归求解
而治(conquer)的阶段则将分的阶段得到的各答案"修补"在一起,即分而治之

再来看看治阶段,我们需要将两个已经有序的子序列合并成一个有序序列,比如上图中的最后一次合并,要将[4,5,7,8]和[1,2,3,6]两个已经有序的子序列,合并为最终序列[1,2,3,4,5,6,7,8],来看下实现步骤

 

左边序列和右边序列分别有一个索引指向第一个元素,然后进行比较,较小的元素存入一个临时数组temp,较小元素边序列索引右移,以此往复不断比较存入,直到一边的索引走到该子序列的最后;然后将有剩余数据的序列的剩余值直接按序存入temp数组中;

最后所有元素都存入临时数组temp中,此时temp数组中的元素有序

最后将temp数组拷贝回原数组,即实现了排序

package test;

import java.util.*;

public class Main {
    public static void main(String[] args) {
        int[] arr = {8, 10, -1, 6, 7, 3, 0, 40, 70};
        int[] temp = new int[arr.length];//归并排序需要额外的空间
        mergeSort(arr, 0, arr.length - 1, temp);
        System.out.println(Arrays.toString(arr));
    }

    //分+合方法
    public static void mergeSort(int[] arr, int left, int right, int[] temp) {
        if (left < right) {
            int mid = (left + right) / 2;//中间索引
            mergeSort(arr, left, mid, temp);//向左递归分解
            mergeSort(arr, mid + 1, right, temp);//向右递归分解
            merge(arr, left, mid, right, temp);//合并
        }
    }

    //合并方法
    public static void merge(int[] arr, int left, int mid, int right, int[] temp) {
        int i = left;//i为指向左边序列第一个元素的索引
        int j = mid + 1;//j为指向右边序列第一个元素的索引
        int t = 0;//指向临时temp数组的当前索引

        //先把左右两边有序数据按规则存入temp数组中,直到有一边的数据全部填充temp中
        while (i <= mid && j <= right) {
            if (arr[i] <= arr[j]) {
                temp[t] = arr[i];
                t++;
                i++;
            } else {
                temp[t] = arr[j];
                t++;
                j++;
            }
        }

        //将有剩余数据的一边全部存入temp中
        while (i <= mid) {//左边序列有剩余元素
            temp[t] = arr[i];
            t++;
            i++;
        }
        while (j <= right) {//右边序列有剩余元素
            temp[t] = arr[j];
            t++;
            j++;
        }

        /**
         * 将temp中的元素拷贝到arr中
         *  注意:不是每次都拷贝全部元素
         *  第一次:tempLeft=0,right=1
         *  第二次:tempLeft=2,right=3
         *  第三次:tempLeft=0,right=3
         */
        t = 0;
        int tempLeft = left;
        while (tempLeft <= right) {
            arr[tempLeft] = temp[t];
            t++;
            tempLeft++;
        }
    }
}

 时间复杂度:O(nlog2n)


 完结撒fa~

  

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值