十大排序

(平均/最好/最坏)时间复杂度、空间复杂度、稳定性

未标题-1

注意:main方法测试调用统一提取出来,按照需求自己打开/关闭注释去调用

    public static void main(String[] args) {
        //随机数个数
        int number = 1000;
        //随机数最大值
        int maxNum = 1000;
        //随机数最小值
        int minNum = 1;
        Random random = new Random();
        Integer[] arr = new Integer[number];
        for (int i = 0; i < number; i++) {
            arr[i] = random.nextInt(maxNum - minNum) + minNum;
        }
        //输出排序前的样子
        System.out.println(Arrays.toString(arr));
        long startTime = System.currentTimeMillis();


        //调用排序方法,主函数的其他代码无关紧要,主要是为了生成数据,打印结果
        BubbleSort.bubbleSort(arr);
        //SelectSort.selectSort(arr);
        //InsertSort.insertSort(arr);
        //ShellSort.shellSort(arr);
        //QuickSort.quickSort(arr);
        //MergeSort.mergeSort(arr);


        long endTIme = System.currentTimeMillis();
        //输出排序后的样子
        System.out.println(Arrays.toString(arr));
        //输出排序用时,生成数据时间不算,结果保留两位小数
        System.out.println((endTIme - startTime) / 1000.0 + "秒");
    }

注意:以下实现方法不唯一,只是给出相对于好的实现方式。

一、冒泡排序(Bubble Sort)

解释:前一个和后一个相比,如果前一个大于后一个,那么他们互换,每次循环找出一个最大的。

在这里插入图片描述

//冒泡排序
public class BubbleSort {
    public static void bubbleSort(Integer[] arr) {
        Integer t;
        //例:arr.length=5,那么要执行4 3 2 1,总共四次
        for (int i = arr.length - 1; i > 0; i--) {
            /**
             * 这个的作用就是说如果在一轮比较中没有发生对换的情况,
             * 那么代表数组在这个时刻已经是有序的了,所以直接退出比较
             */
            boolean flag = true;

            //第一次应该循环0~4,第二次就循环0~3,以此类推
            for (int j = 0; j < i; j++) {
                //如果前一个大于后一个,那么他们对换
                if (arr[j] > arr[j + 1]) {
                    t = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = t;
                    flag = false;
                }
            }
            if (flag) {
                break;
            }
        }
    }
}

二、选择排序(Select Sort)

解释:每次循环中找到一个最小的,记录它的下标,然后对调。

img

//选择排序
public class SelectSort {
    public static void selectSort(Integer[] arr) {
        Integer t;
        for (int i = 0; i < arr.length - 1; i++) {
            //把当前数组中的第i个当做最小的
            int index = i;
            for (int j = i + 1; j < arr.length; j++) {
                //如果arr[j] < arr[index],记录更小的下标j
                if (arr[j] < arr[index]) {
                    //在满足上面条件后,我们应该记录当前最小的下标
                    index = j;
                }
            }
            //最后执行完本次循环后会得到arr中未完成排序部分最小的下标index,让他们交换
            t = arr[i];
            arr[i] = arr[index];
            arr[index] = t;
        }
    }
}

三、插入排序(Insert Sort)

解释:将无序序列中的元素逐个插入到有序序列中,直到整个序列有序(一开始把第一个元素当做有序序列,其他后面元素是无序序列)

在这里插入图片描述

//插入排序
public class InsertSort {
    public static void insertSort(Integer[] arr) {
        Integer t;
        for (int i = 0; i < arr.length - 1; i++) {
            //拿出无序序列的第一个元素,去遍历整个有序序列进行比较
            for (int j = i + 1; j > 0; j--) {
                //如果arr[j]小于arr[j-1]他们对换
                if (arr[j] < arr[j - 1]) {
                    t = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = t;
                } else {
                    //当arr[j-1]小于arr[j]时,代表当前元素已经插入到有序序列的合适位置,退出当前循环进行下一个元素排序
                    break;
                }
            }
        }
    }
}

