排序算法

排序算法

常用排序算法的理解及示例代码:冒泡排序、插入排序、选择排序、归并排序、快速排序、二叉树排序

分析算法

  1. 内存消耗:是否是原地排序算法。指空间复杂度为 O(1) 的算法
  2. 稳定性:是否是稳定的排序算法。等值元素排序后次序不变,为稳定的排序算法
  3. 执行效率:时间复杂度,最好、最坏、平均或均摊

冒泡排序

相邻元素根据大小交换位置,每次遍历将一个元素放到合适的位置上

  1. 是否原地排序:原地排序
  2. 是否稳定:控制相邻元素大小相等时不做交换,则为稳定排序
  3. 时间复杂度:\(O(n^2)\)
  4. 设置标志位,优化算法,避免已经有序后还在做无用循环
public static void bubble(int[] a) {
    for (int i = 0; i < a.length - 1; i++) {
        boolean traverse = true;//是否还需要遍历的标志
        for (int j = 0; j < a.length - i - 1; j++) {
            if (a[j] > a[j + 1]) {
                traverse = false;
                int temp = a[j];
                a[j] = a[j + 1];
                a[j + 1] = temp;
            }
            if (traverse) break;
        }
    }
}

插入排序

总是取无序区第一个元素,有序区由大向小与该元素比较,比该元素大则后移一位,最终让出合适位置

  1. 是否原地排序:原地排序
  2. 是否稳定:控制后面出现的元素插入到前面相同元素的后面,则为稳定排序
  3. 时间复杂度:\(O(n^2)\)
public static void insert(int[] a) {
    for (int i = 0; i < a.length - 1; i++) {
        int x = a[i + 1];
        int j = i;
        for (; j >= 0; j--) {
            if (a[j] > x) a[j + 1] = a[j];//比 x 大后移一位
            else break;//找到合适位置
        }
        a[j + 1] = x;//循环中多减了一次 1
    }
}

选择排序

依次遍历每个位置,与后续元素比较,比该位小的换到该位

  1. 是否原地排序:原地排序
  2. 是否稳定:不是稳定排序
  3. 时间复杂度:\(O(n^2)\)
public static void select(int[] a) {
    for (int i = 0; i < a.length - 1; i++) {
        for (int j = i + 1; j < a.length; j++) {
            if (a[j] < a[i]) {
                int temp = a[j];
                a[j] = a[i];
                a[i] = temp;
            }
        }
    }
}

归并排序

原数组分成两部分,递归得到 length 个数组,每个数组中一个元素

两两数组合并,递归得出排序结果

  1. 是否原地排序:非原地排序,空间复杂度较高,\(O(n)\)
  2. 是否稳定:稳定
  3. 时间复杂度:\(O(nlogn)\)
public static void sort(int[] a, int left, int right) {
    if (left >= right) return;
    int mid = left + (right - left >> 2);//取中间元素
    sort(a, left, mid);
    sort(a, mid + 1, right);
    merge(a, left, mid, right);
}

public static void merge(int[] a, int left, int mid, int right) {
    int[] temp = new int[right - left + 1];
    int l = left, r = mid + 1, count = 0;
    // 比较两部分放入大小,小元素放入数组
    while (l <= mid && r < right) {
        temp[count++] = a[l] < a[r] ? a[l++] : a[r++];
    }
    // 两部分个数不一样时,将长的部分剩余元素加入数组
    while (l > mid && r <= right) temp[count++] = a[r++];
    while (l <= mid && r > left) temp[count++] = a[l++];
    // 复制给原数组
    for (int i = 0; i < temp.length; i++) {
        a[left + i] = temp[i];
    }
}

快速排序

设置头尾指针,选取一个元素作为中点

先从尾指针向前找比中点小的,移到头指针,头指针后移一位

再从头指针向后找比中点大的,移到尾指针,尾指针前移一位

指针相遇时,得到中点位置,保证了该位置左边元素小,右边元素大

递归执行头结点到中点 -1、中点 +1 到尾结点两段元素的排序

  1. 是否原地排序:原地排序
  2. 是否稳定:不稳定
  3. 时间复杂度:\(O(nlogn)\)
public static void quick(int[] a, int left, int right) {
    if (left >= right) return;
    int i = left, j = right;
    int x = a[left];
    while (i < j) {
        while (i < j && a[j] >= x) j--;
        if (i < j) a[i++] = a[j];
        while (i < j && a[i] <= x) i++;
        if (i < j) a[j--] = a[i];
    }
    a[i] = x;
    quick(a, left, i - 1);
    quick(a, i + 1, right);
}

二叉树排序

左小右大生成二叉树,中序遍历得出的结果有序

  1. 是否原地排序:非原地排序
  2. 是否稳定:控制相等时放在右结点,则为稳定排序
  3. 时间复杂度:\(O(n)\)
public static void treeSort(int[] a) {
    TreeNode tree = new TreeNode();
    for (int i : a) {//生成树
        tree.add(i);
    }
    List list = tree.inOrder();//中序遍历
    for (int i = 0; i < list.size(); i++) {
        a[i] = list.get(i);//装回数组 
    }
}

static class TreeNode {//静态内部类,可在静态方法中实例化
    TreeNode left, right;
    Object value;
	// 左小右大生成二叉树树
    public void add(Object o) {
        if (value == null) {
            value = o;
        } else {
            if ((int) o < (int) value) {
                left = (left == null) ? new TreeNode() : left;
                left.add(o);
            } else {
                right = (right == null) ? new TreeNode() : right;
                right.add(o);
            }
        }
    }
	// 中序遍历得出的结果有序	
    public List inOrder() {
        List list = new ArrayList<>();
        if (left != null) list.addAll(left.inOrder());
        list.add((int) value);
        if (right != null) list.addAll(right.inOrder());
        return list;
    }
}

你可能感兴趣的:(排序算法)