常用排序/面试常考排序

O(n2)

冒泡

  1. 外层循环i用来控制:排序次数,需要注意:排数组长度减一次,因为最后一个不需要排。
  2. 内层循环j用来控制:每次比较到多少。需要注意:外层每循环一次,会排序好一个到最后,所以每次需要减去已经排序好的元素i,由于我们是用j和j+1比较,所以arr.length还需要减一
public void sort(int[] arr) {
    // 排数组长度减一次,因为最后一个不需要排
    for (int i = 0; i < arr.length - 1; i++) {
        // 每次排序
        for (int j = 0; j < arr.length - 1 - i; j++) {
            if (arr[j] > arr[j + 1]) {
                int tmp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = tmp;
            }
        }
    }
}

插入

每次划分为左右两部分,左边表示已经排序好的部分,右边表示没有排序好的部分。
每次从已经排序好的最右边比较,这样可以一次移动元素。

/**
     * 每次划分为左右两部分,左边表示已经排序好的部分,右边表示没有排序好的部分。
     * 每次从已经排序好的最右边比较,这样可以一次移动元素。
     * @param arr
     */
public void sort(int[] arr) {
    for (int i = 0; i < arr.length; i++) {
        // 当前的值
        int key = arr[i];
        // 已经排序好的末尾
        int idx = i - 1;

        while (idx >= 0 && arr[idx] > key) {
            // 把大的移动到后面,因为我们用key记住了当前的值,所以没事
            arr[idx + 1] = arr[idx];
            idx--;
        }

        // 放到它该放的位置
        arr[idx + 1] = key;
    }
}

O(n * log(n))

快速

每次把数组分为两部分,分别对这两部分进行排序。

public void sort(int[] arr) {
    int s = 0, e = arr.length - 1;

    sort(arr, s, e);
}

private void sort(int[] arr, int s, int e) {
    if (s < e) {
        // 中间点左右都排序好了
        int mid = partition(arr, s, e);
        // 分别把中间点的左右部分都进行排序
        sort(arr, s, mid);
        sort(arr, mid + 1, e);
    }
}

private int partition(int[] arr, int s, int e) {
    // 基准值
    int pivot = arr[s];
    int i = s, j = e;

    while (i < j) {
        // 从右向左找比当前基准值小的数
        while (i < j && arr[j] >= pivot) {
            j--;
        }
        
        if (i < j) {
            arr[i++] = arr[j];
        }

        while (i < j && arr[i] <= pivot) {
            i++;
        }
        if (i < j) {
            arr[j--] = arr[i];
        }
    }

    arr[i] = pivot;
    return i;
}

归并

把待排序数组分为左右两部分,再分

最后合并

public void sort(int[] arr) {
    partition(arr, 0, arr.length - 1);
}

private void partition(int[] arr, int s, int e) {
    if (s < e) {
        int m = s + (e - s) / 2;
        partition(arr, s, m);
        partition(arr, m + 1, e);
        merge(arr, s, m, e);
    }
}
// 合并两个有序数组
private void merge(int[] arr, int s, int m, int e) {
    // [1,2,3]传来的s和e是0和2,那么需要创建 e-s+1 长度的数组
    int[] res = new int[e - s + 1];
    int resIdx = 0;

    // i 从 s 到 m,j 从 m+1 到 e
    int i = s, j = m + 1;
    while (i <= m && j <= e) {
        if (arr[i] <= arr[j]) {
            res[resIdx++] = arr[i++];
        } else {
            res[resIdx++] = arr[j++];
        }
    }

    while (i <= m) {
        res[resIdx++] = arr[i++];
    }
    while (j <= e) {
        res[resIdx++] = arr[j++];
    }

    int arrIdx = s;
    for (int k = 0; k < resIdx; k++) {
        arr[arrIdx++] = res[k];
    }
}

堆排

思路:

  1. 把最大的元素递到最上面,在这个过程中,所有的子模块都具有相同的性质,也就是所有子模块的最大值就是它的顶点。比如如下的图
    https://pic2.zhimg.com/80/v2-6db33bd4ddb7937ca5946283ef2acc5d_1440w.webp
    构建完毕应该是如下,每个小三角区域,都是最大的在最上面。
    在这里插入图片描述
    这样,在后续比较时,可以用上目前比较的结果。

  2. 后续把最大的元素和队尾元素交换,从顶部重新调整以保持堆的结构,也就是想让所有模块的最大都在最上面。那就从顶部把每个元素进行堆化调整。

为什么要从顶部开始?
因为之前已经确定,最大的在最上面的三角,所以,最上面的三角区域就能找到最大的,然后再把最大的模块【左或右】部分再进行调整,保证其子结构还是符合,所有子模块的最大值都在最上面。就能用于下次比较了。

为什么快?
因为每次只需要调整一个方向上的元素。【左或右】

建堆,每次把0和末尾元素交换,把剩下的再堆化调整

public void sort(int[] arr) {
    sort(arr, 0, arr.length - 1);
}

private void sort(int[] arr, int s, int e) {
	// 建立堆
    buildHeap(arr, s, e);
    int n = e;
    for (int i = e; i >= s; i--) {
    	// 把已经排序好的元素移到末尾
        swap(arr, s, n);
        // 重新堆化调整
        heapify(arr, s, s, --n);
    }
}

private void buildHeap(int[] arr, int s, int e) {
    for (int i = e / 2; i >= s; i--) {
        heapify(arr, i, s, e);
    }
}

private void heapify(int[] arr, int i, int s, int e) {
    int maxIdx = i;
    int left = i * 2 + 1;
    int right = i * 2 + 2;

    if (left >= s && left <= e
        && arr[left] > arr[maxIdx]) {
        maxIdx = left;
    }
    if (right >= s && right <= e
        && arr[right] > arr[maxIdx]) {
        maxIdx = right;
    }

    if (maxIdx != i) {
        swap(arr, maxIdx, i);
        heapify(arr, maxIdx, s, e);
    }
}

private void swap(int[] arr, int i, int j) {
    int tmp = arr[i];
    arr[i] = arr[j];
    arr[j] = tmp;
}

文章部分图片来源
https://zhuanlan.zhihu.com/p/124885051

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值