十大排序算法之7种

虽然网上有很多排序算法的代码了,但是我还是想根据所学将自己的想法展示出来。

1.冒泡排序

class Solution {
    public static void bubbleSort(int[] nums) {
        for (int i = 0; i < nums.length; i++) {
            for (int j = 0; j < nums.length - 1 - i; j++) {
                if (nums[j] > nums[j + 1]) {
                    int temp = nums[j];
                    nums[j] = nums[j + 1];
                    nums[j + 1] = temp;
                }
            }
        }
    }

    public static void main(String[] args) {
        int[] nums = {3,4,8,12,4,7,56,34};
        bubbleSort(nums);
        for (int num : nums) {
            System.out.println(num);
        }
    }
}

这是稳定的排序算法,时间复杂度为O(n^2),因为没有使用额外空间,所以空间复杂度为O(1)。

2.选择排序

//遍历数组,找到最大(最小)的元素,将它与数组中第一个元素进行交换,然后继续找,再和第二个元素进行交换...

class Solution {
    public static void selectionSort(int[] nums) {
        //遍历到的最小元素的下标
        int minIndex;
        for (int i = 0; i < nums.length; i++) {
            minIndex = i;
            for (int j = i + 1; j < nums.length; j++) {
                if (nums[minIndex] > nums[j]) {
                    minIndex = j;
                }
            }
            int temp = nums[i];
            nums[i] = nums[minIndex];
            nums[minIndex] = temp;
        }
    }

    public static void main(String[] args) {
        int[] nums = {3, 4, 8, 12, 4, 7, 56, 34};
        selectionSort(nums);
        for (int num : nums) {
            System.out.println(num);
        }
    }
}

这是不稳定的排序算法,时间复杂度为O(n^2),因为没有使用额外空间,所以空间复杂度为O(1)。

3.插入排序

class Solution {
    public static void insertSort(int[] nums) {
        int len = nums.length;
        //要插入的数
        int insertNum;
        //默认第一个元素是有序的,所以i从1开始
        for (int i = 1; i < len; i++) {
            insertNum = nums[i];
            int j = i - 1;
            //从后向前遍历,将大于要插入的数的元素后移
            while (j >= 0 && nums[j] > insertNum) {
                nums[j + 1] = nums[j];
                j--;
            }
            nums[j + 1] = insertNum;
        }
    }

    public static void main(String[] args) {
        int[] nums = {3, 4, 8, 12, 4, 7, 56, 34};
        insertSort(nums);
        for (int num : nums) {
            System.out.println(num);
        }
    }
}

这是稳定的排序算法,时间复杂度为O(n^2),因为没有使用额外空间,所以空间复杂度为O(1)。

4.快速排序

实现:

快速排序是用递归实现的,首先初始化一个空数组,在原数组中选取一个基准数,然后遍历原数组中的元素,将比基准数小的元素放入空数组的前半部分,比基准数大的元素放入空数组的后半部分,最后将基准数填入空白位置,这样就将原数组分割成两部分。分别对左右部分的数组进行上述操作,最终可以得到一个有序的数组。

改进:

上述实现需要初始化一个空数组,空间复杂度较高,其实并不用初始化一个空数组,只需要使用两个指针,分别为分区指示器和遍历指示器即可。

实现是这样的:

  1. 在数组中选取一个元素作为基准数,将其与数组尾元素进行交换,分区指示器初始指向的下标为数组开始下标-1,遍历指示器初始指向的下标为数组开始下标。

  2. 遍历指示器开始遍历数组,如果当前元素比小于等于基准数,则分区指示器右移一位,移动后如果当前元素下标大于分区指示器的下标,那么分区指示器指向的元素与当前元素进行交换;如果当前元素大于基准数,则分区指示器不变。

  3. 遍历结束之后,就会发现基准数将原来的数组分割成左右两部分,左半部分的元素比基准数小,右半部分的元素比基准数大,左右两侧的数组重复上述操作,最终能够得到一个有序数组。

class Solution {
    private static void sortArray(int[] nums) {
        quickSort(nums, 0, nums.length - 1);
    }

