首页 > 编程语言 >【算法 Java】排序

【算法 Java】排序

时间:2024-07-31 11:51:30浏览次数:9  
标签:arr Java int len ++ 算法 排序 public

排序

所有的排序以从小到大排序为例
模板题:牛客-简单的排序

排序算法的稳定性体现在相同数值元素的相对位置会不会随排序改变,如果改变则不稳定,如果不改变则稳定

冒泡排序

比较相邻的元素。如果第一个比第二个大,就交换他们两个。越大的元素会经由交换慢慢"浮"到数列的末端。

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

  • 稳定的排序算法
BubbleSort.java
import java.util.Scanner;

public class BubbleSort {
    public static void main(String[] args) {

        Scanner sc = new Scanner(System.in);
        
        int len = sc.nextInt();
        int[] arr = new int[len];

        for (int i = 0; i < len; ++ i ) {
            arr[i] = sc.nextInt();
        }

        bubbleSort(arr, len);

        for (int i = 0; i < len; ++ i ) {
            System.out.print(arr[i]);
            System.out.print(i == len - 1 ? '\n' : ' ');
        }

    }

    /**
     * 冒泡排序
     * @param a:待排序的数组
     * @param len:数组中有效数值的个数
     */
    public static void bubbleSort(int[] a, int len) {
        for(int i = 0; i < len - 1; ++ i ) { // 比较轮数:len-1;每轮比较最大的值会冒泡到数列最后,因此每轮比较次数是递减的
            for(int j = 0; j < len - 1 - i; ++ j ) { // 每轮比较次数:len-1-i
                if(a[j] > a[j + 1]) {
                    swap(a, j, j + 1);
                }
            }
        }
    }

    /**
     * 交换数值:交换a[i]和a[j]的数值
     * 一定要注意判断是不是同一个变量,如果是同一个变量再用异或交换变量值会变为0
     */
    public static void swap(int[] a, int i, int j) {
        if(i == j) return;
        a[i] = a[i] ^ a[j];
        a[j] = a[i] ^ a[j];
        a[i] = a[i] ^ a[j];
    }

}

选择排序

从0号索引开始,拿着每一个索引上的元素和之后的元素依次比较。每个索引比较结束之后,该索引上的值就是排好序的元素。

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

  • 不稳定的排序算法
SelectSort.java
import java.util.Scanner;

public class SelectSort {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int len = sc.nextInt();
        int[] a = new int[len];
        for(int i = 0; i < len; ++ i ) {
            a[i] = sc.nextInt();
        }
        selectSort(a, len);
        for(int i = 0; i < len; ++ i ) {
            System.out.print(a[i] + (i == len - 1 ? "\n" : " "));
        }
    }

    public static void selectSort(int[] a, int len) {
        for(int i = 0; i < len - 1; ++ i ) { // 比较轮数
            for(int j = i + 1; j < len; ++ j ) { // 每一轮:比较a[i]及其索引之后的元素
                if(a[i] > a[j]) {
                    swap(a, i, j);
                }
            }
        }
    }

    public static void swap(int[] a, int i, int j) {
        if(i == j) return;
        a[i] = a[i] ^ a[j];
        a[j] = a[i] ^ a[j];
        a[i] = a[i] ^ a[j];
    }
}

插入排序

现在要插入a[i],已知a[0,i-1]已经按照升序排好,现在将a[i]从a[i-1]开始往前依次比较,找到第一个比a[i]小的数,然后将a[i]插入到这个数后面。

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

  • 稳定的排序算法
insertSort
public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        int[] arr = new int[n];
        for(int i = 0; i < n; ++ i ) {
            arr[i] = in.nextInt();
        }
        insertSort(arr, n);
        for(int i = 0; i < n; ++ i ) {
            System.out.print(arr[i]);
            System.out.print(i == n - 1 ? '\n' : ' ');
        }
    }

    public static void insertSort(int[] arr, int len) {
        for(int i = 1; i < len; ++ i ) {
            int tmp = arr[i];
            int j = i;
            while(j > 0 && tmp < arr[j - 1]) {
                arr[j] = arr[j - 1];
                -- j;
            }
            if(j != i) {
                arr[j] = tmp;
            }
        }
    }
}

希尔排序

插入排序的改进算法,算法图解
初始增量: gap=length/2
缩小增量:gap = gap/2
根据增量对原数组分组,对于每一个分组采用插入排序进行排序,直至增量为1,则排序完成。

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

  • 不稳定的排序算法
