数据结构与算法(九)排序

排序算法比较

算法时间复杂度(平均)时间复杂度(最坏)时间复杂度(最好)空间复杂度稳定性
冒泡排序O(n^2)O(n^2)O(n)O(1)稳定
选择排序O(n^2)O(n^2)O(n^2)O(1)不稳定
插入排序O(n^2)O(n^2)O(n)O(1)稳定
希尔排序O(nlogn)O(n^2)O(n)O(1)不稳定
快速排序O(nlogn)O(n^2)O(nlogn)O(logn)不稳定
归并排序O(nlogn)O(nlogn)O(nlogn)O(n)稳定
桶(基数)排序O(n * r)O(n * r)O(n * r)O(n * r)稳定

冒泡排序

每遍历一次,将当前遍历的所有元素的最大值置于最后。即第一次遍历将数组中所有元素的最大值置于倒数第一位,第二次遍历(除开最后一位)剩余所有的数,将其中的最大值置于倒数第二位。以此类推。。。

public static void bubble(int[] arr) {
        int temp = 0;
        for (int i = 0; i < arr.length - 1; i++) {
            boolean flag = false;// 作为标记,如果一次遍历中没有任何交换,则数组已经有序,不需要再进行遍历
            for (int j = 0; j < arr.length - 1 - i; j++) {

                if (arr[j] > arr[j + 1]) {
                    flag = true;
                    temp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = temp;
                }
            }
            if (!flag) {
                break;
            }
        }
    }

选择排序

每遍历一次,将当前遍历的所有元素的最小值和首位进行交换。即第一次遍历将数组中所有元素的最小值和数组第一位进行交换,第二次遍历(除开第一位)剩余所有的数,将其中的最小值和第二位进行交换。以此类推。。。

public static void select(int[] arr) {
        for (int i = 0; i < arr.length - 1; i++) {
            int minIndex = i;
            int min = arr[i];
            //遍历找到最小值
            for (int j = i + 1; j < arr.length - 1; j++) {
                if (min > arr[j]) {
                    min = arr[j];
                    minIndex = j;
                }
            }

            if (minIndex != i) {
                arr[minIndex] = arr[i];
                arr[i] = min;
            }
        }
    }

插入排序

将数组分为两个部分,前半部分有序,依次遍历后半部分,在有序部分中找到合适的位置插入

public static void insert(int[] arr) {
        int insertIndex = 0;
        int insertVal = 0;
        for (int i = 1; i < arr.length; i++) {
            insertVal = arr[i];
            insertIndex = i;
//            for (insertIndex = i - 1; insertIndex >= 0; insertIndex--) {
//                if (arr[insertIndex] > insertVal) {
//                    arr[insertIndex + 1] = arr[insertIndex];//将比插入值大的数向后移
//                } else {
//                    break;
//                }
//            }
//
//            insertIndex++;
            while (insertIndex > 0 && arr[insertIndex - 1] > insertVal) {
                arr[insertIndex] = arr[insertIndex - 1];
                insertIndex--;
            }

            if (insertIndex != i) {
                arr[insertIndex] = insertVal;
            }
        }
    }

希尔排序

希尔排序是插入排序的一种优化,在简单插入排序中,如果出现很小的数在数组后端,需要后移的操作数量很多。为了减少这种移动,所以有了希尔算法。
希尔排序的核心思想是:
将数组按照一定的增量分组,对每组进行简单插入排序,逐渐减少增量至1,即对整个数组进行一次简单插入,得到最后的有序数组。

public static void shell(int[] arr) {
        for (int step = arr.length / 2; step > 0; step = step / 2) {
            for (int i = step; i < arr.length; i++) {
                int insertVal = arr[i];
                int insertIndex = i;

                while (insertIndex > step - 1 && arr[insertIndex - step] > insertVal) {
                    arr[insertIndex] = arr[insertIndex - step];
                    insertIndex -= step;
                }

                if (insertIndex != i) {
                    arr[insertIndex] = insertVal;
                }

            }
        }
    }

快速排序

快速排序是找到一个基准数,将比基准数小的数全部放到左边,将比基准数大的数全部放到右边。然后依次递归排序左右子数组。

public static void quick(int[] arr, int left, int right) {
        if (left >= right) {
            return;
        }

        //取中间的值为基准值
        int pivotVal = arr[(left + right) / 2];
        //采用双指针遍历数组
        int i = left;
        int j = right;

        int temp = 0;
        while (j > i) {//当左右指针相遇, 即遍历完整个数组, 退出

            while (arr[i] < pivotVal) {//当前数小于基准值且在左边, 不需要移位
                i++;
            }

            while (arr[j] > pivotVal) {//当前值大于基准值且在右边,不需要移位
                j--;
            }

            if (i >= j) {//当左右指针相遇, 即遍历完整个数组, 退出
                break;
            }

            if (i != j) {
                temp = arr[i];
                arr[i] = arr[j];
                arr[j] = temp;
            }

            if (arr[i] == pivotVal) {//防止死循环
                j--;
            }

            if (arr[j] == pivotVal) {//防止死循环
                i++;
            }
        }

        if (i == j) {//防止死循环
            i++;
            j--;
        }

        //遍历左边数组
        quick(arr, left, j);
        //遍历右边数组
        quick(arr, i, right);
    }