    public static void quickSort(int[] array, int start, int end) {
        if (array.length < 1 || start < 0 || end >= array.length || start > end)
            return;
        //数据被分割成独立的两部分时,从哪分区的指示器
        int zoneIndex = partition(array, start, end);
        if (zoneIndex > start)
            quickSort(array, start, zoneIndex - 1);
        if (zoneIndex < end)
            quickSort(array, zoneIndex + 1, end);
    }
    
    private static int partition(int[] array, int start, int end) {
        //只有一个元素时,无需再分区
        if (start == end) return start;
        //随机选取一个基准数
        int pivot = (int) (start + Math.random() * (end - start + 1));
        //zoneIndex是分区指示器,初始值为分区头元素下标减一
        int zoneIndex = start - 1;
        //将基准数和分区尾元素交换位置
        swap(array, pivot, end);
        for (int i = start; i <= end; i++) {
            //当前元素小于基准数
            if (array[i] <= array[end]) {
                //首先分区指示器累加
                zoneIndex++;
                //当前元素在分区指示器的右边时,交换当前元素和分区指示器元素
                if (i > zoneIndex)
                    swap(array, i, zoneIndex);
            }
        }
        return zoneIndex;
    }

    private static void swap(int[] array, int i, int j) {
        int temp = array[i];
        array[i] = array[j];
        array[j] = temp;
    }

    public static void main(String[] args) {
        int[] nums = {23, 53454, 6456, 4233, 232, 45, 2334};
        sortArray(nums);
        for (int num : nums) {
            System.out.println(num);
        }
    }
}

这是不稳定的排序算法,时间复杂度为O(nlogn ),因为使用到了递归,时间复杂度为O(logn)。

5.希尔排序

又称为缩小增量排序。

class Solution {
    private static void shellSort(int[] nums) {
        if (nums == null) return;
        int len = nums.length;
        /*按增量分组后,每个分组中,currentValue代表当前待排序数据,该元素之前的组内数据均已被排序过*/
       /*gap指用来分组的增量,会依次递减*/
        int currentValue, gap = len / 2;
        while (gap > 0) {
            for (int i = gap; i < len; i++) {
                currentValue = nums[i];
                /*组内已被排序数据的索引*/
                int preIndex = i - gap;
                /*在组内已被排序过的数据中倒序寻找合适的位置,如果当前待排序数据比比较的元素要小,
                则将比较的元素在组内后移一位*/
                while (preIndex >= 0 && nums[preIndex] > currentValue) {
                    nums[preIndex + gap] = nums[preIndex];
                    preIndex -= gap;
                }
                /*while循环结束时,说明已经找到了当前待排序数据的合适位置,插入*/
                nums[preIndex + gap] = currentValue;
            }
            gap /= 2;
        }
    }


    public static void main(String[] args) {
        int[] nums = {23, 53454, 6456, 4233, 232, 45, 2334};
        shellSort(nums);
        for (int num : nums) {
            System.out.println(num);
        }
    }
}

 这是不稳定的排序算法,时间复杂度为O(nlogn),空间复杂度为O(1)。

6.堆排序

这里以升序为例:

利用完全二叉树的性质排序一个数组,对于任意一个根节点 i ,2*i+1为左子树节点,2 *(i+1)为右子树节点。通过不断调整,使之成为一个最大堆,将数组中第一个元素和尾元素进行交换,同时数组长度-1(剥离排序好的元素),接着再次递归调整堆,使之再次成为一个最大堆,再进行交换...,直到全部排序好为止。

class Solution {
    private static int len;

    private static void sortArray(int[] nums) {
        len = nums.length;
        if (len < 1) return;//这里也是保证程序的健壮性
        /*1.构建一个最大堆*/
        buildMaxHeap(nums);
        /*2.循环将堆首位(最大值)与未排序数据末位交换,然后重新调整为最大堆*/
        while (len > 0) {
            swap(nums, 0, len - 1);
            len--;
            adjustHeap(nums, 0);
        }
    }

    /**
     * 建立最大堆
     */
    private static void buildMaxHeap(int[] array) {
        /*从最后一个非叶子结点开始向上构造最大堆*/
        for (int i = (len / 2 - 1); i >= 0; i--) {
            adjustHeap(array, i);
        }
    }