shellSort
public static void shellSort(int[] arr, int len) {
        for(int gap = len >> 1; gap >= 1; gap >>= 1) {
            for(int i = gap; i < len; ++ i ) {
                int tmp = arr[i];
                int j = i;
                while(j - gap >= 0 && arr[j - gap] > tmp) {
                    arr[j] = arr[j - gap];
                    j -= gap;
                }
                if(i != j) {
                    arr[j] = tmp;
                }
            }
        }
    }

快速排序

期望(平均)时间复杂度/最优时间复杂度:O(nlogn)->分界值每次都取到序列的中位数

最差时间复杂度O(n^2)->分界值每次都取到最小值或最大值,退化为选择排序(实际上,等概率随机取分界值,每次都取到边缘数值的概率很小,所以几乎不可能达到最差情况)

  • 不稳定的排序算法

三路快速排序

  1. 随机选取分界点pivot
  2. 将待排数列划分为三个部分:小于分界点的区间[l, lit)、等于分界点的区间[lit, rit) 以及大于分界点的区间[rit, r)
  3. 再排序小于分界点区间和大于分界点区间
  • 不稳定的排序算法

三路快速排序在处理含有多个重复值的数组时,效率远高于原始快速排序。其最佳时间复杂度为 O(n)。

quickSort
import java.util.*;

public class Main {

    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        int[] arr = new int[n];
        for(int i = 0; i < n; ++ i ) {
            arr[i] = in.nextInt();
        }
        quickSort(arr, 0, n);
        for(int i = 0; i < n; ++ i ) {
            System.out.print(arr[i]);
            System.out.print(i == n - 1 ? '\n' : ' ');
        }
    }

    public static void quickSort(int[] a, int l, int r) {
        if(l == r) return;
        Random random = new Random();
        int x = l + random.nextInt(r - l);
        int pivot = a[x];
        int i = l, lit = l, rit = r;
        while(i < rit) {
            if(a[i] < pivot) {
                swap(a, i ++ , lit ++ );
            } else if(a[i] > pivot) {
                swap(a, i, -- rit);
            } else {
                ++ i;
            }
        }
        quickSort(a, l, lit);
        quickSort(a, rit, r);
    }

    public static void swap(int[] a, int i, int j) {
        if(i == j) return;
        a[i] = a[i] ^ a[j];
        a[j] = a[i] ^ a[j];
        a[i] = a[i] ^ a[j];
    }

}

归并排序

时间复杂度:O(nlogn)
划分区间一共有logn层,而每一层合并时每一个元素都会遍历到一次,所以是nlogn

  • 稳定的排序算法
mergeSort
import java.util.*;


public class Main {
    private static int[] tmp;

    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        int[] arr = new int[n];
        for(int i = 0; i < n; ++ i ) {
            arr[i] = in.nextInt();
        }
        tmp = new int[n];
        mergeSort(arr, 0, n - 1);
        for(int i = 0; i < n; ++ i ) {
            System.out.print(arr[i]);
            System.out.print(i == n - 1 ? '\n' : ' ');
        }
    }

    public static void mergeSort(int[] arr, int l, int r) { // [l, r]
        if(l == r) return;
        int mid = l + ((r - l) >> 1);
        mergeSort(arr, l, mid);
        mergeSort(arr, mid + 1, r);
        merge(arr, l, mid, r);
    }
    
    public static void merge(int[] arr, int l, int mid, int r) {// [l, mid] [mid + 1, r]
        int i = l, j = mid + 1, k = l;
        while(i <= mid && j <= r) {
            if(arr[i] <= arr[j]) tmp[k ++ ] = arr[i ++ ]; // 这里必须是小于等于,不然回影响算法的稳定性
            else tmp[k ++ ] = arr[j ++ ];
        }
        while(i <= mid) tmp[k ++ ] = arr[i ++ ];
        while(j <= r) tmp[k ++ ] = arr[j ++ ];
        for(int id = l; id <= r; ++ id ) arr[id] = tmp[id];
    }
}

堆排序

  • 不稳定的排序算法
  • 时间复杂度O(nlogn)

升序排序:维护大根堆,取堆顶(最大值)置数组末尾选择排序
降序排序:维护小根堆,取堆顶(最小值)置数组末尾选择排序

heapSort
import java.util.*;