四、希尔排序(Shell Sort)

解释:基于插入排序的排序算法,通过比较一定间隔(增量)的元素来进行插入排序,不断缩小增量直至为1,最终完成排序。

//计算首次合适的一定间隔h的方法
int h=1;
while (h< arr.length/2){
    h=2*h+1;
}
//例:arr的长度为10,那么一开始h等于7
//那按照h等于7的情况来看,希尔排序完成,它要走的间隔h分别为 7 3 1,因为每次执行一轮排序就会进行h=h/2

在这里插入图片描述

//希尔排序
public class ShellSort {
    public static void shellSort(Integer[] arr) {
        Integer t;
        //计算首次合适的一定间隔h的
        int h = 1;
        while (h < arr.length / 2) {
            h = 2 * h + 1;
        }
        while (h >= 1) {
            /**这就是根据间隔h计算本次运行次数,不会就记代码吧
             * 例arr.length=10,h=7是循环3次
             * 例arr.length=10,h=3是循环7次
             * 例arr.length=10,h=1是循环9次
             */
            for (int i = 0; i < arr.length - h; i++) {
                //这边要注意间隔是从arr后面往前取
                for (int j = i + h; j >= h; j -= h) {
                    if (arr[j] < arr[j - h]) {
                        t = arr[j - h];
                        arr[j - h] = arr[j];
                        arr[j] = t;
                    } else {
                        break;
                    }
                }
            }
            h = h / 2;
        }
    }
}

五、快速排序(Quick Sort)

解释:选取一个基准值(pivot),将序列分为小于和大于基准值的两部分,并对这两部分分别进行递归排序,最终完成排序。

213170918_10_20210116035831682

//快速排序,实现方法有很多,这是其中一种,但是原理是一样的
public class QuickSort {
    public static void quickSort(Integer[] arr) {
        //获取arr的左右下标,然后调用开始排序
        int l = 0;
        int r = arr.length - 1;
        quickSort(arr, l, r);
    }

    public static void quickSort(Integer[] arr, int l, int r) {
        if (l >= r) {
            return;
        }
        //进行排序
        int partition = partition(arr, l, r);
        //左分区
        quickSort(arr, l, partition - 1);
        //又分区
        quickSort(arr, partition + 1, r);

    }

    public static int partition(Integer[] arr, int l, int r) {
        //记录基准下标,默认左边第一个
        int leftBaseIndex = l;
        Integer t;
        while (l < r) {
            //要先右边在左边,顺序不能错
            //右边是找出大于arr[leftBaseIndex]的
            while (l < r && arr[r] > arr[leftBaseIndex]) {
                --r;
            }
            //左边是找出小于arr[leftBaseIndex]的
            while (l < r && arr[l] <= arr[leftBaseIndex]) {
                ++l;
            }
            if (l < r) {
                //交换
                t = arr[l];
                arr[l] = arr[r];
                arr[r] = t;
            }
        }
        t = arr[leftBaseIndex];
        arr[leftBaseIndex] = arr[r];
        arr[r] = t;

        return r;
    }

}

六、归并排序(Merge Sort)

解释:将序列不断划分为更小的子序列并对子序列进行排序,最后将子序列合并成一个有序序列,完成排序。

//获得中间位置
mid = l + (r - l) / 2

img

//归并排序
public class MergeSort {
    public static void mergeSort(Integer[] arr) {
        Integer[] assist = new Integer[arr.length];
        int l = 0;
        int r = arr.length - 1;
        mergeSort(arr, l, r, assist);
    }

    public static void mergeSort(Integer[] arr, int l, int r, Integer[] assist) {
        if (l >= r) {
            return;
        }
        int mid = l + (r - l) / 2;
        mergeSort(arr, l, mid, assist);
        mergeSort(arr, mid + 1, r, assist);
        merge(arr, l, mid, r, assist);
    }

