算法的六种常见排序

1 . 冒泡排序( Bubble Sort )

在遍历数组时判断 当前位置的 数组大小(a[ j ])是否比下一位(a[ j + 1 ])大,如果是,则将

a[ j ] 与 a[ j + 1 ]数值交换,即把大的数往后放,就像是冒泡一样,大的数一直后移,直到数组有序。

在此进行了优化,添加了flag判断本次循环是否进行了交换,如未进行交换则说明排序完毕,跳出循环,减少了可能出现的已经排好序但循环没有结束,继续运行的情况;

当前数组 :6    1    5    3    4

内循环第一遍                   1    6    5    3    4

内循环第一遍                   1    5    6    3    4

内循环第一遍                   1    5    3    4    6

下一次内循环

内循环第一遍                   1    3    5    4    6

内循环第二遍                   1    3    4    5    6

内循环第三遍                   1    3    4    5    6

此时未进行交换,flag = 0,跳出循环,无需再进行下一次内循环

冒泡排序的时间复杂度是O(n^2)的

import java.util.Scanner;

public class BubbleSort {
    public static void main(String[] args) {
        int n;
        Scanner scanner = new Scanner(System.in);
        n = scanner.nextInt();
        int[] a = new int[n];
        for (int i = 0; i < n; i++) {
            a[i] = scanner.nextInt();
        }
        bubble(a, n);
    }

    public static void bubble(int[] a, int n) {
        int x;
        int flag = 0;//这句可以不写
        x = a[0];
        for (int i = 0; i < n - 1; i++) {
            for (int j = 0; j < n - i - 1; j++) {
                if (a[j] >a[j  + 1]) {
                    flag++;
                    x = a[j];
                    a[j] = a[j + 1];
                    a[j + 1] = x;
                }
            }
            if (flag == 0) {//可以不写
                break;
            } else
                flag = 0;
        }
        for (int i = 0; i < n; i++) {
            System.out.print(a[i] + " ");
        }
    }
}

2 . 插入排序 ( Insert Sort )

插入排序运用到了链表,将数据存储在链表中,我在写的时候先在头节点后插入了两个节点,这算是一个bug,不能输入两个以下的数。

在输入时,遍历链表,将新建的链表节点按顺序插入链表中:

此时链表中数值:1     2    5    6    9

新输入的值:3

遍历链表,找到第一个大于三的数,在这个数之前插入新的节点:

1    2    3    5    6    9

插入完毕

如果遍历结束没有找到比输入数值大的节点,则将新建的节点插入链表末尾

import javax.xml.soap.Node;
import java.util.*;

public class InsertSort {
    public static void main(String[] args) {
        ListNode p1;
        ListNode head = new ListNode(0);
        int n, m;
        Scanner scanner = new Scanner(System.in);
        n = scanner.nextInt();
        m = scanner.nextInt();
        p1 = new ListNode(m);
        head.next = p1;
        m = scanner.nextInt();
        ListNode p2 = new ListNode(m);
        if (p2.val > p1.val) {
            insert(p1, p2);
        } else {
            insert(head, p2);
        }
        p1 = head;
        for (int i = 0; i < n - 2; i++) {
            m = scanner.nextInt();
            p2 = new ListNode(m);
            while (p1.next != null) {
                if (p2.val > p1.val && p1.next.val > p2.val) {
                    insert(p1, p2);
                    p1 = p1.next;
                    break;
                }
                if (p2.val == p1.val) {
//                    ListNode p;
//                    p = new ListNode(m);
                    insert(p1, p2);
                    p1 = p1.next;
                    break;
                }
                p1 = p1.next;
            }
            if (p2.val > p1.val)
                insert(p1, p2);
            p1 = head;
        }
        p1 = head.next;
        for (int i = 0; i < n; i++) {
            System.out.print(p1.val + " ");
            p1 = p1.next;
        }
    }

    public static void insert(ListNode p, ListNode p1) {
        ListNode p2;
        p2 = p1;
        p2.next = p.next;
        p.next = p2;

    }
}

class ListNode {
    int val;
    ListNode next;

    ListNode(int x) {
        val = x;
    }
}

3 . 快速排序 ( Quck Sort )