归并排序

归并排序是将数组划分为两个部分,一直分解到每个数组只有一个数字。接着开始合并,每次将两个子数组合并为一个有序的数组.
比如将【2,1,3,4】会被分解为【2】、【1】、【3】、【4】,接着将【2】、【1】合并为【1,2】,【3】、【4】合并为【3,4】,然后将【1,2】,【3,4】合并为【1,2,3,4】。

public static void merge(int[] arr, int left, int right) {
        if (left < right) {
            int mid = (left + right) / 2;
            merge(arr, left, mid);
            merge(arr, mid + 1, right);

            //开始合并
            //1. 遍历左右数组,将数字一从小到大的顺序填充到临时数组中, 直到某一边数组填充完毕
            int i = left;//左边数组的初始位置
            int j = mid + 1;//右边数组的初始位置
            int[] temp = new int[right - left + 1];
            int tempIndex = 0;
            while (i <= mid && j <= right) {
                if (arr[i] <= arr[j]) {
                    temp[tempIndex] = arr[i];
                    i++;
                    tempIndex++;
                } else {
                    temp[tempIndex] = arr[j];
                    j++;
                    tempIndex++;
                }
            }

            //2.将另一边剩余的数字依次填充到临时数组中
            while (i <= mid) {
                temp[tempIndex] = arr[i];
                i++;
                tempIndex++;
            }
            while (j <= right) {
                temp[tempIndex] = arr[j];
                j++;
                tempIndex++;
            }

            //3.将临时数组copy到原来的数组中
            tempIndex = 0;
            i = left;
            while (tempIndex < temp.length) {
                arr[i] = temp[tempIndex];
                i++;
                tempIndex++;
            }


        }
    }

桶排序

桶排序(基数排序),将所有数字变成同样的数位长度(即数位较短的数前面补零)例如【1,100】变为【001,100】
从低位开始,依次进行一次排列,当最高位排完就得到了有序序列
说明:桶排序非常占用内存,如果排列的数组过大,容易造成内存溢出,另外不支持负数的排序

public static void bucket(int[] arr) {
        //定义十个桶,占用内存。。。。
        int[][] bucket = new int[10][arr.length];
        //记录每个桶的下标,即存储的数字个数
        int[] bucketElementsCount = new int[10];
        //找到数组的最大值
        int max = arr[0];
        for (int i = 0; i < arr.length; i++) {
            if (arr[i] > max) {
                max = arr[i];
            }

        }
        int maxLength = (max + "").length();
        //第一次遍历数字的个位数,第二次是十位数,以此类推。。。
        for (int i = 0, n = 1; i < maxLength; i++, n *= 10) {
            //将数字放入对应的同种
            for (int j = 0; j < arr.length; j++) {
                int digit = arr[j] / n % 10;
                bucket[digit][bucketElementsCount[digit]] = arr[j];
                bucketElementsCount[digit]++;
            }

            //遍历桶,将桶中的元素依次放回原数组
            int index = 0;
            for (int j = 0; j < bucketElementsCount.length; j++) {
                for (int k = 0; k < bucketElementsCount[j]; k++) {
                    arr[index] = bucket[j][k];
                    index++;
                }
                bucketElementsCount[j] = 0;
            }


        }
    }

堆排序

将数组设计成大顶堆或者小顶堆,将堆顶位置的元素与数组末端的元素交换得到有序数组
大顶堆原理:大顶堆要求父节点的值大于等于左子树的值,又大于等于右子树的值。
小顶堆原理:小顶堆要求父节点的值小于等于左子树的值,又小于等于右子树的值。

 public static void heap(int[] arr) {
        //找到当前的第一个非叶子节点
        int i = (arr.length - 1) / 2;
        //从该节点从下往上,右至左创建调整堆
        for (; i >= 0; i--) {
            adjustHeap(arr, i, arr.length);
        }

        int temp = 0;
        for (int j = arr.length - 1; j > 0; j--) {
            //交换堆顶元素和末尾元素
            temp = arr[j];
            arr[j] = arr[0];
            arr[0] = temp;

            //重新构建大顶堆
            adjustHeap(arr, 0, j);
        }
    }

    private static void adjustHeap(int[] arr, int i, int length) {
        int temp = arr[i]; //保留当前节点
        int maxIndex = 0;
        int lchildIndex = 2 * i + 1;//当前节点的左子节点的下标
        int rchildIndex = 2 * i + 2;//当前节点的右子节点的下标
        while (lchildIndex < length) {
            maxIndex = lchildIndex;
            if (rchildIndex < length && arr[lchildIndex] < arr[rchildIndex]) {
                //左子节点小于右子节点
                maxIndex = rchildIndex;
            }

            if (temp >= arr[maxIndex]) {
                //当前节点比子节点都大, 退出
                break;
            }

            arr[i] = arr[maxIndex];

            //继续调整当前节点的左子节点
            i = maxIndex;
            lchildIndex = 2 * i + 1;
            rchildIndex = 2 * i + 2;

        }
        arr[i] = temp;

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值