    public static void merge(Integer[] arr, int l, int mid, int r, Integer[] assist) {
        int i = l, p1 = l, p2 = mid + 1;
        //把当前排序的左右两边的数据从小到大放到assist中
        while (p1 <= mid && p2 <= r) {
            if (arr[p1] < arr[p2]) {
                assist[i++] = arr[p1++];
            } else {
                assist[i++] = arr[p2++];
            }
        }
        //有可能是左边放完了,右边没放完
        while (p1 <= mid) {
            assist[i++] = arr[p1++];
        }
        //有可能是又边放完了,右边没放完
        while (p2 <= r) {
            assist[i++] = arr[p2++];
        }
        //将当前有序的数组返回arr
        if (r + 1 - l >= 0) System.arraycopy(assist, l, arr, l, r + 1 - l);
    }
}

注意:从这边开始,以下代码是偷来的,还没看,不知道有没有错,等有空再看再更新。。。。

七、堆排序(Heap Sort)

解释:将待排序序列构建成最大堆或最小堆,不断将堆顶元素取出并调整堆结构,最终得到一个有序序列。

在这里插入图片描述

public class HeapSort {
    public static void main(String[] args) {
		int a[]={3,44,38,5,47,15,36,26,27,2,46,4,19,50,48};	
		sort(a);
		System.out.println(Arrays.toString(a));
	}
    public static void sort(int[] arr) {
        int length = arr.length;
        //构建堆
        buildHeap(arr,length);
        for ( int i = length - 1; i > 0; i-- ) {
            //将堆顶元素与末位元素调换
            int temp = arr[0];
            arr[0] = arr[i];
            arr[i] = temp;
            //数组长度-1 隐藏堆尾元素
            length--;
            //将堆顶元素下沉 目的是将最大的元素浮到堆顶来
            sink(arr, 0,length);
        }
    }
    private static void buildHeap(int[] arr, int length) {
        for (int i = length / 2; i >= 0; i--) {
            sink(arr,i, length);
        }
    }
    
    private static void sink(int[] arr, int index, int length) {
        int leftChild = 2 * index + 1;//左子节点下标
        int rightChild = 2 * index + 2;//右子节点下标
        int present = index;//要调整的节点下标

        //下沉左边
        if (leftChild < length && arr[leftChild] > arr[present]) {
            present = leftChild;
        }

        //下沉右边
        if (rightChild < length && arr[rightChild] > arr[present]) {
            present = rightChild;
        }

        //如果下标不相等 证明调换过了
        if (present != index) {
            //交换值
            int temp = arr[index];
            arr[index] = arr[present];
            arr[present] = temp;

            //继续下沉
            sink(arr, present, length);
        }
    }
}

八、计数排序 (Count Sort)

解释:统计序列中每个元素的出现次数,将元素映射到有序的桶中,最后按照桶的顺序输出元素,完成排序。

img

public class CountSort {
	public static void main(String[] args) {
		int[] array = { 4, 2, 2, 8, 3, 3, 1 };
		// 找到数组中最大的值 ---> max:8
		int max = findMaxElement(array);
		int[] sortedArr = countingSort(array, max + 1);
		System.out.println("计数排序后的数组: " + Arrays.toString(sortedArr));
	}
	private static int findMaxElement(int[] array) {
		int max = array[0];
		for (int val : array) {
			if (val > max)
				max = val;
		}
		return max;
	}
	private static int[] countingSort(int[] array, int range) { //range:8+1
		int[] output = new int[array.length]; 
		int[] count = new int[range];
		//初始化: count1数组
		for (int i = 0; i < array.length; i++) {
			count[array[i]]++;
		}
		//计数: count2数组,累加次数后的,这里用count2区分
		for (int i = 1; i < range; i++) {
			count[i] = count[i] + count[i - 1];
		}
		//排序:最后数组
		for (int i = 0; i < array.length; i++) {
			output[count[array[i]] - 1] = array[i];
			count[array[i]]--;
		}
		return output;
	}
}

九、桶排序(Bucket Sort)

解释:将待排序序列均匀地分到若干个桶中,对每个桶中的元素进行排序,最后按照桶的顺序输出所有元素,完成排序。