快速排序是较为不稳定的排序,基准选择的优秀程度直接决定了算法的时间复杂度,最优可以达到O(nlogn),最劣达到了O(n^2)。

算法思想:

选择数组中的第一个数值作为基准(target),从左往右找到大于基准(target)的数值L,再从左往右找到小于基准(target)的数值R,判断 L 是否在 R 的左侧,如果是则将L和R的数值交换,继续往后找,直到 L 的位置在 R 的右边,此时,将target的数值与找到的 比 target 小的数值 R 与target交换。

如果在 target 右侧找不到比 target 小的数,则将 target 右移;可以通过循环实现以上操作。

下图打印了快排的交换过程与每次交换的 target 和交换数组的下标。

快排的实际上是给每一个target对应的数值找一个“家”,即找出target对应的值在数组中对应的位置,他是数组中第一小的值,就给他找第一个位置,即a[0],它是第五小的值,就把它放在a[4],快排就是这样一个过程。

利用递归可以实现这一点,我们按照part方法为target找到位置的下标进行分割,将它左边和右边分为两次递归,代码如下:

import java.util.Scanner;

public class QuckSort {
    public static void main(String[] args) {
        int n;
        Scanner scanner = new Scanner(System.in);
        n = scanner.nextInt();
        int[] a = new int[n];
        for (int i = 0; i < n; i++) {
            a[i] = scanner.nextInt();
        }
        qucksort(a, 0, n - 1);
        for (int i = 0; i < n; i++) {
            System.out.print(a[i] + " ");
        }
    }

    public static int part(int a[], int p, int r) {
        int i = p, j = r + 1;
        int target = a[p];
        //System.out.println("target="+x);
        while (true) {
            while (a[++i] < target && i < r) ;
            while (a[--j] > target) ;
            if (i >= j)
                break;
            //System.out.println("swap:a["+i+"],a["+j+"]");
            int y = a[i];
            a[i] = a[j];
            a[j] = y;
        }
        //System.out.println("swap:a["+p+"],a["+j+"]");
        a[p] = a[j];
        a[j] = target;
/*        for(int value : a){
            System.out.print(value+" ");
        }
        System.out.println();*/
        return j;
    }

    public static void qucksort(int a[], int p, int r) {
        if (p < r) {
            int q = part(a, p, r);
            qucksort(a, p, q - 1);
            qucksort(a, q + 1, r);
        }
    }
}
/*
10
6 1 5 3 4 7 10 2 15 9
 */

4 . 插入排序( Insert Sort)

插入排序就是每次遍历数组,取出其中最小值,放到新数组中的一种排序方法

每次找到数组当前的最小值,将其放到新数组中,并在原来数组中将其更新为极大值或者非法数值,例如 6 1 5 3 4 ----> 取出1 后:6 9999 5 3 4,或者 6 -1 5 3 4;

代码如下:

import java.util.Scanner;

public class SelectSort {
    public static void main(String[] args) {
        int n;
        Scanner scanner = new Scanner(System.in);
        n = scanner.nextInt();
        int[] array = new int[n];
        for (int i = 0; i < n; i++) {
            array[i] = scanner.nextInt();
        }
        array = sort(array, n);

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

    public static int select(int[] a, int n) {
        int m, t = 0;
        m = 999999;
        for (int i = 0; i < n; i++) {
            if (m > a[i]) {
                m = a[i];
                t = i;
            }
        }
        return t;
    }

    public static int[] sort(int a[], int n) {
        int[] array = new int[n];
        for (int i = 0; i < n; i++) {
            array[i] = a[select(a, n)];
            a[select(a, n)] = 999999;
        }
        return array;
    }

}

5 . 希尔排序 ( Shell Sort )

希尔排序是从大佬的博客里学会的,链接:

https://www.cnblogs.com/chengxiao/p/6104371.html

算法思想是将数组按照增量(一般为数组长度的一半作为初始增量)分组,将每组进行插入排序,然后将增量缩短为原来的一半,将分组减少,使数组逐渐有序化;

直到增量为1,数组化为一组数据时,继续运行,操作类似于冒泡排序遍历一遍,将数组数值微调即可获得排好序的数组;

希尔排序的时间复杂度为O(n^(1.3—2))

偷来的图:

import java.util.Scanner;

public class ShellSort {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n;
        n = scanner.nextInt();
        int[] nums = new int[n];
        for (int i = 0; i < n; i++) {
            nums[i] = scanner.nextInt();
        }
        sort(nums);
        for (int i = 0; i < n; i++) {
            System.out.print(nums[i] + " ");
        }
        /*sort1(nums);
        for (int i = 0; i < n; i++) {
            System.out.print(nums[i]+" ");
        }*/
    }