public class Main{
    static int n;
    static int[] a;
    
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        n = in.nextInt();
        a = new int[n];
        for(int i = 0; i < n; ++ i ) {
            a[i] = in.nextInt();
        }
        heapSort();
        for(int i = 0; i < n; ++ i ) {
            System.out.print(a[i]);
            System.out.print(i == n - 1 ? "\n" : " ");
        }
    }
    
    public static void heapSort() {
        // 从【最后一个叶子节点的父亲节点】开始往前遍历,完成数组的(大根)堆化
        for(int i = (n - 1 - 1) >> 1; i >= 0; -- i) {
            sift(i, n - 1);
        }
        // 将堆顶固定在数组末尾(选择排序),将剩余堆维持堆的特性
        for(int i = n - 1; i > 0; -- i) {
            swap(i, 0);
            sift(0, i - 1);
        }
    }
    
    // 将数组子区间[st, ed]进行(大根)堆化
    public static void sift(int st, int ed) {
        int rt = st;
        int son = rt << 1 | 1;
        while(son <= ed) {
            // 取根节点值更大的子节点(默认左儿子,如果右儿子更大则更换)
            if(son + 1 <= ed && a[son + 1] > a[son]) ++ son;
            // 如果根节点值小于子节点,则交换
            if(a[rt] < a[son]) swap(rt, son);
            else return; // 否则堆化完成
            // 继续往下堆化子树
            rt = son;
            son = rt << 1 | 1;
        }
    }
    
    public static void swap(int i, int j) {
        if(i == j) return ;
        a[i] = a[i] ^ a[j];
        a[j] = a[i] ^ a[j];
        a[i] = a[i] ^ a[j];
    }
}

标签:arr,Java,int,len,++,算法,排序,public
From: https://www.cnblogs.com/Eve7Xu/p/17869459.html

相关文章

  • 【JAVA】TestNG 开源测试框架
    创建maven项目https://www.cnblogs.com/phoenixy/p/16850747.htmlpom.xml<?xmlversion="1.0"encoding="UTF-8"?><projectxmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSche......
  • 代码随想录算法训练营第53天 | 图论2:岛屿数量相关问题
    99.岛屿数量https://kamacoder.com/problempage.php?pid=1171岛屿深搜https://www.programmercarl.com/kamacoder/0099.岛屿的数量深搜.html岛屿广搜https://www.programmercarl.com/kamacoder/0099.岛屿的数量广搜.html#思路100.岛屿的最大面积https://www.programmercar......
  • 三种语言实现差分(C++/Python/Java)
    题目输入一个长度为n的整数序列。接下来输入m个操作,每个操作包含三个整数l,r,c表示将序列中[l,r]之间的每个数加上c。请你输出进行完所有操作后的序列。输入格式第一行包含两个整数n和m。第二行包含n个整数,表示整数序列。接下来m行,每行包含三个整数l,r,c,表......
  • (10-2-02)智能行为决策算法:常用的智能行为决策算法(2)-------强化学习
    10.2.2 强化学习强化学习是一种机器学习方法,其核心思想是通过代理程序与环境的交互学习最优行为策略,以最大化累积奖励。在强化学习中,代理程序通过观察环境的状态,并选择动作来影响环境,从而学习如何在面对不同状态时做出最优的决策。和强化学习相关的关键概念包括:环境与代理......
  • JavaScript 中的浅拷贝和深拷贝
    目录浅拷贝定义特点示例使用场景实现方法深拷贝定义特点示例使用场景实现方法浅拷贝定义浅拷贝是指仅复制对象的第一层属性。如果对象的属性是基本类型(如字符串、数字、布尔值),则会复制这些值;如果属性是引用类型(如对象、数组),则只会复制指向这些对象的引用,而不......
  • 三种语言实现二维前缀和(C++/Python/Java)
    题目输入一个n行m列的整数矩阵,再输入q个询问,每个询问包含四个整数x1,y1,x2,y2表示一个子矩阵的左上角坐标和右下角坐标。对于每个询问输出子矩阵中所有数的和。输入格式第一行包含三个整数n,m,q接下来n行,每行包含m个整数,表示整数矩阵。接下来q行,每行包含四个整数......
  • Java中数据类型的转换
    数据类型的转换目录数据类型的转换隐式类型转换显式类型转换隐式类型转换隐式类型转换也叫做自动类型转换。规则从存储范围小的类型到存储范围大的类型。转换方向byte→short(char)→int→long→float→double(这里指的是只有前面的数据类型能随便转换成后面的)—实际开发......
  • 数学建模--拟合算法
    目录拟合与插值的区别常用的拟合算法应用实例总结最小二乘法在不同数据分布下的性能表现如何?傅里叶级数拟合在图像处理中的应用案例有哪些?贝叶斯估计法与最大似然估计法在参数估计中的优缺点分别是什么?最大似然估计法(MLE)优点:缺点:贝叶斯估计法(BayesianEstimation)优......
  • 基于java jsp ssm医院预约挂号管理系统
    前言......
  • 基于java jsp ssm医院人事档案排班,打卡,试用期,请假离职工资管理系统
    前言......