    /**
     * 调整使之成为最大堆
     */
    private static void adjustHeap(int[] array, int i) {
        int maxIndex = i;
        int left = 2 * i + 1;
        int right = 2 * (i + 1);
        /*如果有左子树,且左子树大于父节点,则将最大指针指向左子树*/
        if (left < len && array[left] > array[maxIndex])
            maxIndex = left;
        /*如果有右子树,且右子树大于父节点且大于左子树,则将最大指针指向右子树*/
        if (right < len && array[right] > array[maxIndex] && array[right] > array[left])
            maxIndex = right;
        if (maxIndex != i) {
            swap(array, maxIndex, i);
            adjustHeap(array, maxIndex);
        }
    }

    private static void swap(int[] array, int i, int j) {
        int temp = array[i];
        array[i] = array[j];
        array[j] = temp;
    }


    public static void main(String[] args) {
        int[] nums = {23, 53454, 6456, 4233, 232, 45, 2334};
        sortArray(nums);
        for (int num : nums) {
            System.out.println(num);
        }
    }
}

这是不稳定的排序算法,时间复杂度为O(nlogn),空间复杂度为O(1)。 

7.归并排序

        使用的是分治法的思想,先将一个数组一分为二,然后继续对拆开的数组进行拆分,直到每个组只有一个元素为止;再把相邻的数组进行合并,合并的过程中进行排序,合并完成也就排好序了。         

class Solution {
    public static void mergeSort(int[] nums) {
        sortSelection(nums, 0, nums.length - 1);
    }

    private static void sortSelection(int[] nums, int start, int end) {
        if (start == end)  //只有一个元素时,停止再分
            return;
        int mid = (start + end) / 2;
        sortSelection(nums, start, mid);
        sortSelection(nums, mid + 1, end);
        merge(nums, start, mid + 1, end);
    }

    private static void merge(int[] nums, int start, int start1, int end) {
        int len = start1 - start;//左边数组的长度
        int[] temp = new int[len];
        System.arraycopy(nums, start, temp, 0, len);

        int p1 = 0, p2 = start1;
        for (int i = start; i <= end; i++) {
            if (temp[p1] < nums[p2]) {
                nums[i] = temp[p1];
                p1++;
                if (p1 == len) //左半部分的数组已经排序完了
                    break;
            } else {
                nums[i] = nums[p2];
                p2++;
                if (p2 > end) {
                    while (p1 < len) {  // 这里应该先++i,因为索引为i的位置已经被赋值了,这是是把左边部分数组的内容直接赋值到nums中
                        nums[++i] = temp[p1++];
                    }
                }
            }
        }
    }

    public static void main(String[] args) {
        int[] nums = new int[50];
        Random random = new Random();
        for (int i = 0; i < 50; i++) {
            nums[i] = random.nextInt(100);
        }
        mergeSort(nums);
        for (int num : nums) {
            System.out.print(num + "\t");
        }
    }
}

这是稳定的排序算法,时间复杂度为O(nlogn),因为使用了额外数组,空间复杂度为O(n)。

总结:

冒泡排序、插入排序、归并排序是稳定的排序算法。

选择排序、希尔排序、快速排序和堆排序是不稳定的排序算法。

  • 38
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Python 中的十大排序算法包括冒泡排序、选择排序、快速排序、归并排序、堆排序、插入排序、希尔排序、计数排序、桶排序和基数排序。 冒泡排序通过比较相邻的元素并交换它们的位置来排序。选择排序通过选择最小的元素并将其放在已排序的序列的末尾来排序。快速排序使用分治法将列表分成较小的子列表,并通过递归地排序子列表来完成排序。归并排序将列表分成较小的子列表,并通过递归地排序子列表,然后将它们合并在一起来完成排序。堆排序使用堆数据结构来排序元素。插入排序通过将未排序的元素插入已排序的序列中来排序。希尔排序是插入排序的一变体,通过将元素分成较小的子序列并分别进行插入排序来排序。计数排序通过计算每个元素的出现次数,然后按照计数的顺序重构列表来排序。桶排序将元素分配到不同的桶中,然后对每个桶进行排序,最后将桶中的元素合并在一起来完成排序。基数排序根据元素的位数进行排序,从最低位开始依次排序,最终得到有序列表。以上是 Python 中的十大排序算法。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [用Python实现十大经典排序算法](https://blog.csdn.net/lemonbit/article/details/121738385)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值