    public static void sort(int[] nums) {
        //增量growth,并逐步缩小增量
        for (int growth = nums.length / 2; growth > 0; growth /= 2) {
            //从第growth个元素,逐个对其所在组进行直接插入排序操作
            for (int i = growth; i < nums.length; i++) {
                int j = i;
                while (j - growth >= 0 && nums[j] < nums[j - growth]) {
                    //插入排序采用交换法
                    swap(nums, j, j - growth);
                    j -= growth;
                }
            }
        }
    }
/*
    public static void sort1(int[] nums) {//shell分组
        int growth = nums.length / 2;
        while (growth > 0) {
            insert(nums, growth);
            growth /= 2;
        }
    }

    public static void insert(int[] nums, int growth) {//插入排序
        for (int i = growth; i < nums.length; i++) {
            int j = i;
            while (j - growth >= 0 && nums[j] < nums[j - growth]) {
                //插入排序采用交换法
                swap(nums, j, j - growth);
                j -= growth;
            }
        }
    }*/

    public static void swap(int[] nums, int l, int r) {//交换数组
        int x = nums[l];
        nums[l] = nums[r];
        nums[r] = x;
    }
}

6 . 归并排序 ( Merge Sort )

        归并排序和堆排序是可以稳定达到O(nlogn)时间复杂度的排序方法,但代码复杂程度较高

算法思想:

        归并排序是将数组一分为二,再分为四,直到每个元素都各自为一组,不能再分时,将所有元素有序合并,将完全拆分的数组再重新组合成有序的数组的一个过程。

通常我们用递归的方法实现归并排序。

图片来自百度百科

import java.util.Scanner;

public class MergeSort {
    public static void main(String[] args) {
        int n;
        Scanner scanner = new Scanner(System.in);
        int[][] a = new int[1000][2];
        int[][] b = new int[1000][2];
        n = scanner.nextInt();
        for (int i = 0; i < n; i++) {
            a[i][0] = scanner.nextInt();
            a[i][1] = i;
        }
        mergeSort(a, 0, n - 1, b);
        for (int i = 0; i < n; i++) {
            System.out.print(b[i][0] + " ");
        }
        System.out.println();
    }

    public static void Merge(int a[][], int b[][], int l, int m, int r) {
        int k = 0;
        int p1 = l, p2 = m + 1;
        while (p1 <= m && p2 <= r) {
            if (a[p1][0] < a[p2][0]) {
                b[k++][0] = a[p1++][0];
                b[k][1] = a[p1][1];
            } else {
                b[k++][0] = a[p2++][0];
                b[k][1] = a[p2][1];
            }
        }
        while (p1 <= m) {
            b[k++][0] = a[p1++][0];
            b[k][1] = a[p1][1];
        }
        while (p2 <= r) {
            b[k++][0] = a[p2++][0];
            b[k][1] = a[p2][1];
        }
        for (int i = 0; i < r - l + 1; i++) {
            a[l + i][0] = b[i][0];
        }
    }

    public static void mergeSort(int a[][], int left, int right, int b[][]) {
        if (left < right) {
            int i = left + (right - left) / 2;
            mergeSort(a, left, i, b);
            mergeSort(a, i + 1, right, b);
            Merge(a, b, left, i, right);
        }
    }
}

上述六种排序各有优劣,

其中 冒泡排序,插入排序,选择排序 实现较为简单,但时间复杂度较高

快速排序,希尔排序 实现复杂且 耗费时间不稳定

归并排序,堆排序的时间复杂度较低,但是代码非常复杂,不容易实现

堆排序是本人学习的漏洞,会单独写一个文章

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值