八大基本排序算法(附JAVA源码)

八大基础排序

冒泡排序

对相邻的元素,不断进行比较,就像小泡泡一样,向上浮动。

for(int i = 0; i < n; i++)
    for(int j = i + 1; j < n; j ++)
        if(a[i] > a[j]){// swap
            int temp = a[i];
            a[i] = a[j];
            a[j] = temp;
        }

冒泡的一个小优化:

for(int i = 0; i < n; i++){
    boolean flag = true; 
    for(int j = i + 1; j < n; j ++){
        if(a[i] > a[j]){
            int temp = a[i];
            a[i] = a[j];
            a[j] = temp;
            flag = false;
        }
    }
    if(flag)break;// 如果此轮都没有交换,则代表已排好序,退出。
}

插入排序

将数组分为两个部分,一部分是排好序的,一部分是未排好序的,从未排序中选一个元素,插入到排序的部分,插入后依然有序。

// 两个for
for (int i = 1; i < n; i++) {
    int cur = a[i],j;
    for(j = i - 1; j >= 0 && a[j] > cur; j --)a[j + 1] = a[j];// 先往后挪,然后再插入
    a[j + 1] = cur;
}
// for + while
for (int i = 1; i < n; i++) {
    int cur = a[i],j = i - 1;
    while(j >= 0 && a[j] > cur){
        a[j + 1] = a[j];
        j--;
    }
    a[j + 1] = cur;
}

选择排序

找到最小的元素插在第0位置,找到第二小的元素插在第1位置,以此类推;

for (int i = 0; i < n; i++) {
    int minIndex = i;
    for(int j = i + 1; j < n; j++){
        if(a[j] < a[minIndex])minIndex = j; // 寻找最小值的下标
    }
    // swap()
    int temp = a[i];
    a[i] = a[minIndex];
    a[minIndex] = temp;
}

快速排序

快排思路:选择一个基数,每次遍历将大于基数的数放在右边,小于的放在左边,然后对此基数的左右两边做重复的操作。

时间复杂度分析:每次从i==j处分开,分开期望值为(1/2)故平均时间复杂度O(nlogn),若每次都在 I=j =left或者i=j=right(两边分开)相当于双重循环,最差时间复杂度O(n^2)最优就是每次都在中间分开,复杂度为O(nlogn);

//yxc模板:
public static void quickSort(int[] q, int l, int r){
    if(l >= r) return ;
    int x = q[l + r >> 1];
    int i = l - 1, j = r + 1; //正是先变换坐标,再进行判断,故i = l - 1, j = r + 1
    while(i < j){
        do i++; while(q[i] < x);//每次都先进行坐标变换,导致最后i==j+1
        do j--; while(q[j] > x);//两个wehile判断均没有=号,会在一开始就移动基准数。正是由于第一次便移动的基数,故
        if(i < j){
            int t = q[i];
            q[i] = q[j];
            q[j] = t;
        }
    }// i == j + 1;
    quickSort(q,l,j);
    quickSort(q,j + 1,r);
}

此快排注意点:当基数选择在左边时,必须是右哨兵先动,因为右哨兵停下时,一定是小于基数的数,这样能保证

//交换基数时
a[left] = a[l];
a[l] = temp;

不会将大于基数的数,与left交换。

当基数选择在最右边同理,要先动左哨兵。

public static void quickSort(int left, int right, int[] a){
        if(left >= right)return ;
        int temp = a[left];
        int l = left, r = right;
        while(l < r){
            while(l < r && a[r] > temp) r--;
            while(l < r && a[l] <= temp) l++;//必须要有这个=号的判断,因为此模板是先判断后再进行坐标的变化,不同于上面的模板。只有这样才能保证最终i==j 否则当有两个数相等时,无法跳出循环,会陷入死循环。正式由于此处的=号判断,导致其最后要将基数调换,因为基数在最初时,不会进行调换。
            
            if(l < r){ 
                int t = a[r];
                a[r] = a[l];
                a[l] = t;
            }
        }//出来时,保证了i==j,并且基数会是最终的位置。
        a[left] = a[l];
        a[l] = temp;

        quickSort(left,l-1,a);//由于基数是最终位置,故只需递归两边
        quickSort(l+1,right,a);
    }

归并排序

不断划分子数组,永远划分成一半,这样必定需要划分logN每次遍历n次,故时间复杂度为O(nlogn),且最优,最差,平均复杂度都是O(nlogn)。