在这里插入图片描述

public class BucketSort {
    public static void sort(int[] arr){
        //最大最小值
        int max = arr[0];
        int min = arr[0];
        int length = arr.length;

        for(int i=1; i<length; i++) {
            if(arr[i] > max) {
                max = arr[i];
            } else if(arr[i] < min) {
                min = arr[i];
            }
        }

        //最大值和最小值的差
        int diff = max - min;

        //桶列表
        ArrayList<ArrayList<Integer>> bucketList = new ArrayList<>();
        for(int i = 0; i < length; i++){
            bucketList.add(new ArrayList<>());
        }

        //每个桶的存数区间
        float section = (float) diff / (float) (length - 1);

        //数据入桶
        for(int i = 0; i < length; i++){
            //当前数除以区间得出存放桶的位置 减1后得出桶的下标
            int num = (int) (arr[i] / section) - 1;
            if(num < 0){
                num = 0;
            }
            bucketList.get(num).add(arr[i]);
        }

        //桶内排序
        for(int i = 0; i < bucketList.size(); i++){
            //jdk的排序速度当然信得过
            Collections.sort(bucketList.get(i));
        }

        //写入原数组
        int index = 0;
        for(ArrayList<Integer> arrayList : bucketList){
            for(int value : arrayList){
                arr[index] = value;
                index++;
            }
        }
    }
}

十、基数排序(Raix Sort)

解释:将待排序序列按照每一位数字的大小关系进行排序,从低位到高位进行多次排序,最终得到一个有序序列。

img

import java.util.Arrays;
public class RaixSort {
	public static void main(String[] args) {
		int[] arr = { 53, 3, 542, 748, 14, 214 };

		// 得到数组中最大的数
		int max = arr[0];// 假设第一个数就是数组中的最大数
		for (int i = 1; i < arr.length; i++) {
			if (arr[i] > max) {
				max = arr[i];
			}
		}
		// 得到最大数是几位数
		// 通过拼接一个空串将其变为字符串进而求得字符串的长度,即为位数
		int maxLength = (max + "").length();

		// 定义一个二维数组,模拟桶,每个桶就是一个一维数组
		// 为了防止放入数据的时候桶溢出,我们应该尽量将桶的容量设置得大一些
		int[][] bucket = new int[10][arr.length];
		// 记录每个桶中实际存放的元素个数
		// 定义一个一维数组来记录每个桶中每次放入的元素个数
		int[] bucketElementCounts = new int[10];

		// 通过变量n帮助取出元素位数上的数
		for (int i = 0, n = 1; i < maxLength; i++, n *= 10) {
			for (int j = 0; j < arr.length; j++) {
				// 针对每个元素的位数进行处理
				int digitOfElement = arr[j] / n % 10;
				// 将元素放入对应的桶中
				// bucketElementCounts[digitOfElement]就是桶中的元素个数,初始为0,放在第一位
				bucket[digitOfElement][bucketElementCounts[digitOfElement]] = arr[j];
				// 将桶中的元素个数++
				// 这样接下来的元素就可以排在前面的元素后面
				bucketElementCounts[digitOfElement]++;
			}
			// 按照桶的顺序取出数据并放回原数组
			int index = 0;
			for (int k = 0; k < bucket.length; k++) {
				// 如果桶中有数据,才取出放回原数组
				if (bucketElementCounts[k] != 0) {
					// 说明桶中有数据,对该桶进行遍历
					for (int l = 0; l < bucketElementCounts[k]; l++) {
						// 取出元素放回原数组
						arr[index++] = bucket[k][l];
					}
				}
				// 每轮处理后,需要将每个bucketElementCounts[k]置0
				bucketElementCounts[k] = 0;
			}
		}
		System.out.println(Arrays.toString(arr));//[3, 14, 53, 214, 542, 748]
	}
}
posted @ 2023-03-05 22:47  Airgity  阅读(32)  评论(0编辑  收藏  举报