private static void merge_sort(int[] q, int l, int r) {
		// 递归出调节
		if(l >= r)return ;
		//不断二分
		int mid = l + r >> 1;
		
		//递归子区间
		merge_sort(q, l, mid);
		merge_sort(q, mid + 1, r);
		
		int i = l, j = mid + 1, k = 0;
		
		// 此时分成左边数组和右边数组并且都是升序的,利用双指针将左右按升序放入额外开辟的数组temp中
		while(i <= mid && j <= r) {
			if(q[i] <= q[j])temp[k++] = q[i++];
			else temp[k++] = q[j++];
		}
		// 右边数组放完, 左边还有剩
		while(i <= mid)temp[k++] = q[i++];
		// 左边数组放完, 右边还有剩
		while(j <= r)temp[k++] = q[j++];
		//将临时数组,放入原数组中
		for(i = l,k = 0; i <= r;)q[i++] = temp[k++];
	}

堆排序

堆:分为大顶堆和小顶堆;

  1. 大顶堆:每个节点的值都大于或等于其子节点的值,在堆排序算法中用于升序排列;

  2. 小顶堆:每个节点的值都小于或等于其子节点的值,在堆排序算法中用于降序排列;

import java.util.Scanner;

public class Main {
    static Scanner sc = new Scanner(System.in);
    static int size = 0;
    static final int N = 100050;
    public static void main(String[] args) {
        int n = sc.nextInt();
        int[] heap = new int[N];

        for(int i = 1; i <= n;i ++){
            int u = sc.nextInt();
            insert(heap,u);
        }
        // 排序,将 最大的 放最后面 (大顶堆)
        for(int i = 1; i <= n; i ++){
            swap(heap,1,size);
            size --;
            push_down(heap,1);
        }


        for(int i = 1; i <= n; i ++) System.out.print(heap[i] + " ");
    }
    // 大顶堆 向上传
    private static void push_down(int[] heap,int u){
        int t = u, l_son = u * 2, r_son = u * 2 + 1;
        while(l_son <= size && heap[l_son] > heap[t])t = l_son;
        while(r_son <= size && heap[r_son] > heap[t])t = r_son;
        if(t != u){
            swap(heap,t,u);
            push_down(heap,t);
        }
    }
    
    private static void push_up(int[] heap, int u){
        while(u / 2 > 0 && heap[u / 2] < heap[u]){
            swap(heap,u / 2, u);
            u /= 2;
        }
    }

    private static void insert(int[] heap,int v){
        heap[++ size] = v;
        push_up(heap,size);
    }

    private static void remove_top(int[] heap){
        heap[1] = heap[size--];
        push_down(heap,1);
    }
    private static void swap(int[] a, int i, int j){
        int temp = a[i];
        a[i] = a[j];
        a[j] = temp;
    }
}

基数排序

基数排序是一种非比较型整数排序算法,其原理是将整数按位数切割成不同的数字,然后按每个位数分别比较。由于整数也可以表达字符串(比如名字或日期)和特定格式的浮点数,所以基数排序也不是只能使用于整数。

import java.util.ArrayList;
import java.util.Scanner;

public class Main {
    static Scanner sc = new Scanner(System.in);
    public static void main(String[] args) {
        // digit 代表最大值有多少位
        int n = sc.nextInt(), max = Integer.MIN_VALUE,digit = 0;
        int[] a = new int[n];
        for (int i = 0; i < n; i++) {
            a[i] = sc.nextInt();
            max = Math.max(max,a[i]);
        }
        while(max > 0){
            max /= 10;
            digit ++;
        }
        // 基数排序
        radix_sort(a,digit);

        for (int i = 0; i < n; i++) System.out.print(a[i] + " ");
    }

    /**
     *  从低位往高位依次排序,将所有位数的排序结果都排过后即为答案
     * @param a
     * @param digit
     */
    private static void radix_sort(int[] a,int digit){
        // cnt[0] 存在 第i位 位0 的所有数,
        ArrayList[] cnt = new ArrayList[10];
        for(int i = 0; i < 10; i ++)cnt[i] = new ArrayList<Integer>();

        for(int i = 0; i < digit; i ++){
            // 对下一位进行排序,比如 十位,则cnt里面存的是个位的排序结果,将其清空
            for(int j = 0; j < 10; j ++)cnt[j].clear();
            // 将其 按 位数 放入桶中
            for(int j = 0; j < a.length; j ++){
                cnt[get(a[j],i)].add(a[j]);
            }
            // 取出来,赋值给 原数组
            for(int j = 0, k = 0; j < 10; j ++){
                for(Object x : cnt[j]){
                    Integer t = (Integer)x;
                    a[k ++] = t;
                }
            }

        }
    }

    private static int get(int i, int i1) {
        while(i1-- > 0)i /= 10;
        return i % 10;
    }
}

希尔排序

略:

若我的讲得不够详细,可以去https://sort.hust.cc/10.radixsort;

参考学习:https://sort.hust.cc/10.radixsort + y总;

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值