排序算法真的很多,最难受的是你学完以后过了半年就很难很顺利的实现出来了,笔者都已经写过三遍了......,大一时候写了一遍,一年前开始刷题时候学了一遍,面试前还得再看一遍~~,因此本篇博客对排序算法进行了整理,面试前看一下事半功倍。排序算法一般还是结合TOPK手撕,让手写一个快排或者最小堆居多。主要还是排序思想,比如归并排序的分治和。
从稳定和不稳定来看
- 稳定:冒泡排序、选择排序、归并排序、计数排序、桶排序、基数排序。
- 不稳定:插入排序、希尔排序、堆排序、快速排序。
从时间复杂度来看,分为平均,最坏,最好。这个应该是很显然的,就是三个层次,O(n^2^)、O(nlogn)、O(n),O(n)只有满足一定条件的非比较排序算法才能满足。
附一个偷的图:
冒泡排序
- 因为排序过程形似泡泡一个一个往上冒因而得名。特点是相邻元素两两比较,这样最大值或最小值可以浮到数组的最后面。
- 它的优点是在经过优化后,对于有序或近乎有序的数组可以达到O(n)的时间复杂度。
private void bubbleSort(int[] a) {
int cache = a.length - 1; // 记录后面有序的
int j;
while (true) {
if (cache == 0) return; // 处理到最后一个元素了
j = cache;
for (int i = 0; i < j; i++) {
if (a[i] > a[i + 1]) { // 交换位置
int temp = a[i];
a[i] = a[i + 1];
a[i + 1] = temp;
cache = i;
}
}
}
}
选择排序
- 顾名思义,每轮选择一个最大或最小的元素,然后从前往后目标元素和最大值或最小值交换即可。
- 优点是简单易懂、移动次数较少、时间复杂度确定
private void selectSort(int[] a) {
for (int i = 0; i < a.length - 1; i++) { // 外层控制排序的元素
int min = i;
for (int j = i + 1; j < a.length; j++) { // 找到剩余元素的最小值
if (a[j] < a[min]) {
min = j;
}
}
if (min != i) {
// 交换元素
int temp = a[i];
a[i] = a[min];
a[min] = temp;
}
}
}
堆排序
- 堆排序是对选择排序的改进,它使用大顶堆来进行内层元素的选择。
- 优点是时间复杂度改进成了O(nlog(n))。
static class Heap {
int[] arr;
int size = 0;
public Heap(int[] arr) {
this.arr = arr;
this.size = arr.length;
heapify();
}
// 堆化
private void heapify() {
// 弗洛伊德堆化算法
int nonLeafIndex = size / 2 - 1;
for (int i = nonLeafIndex; i >= 0; i--) {
down(i);
}
}
// 取出元素
private int poll() {
if (size == 0) throw new RuntimeException();
int polled = arr[0];
// 首先交换最后一个元素和第一个元素,然后下沉即可。
swap(0, size - 1);
size--;
down(0); // 先size--,再下潜,不然之前删除的那个元素还是会参与比较
return polled;
}
// 下潜
private void down(int parent) {
int left = 2 * parent + 1;
int right = left + 1;
int max = parent;
// 比较left和right哪个值更大
if (left < size && arr[left] > arr[max]) {
max = left;
}
if (right < size && arr[right] > arr[max]) {
max = right;
}
if (max != parent) { // 如果还需要继续下潜的话
swap(max, parent);// 交换两个节点
down(max); // 继续下潜
}
}
private void swap(int curr, int parent) {
int temp = arr[curr];
arr[curr] = arr[parent];
arr[parent] = temp;
}
}
// 堆排序的思想很简单,就是把快排里面找最大值或者最小值的方法用堆实现了,这样可以达到nlog(n)的级别
private void heapSort(int[] a) {
Heap heap = new Heap(a);
for (int i = a.length - 1; i >= 0; i--) {
// 拿到最大值
int max = heap.poll();
a[i] = max; // 直接赋值,不用交换,因为原始值都存到大顶堆里面了
}
}
插入排序
- 插入排序是维护一个有序数组,在未排序数组中取出第一个元素插入到有序数组中,直至未排序数组长度为空。
- 优点是在小数据量和近乎有序情况下表现优异,常常用来优化编程语言的内置排序函数。
private void insertSort(int[] a) {
for (int i = 1; i < a.length; i++) { // 控制右侧数字
int num = a[i];
int j = i - 1;
// 右移左边数组,直到找到插入位置
while (j >= 0 && a[j] > num) { // 当前元素大于要插入的元素
a[j + 1] = a[j];
j--;
}
if (j != i - 1) { // 寻找完成后判断j是否改变,当然不判断也可以
a[j + 1] = num;
}
}
}
希尔排序
- 希尔排序是对插入排序算法的改进,改变了任意一个元素移动到它应该在的位置的步长,比如可以一次移动32步而非1步。
- 优点是将插入排序算法的时间复杂度优化到了O(nlog(n))—>O(nlog^2^(n^2^))量级
private void shellSort(int[] a) {
for (int gap = a.length >> 1; gap >= 1; gap/=2) {
// 把之前插入排序的默认1改成gap即可,左边比较的步长也是gap,初始化时候也是gap;
for (int i = gap; i < a.length; i++) {
int num = a[i], j = i - gap;
while (j >= 0 && a[j] > num) {
a[j + gap] = a[j];
j -= gap;
}
if (j != i - gap) {
a[j + gap] = num;
}
}
}
}
归并排序
- 归并排序的思想是分治合,切分到最小可治单元进行合。常常采用递归实现,称为自顶至下实现。
- 优点是最小、最坏、平均时间复杂度都是O(nlog(n))
private void mergeSort(int[] a) {
split(a, 0, a.length - 1, new int[a.length]);
}
/**
* 切分的递归方法
*
* @param a 原始数组
* @param left 切分的左索引
* @param right 切分的右索引
* @param temp 临时数组,用于合并
*/
private void split(int[] a, int left, int right, int[] temp) {
if (left == right) return; // 切分到只有一个元素了,“治”
// 不停切分,“分”
int mid = (left + right) / 2;
split(a, left, mid, temp);
split(a, mid + 1, right, temp);
// 合并两个有序数组,“合”
merge(a, left, mid, temp, mid + 1, right);
System.arraycopy(temp, left, a, left, right - left + 1); // 从left开始拷贝回去
}
/**
* 合并两个有序数组
*
* @param a 原始数组
* @param l1 数组1左索引
* @param r1 数组1右索引
* @param res 要合并到的数组
* @param l2 数组2左索引
* @param r2 数组2右索引
*/
private void merge(int[] a, int l1, int r1, int[] res, int l2, int r2) {
int p1 = l1, p2 = l2; // 双指针分别指向两个数组的起始索引
int p = l1; // 新数组的索引
// 两个数组元素比较
while (p1 <= r1 && p2 <= r2) {
if (a[p1] <= a[p2]) {
res[p++] = a[p1];
p1++;
} else {
res[p++] = a[p2];
p2++;
}
}
// 处理之前没有处理到的数组
if (p1 != r1 + 1) System.arraycopy(a, p1, res, p, r1 - p1 + 1); // 第一个数组有残留
if (p2 != r2 + 1) System.arraycopy(a, p2, res, p, r2 - p2 + 1); // 第二个数组有残留
}
快速排序
-
快速排序也是采用分治的思想,流程是每轮选择一个基准点,将比它小的元素放到左边,比他大的放到右边,紧接着继续对另外两堆进行处理,直到遍历到每堆是一个元素。
-
快排的优点是在大数据量情况下几乎是性能最优异的排序算法
-
快排有两个因为分区十分不均匀导致的缺点
- 处理反着有序的元素。时间复杂度会达到O(n^2^),使用随机基准点解决
- 处理大量重复元素,传统分区算法时间复杂度会达到O(n^2^),改写分区算法兼容重复元素
-
有一个三向切分快速排序,分成三堆可以解决上面提到的两个问题,还是改写的分区算法。
public void quickSort(int[] a) {
quickSort(a, 0, a.length - 1);
}
// 递归入口
private void quickSort(int[] a, int left, int right) {
if (left >= right) return; // left>=right
int idx = partition2(a, left, right);
quickSort(a, left, idx - 1);
quickSort(a, idx + 1, right);
}
// 基础的分区,随机基准点+双边快排
private int partition(int[] a, int left, int right) {
int idx = (int) ((Math.random() * (right - left + 1)) + left); // 生成[left,right]的随机索引
swap(a, left, idx); // 把随机值交换到前面
int pivot = a[left];
int i = left, j = right; // 左右指针,i负责找大于基准点元素的,j负责找小于基准点元素的
// 关于a[j]>pivot还是a[j]>=pivot,这个不影响,因为无论如何这个实现相等元素都会跑到一边去。但是不要都不相等。
while (i < j) {
// 不停找小于等于 基准点元素的
while (i < j && a[j] >= pivot) {
j--;
}
// 不停找大于基准点元素的
while (i < j && a[i] <= pivot) {
i++;
}
swap(a, i, j);
}
swap(a, left, i); // 这个边界条件我的建议是背下来,最后把基准点和i的元素交换
return i;
}
/*
* 改进方法。首先这次左边和右边都是遇到相等的就停下来然后交换。
* 同时更新i和j,这个时候可以把相等的元素打均匀。
* */
private int partition2(int[] a, int left, int right) {
int randomIndex = (int) ((Math.random() * (right - left + 1)) + left); // 生成[left,right]的随机索引
swap(a, randomIndex, left);
int bv = a[left]; // 每次取基准值为当前排序序列的第一个元素
// 使用双边快排实现
int i = left + 1;
int j = right;
// i小于j的时候直接开始找
while (i <= j) {
// 从左往右找,找到第一个比基准值大的元素(这里需要等于)
while (i <= j && a[i] < bv) {
i++;
}
// 从右往左找,使用while循环,直到找到一个比基准值小的或等于的元素
while (i <= j && a[j] > bv) {
j--;
}
// 兜底的判断,用于处理重复元素的情况。这里的两个一个++,一个--
if (i <= j) {
// i和j进行交换
swap(a, i, j);
i++;
j--;
}
}
// 当寻找完毕了,就把基准点换到i或者j这边
swap(a, left, j);
return j;
}
// 交换元素
private void swap(int[] a, int idx1, int idx2) {
int temp = a[idx1];
a[idx1] = a[idx2];
a[idx2] = temp;
}
三向切分快速排序
public void quickSort(int[] a) {
quickSort(a, 0, a.length - 1);
}
public static void quickSort(int[] a, int left, int right) {
// 小数据可以使用插入排序,优化性能
if (right - left <= 16) {
insertSort(a, left, right);
return;
}
// 递归出口,注意是left>=right,因为p为0的情况下就会如果相等就会爆栈
if (left >= right) {
return;
}
// 处理逆序数组
int randomIndex = ThreadLocalRandom.current().nextInt(right - left + 1) + left;
swap(a, randomIndex, left);
// 处理重复元素
int lt = left, gt = right; // 声明两个指针
int pivot = a[left]; // 初始化基准点
int i = left; // 初始化指针
// 6 4 3 2 8 5
// i=0 gt=5 pivot=6 i=1 4<6 a[1]=a[0] a[0]=a[1] 4 6 3 2 8 5 i=2
while (i <= gt) {
// 小于基准点的时候更新lt的索引,并且交换原始数组的元素。更新i指针,
// 根据上面的过程可以看到,a[lt]一定是已经判断过了(lt<=i),所以可以更新i指针。
if (a[i] < pivot) {
swap(a, lt++, i++);
// 大于基准点的时候仅仅交换一下位置,不要更新i指针,因为当前交换的元素a[gt]还没有判断呢
} else if (a[i] > pivot) {
swap(a, i, gt--);
}
// 跟基准点相等的元素就直接跳过,不再处理了
else {
i++;
}
}
quickSort(a, left, lt - 1); // 基准点左边的元素
quickSort(a, gt + 1, right); // 基准点右边的元素
}
// 交换元素
private void swap(int[] a, int idx1, int idx2) {
int temp = a[idx1];
a[idx1] = a[idx2];
a[idx2] = temp;
}
文章评论
Hi there! Do you know if they make any plugins to help with Search
Engine Optimization? I'm trying to get my site to rank for some targeted keywords but I'm not seeing very
good results. If you know of any please share. Appreciate it!
I saw similar blog here: <a href="https://www.ecobij.nl/">Wool product</a>
With low fare airlines providing low cost tickets to Goa , taking a flight to Goa is now a cinch.
<A HREF=https://honeywpqf802772.blog4youth.com/29557229/unveiling-the-truth-behind-sugar-defender-no-longer-quite-as-sweet>sugar defender</A> For
years, I've battled unpredictable blood sugar level swings that left me really feeling drained
and sluggish. But given that incorporating Sugar my energy levels
are currently steady and regular, and I no longer strike a
wall in the afternoons. I appreciate that it's a mild,
all-natural technique that does not come with any type of undesirable side effects.
It's truly changed my daily life. <A HREF=https://carlyelqa719590.webbuzzfeed.com/profile>sugar defender reviews</A>
<a href="https://kalenxmz395074.blog-ezine.com/29170094/unveiling-the-truth-behind-sugar-defender-no-longer-quite-as-sweet">sugar defender reviews</a> Including Sugar Protector
right into my everyday regimen overall well-being. As somebody who focuses on healthy consuming, I appreciate the extra defense this supplement
offers. Considering that beginning to take it, I have actually seen a
significant enhancement in my power degrees and a considerable
decrease in my wish for undesirable treats such a
such a profound effect on my life.
<a href=https://orlandorolc433160.wikicorrespondence.com/user>Sugar defender reviews</a> I've fought with blood sugar level fluctuations
for many years, and it actually affected my power degrees throughout the day.
Considering that starting Sugar Protector, I really feel extra well balanced
and sharp, and I don't experience those afternoon plunges anymore!
I love that it's an all-natural remedy that functions with no rough
adverse effects. It's really been a game-changer for me <a href="https://cyrusoksq957630.mpeblog.com/52474149/unveiling-the-truth-behind-defender-of-sugar-the-sweetness-may-be-fading">Sugar defender Reviews</a>
<a href='https://theocflx410040.nizarblog.com/29162440/unveiling-the-truth-behind-defender-of-sugar-the-sweetness-may-be-fading'>sugar defender ingredients</a> Adding
Sugar Protector to my day-to-day regimen was just one of the
best choices I've made for my health. I'm careful regarding what I
consume, but this supplement adds an added layer of assistance.
I feel more steady throughout the day, and my desires have actually lowered substantially.
It behaves to have something so simple that makes such a large distinction! <a href="https://aishamows235703.blog-a-story.com/9040264/unveiling-the-truth-behind-sugar-defender-no-longer-quite-as-sweet">sugar defender Official Website</a>
<A HREF='https://harmonyhrjj516653.blogdosaga.com/28979514/unveiling-the-truth-behind-sugar-defender-the-sweetness-may-be-fading'>sugar defender ingredients</A> Integrating Sugar Defender into my daily routine general wellness.
As someone who focuses on healthy and balanced consuming,
I appreciate the extra defense this supplement supplies.
Since beginning to take it, I've noticed a significant improvement in my
energy degrees and a significant decrease in my desire for harmful snacks such a such an extensive influence on my life.
<A HREF="https://saulceoa348997.slypage.com/29624877/unveiling-the-truth-behind-defender-of-sugar-the-sweetness-may-be-fading">sugar defender</A>
<a href="https://sugardefender-en-us.com">sugar defender official website</a> Integrating Sugar Protector into my everyday program has been a game-changer for my total well-being.
As someone that currently focuses on healthy and balanced eating,
this supplement has actually offered an included boost of protection. in my energy levels, and my need for
harmful treats so easy can have such a profound effect on my day-to-day
live. <A HREF=https://sugardefender-en-us.com/index.html>sugar defender reviews</A>
That is a very good tip particularly to those fresh to the blogosphere. Brief but very precise information… Appreciate your sharing this one. A must read post!
You've made some good points there. I looked on the net for additional information about the issue and found most individuals will go along with your views on this website.
I blog quite often and I seriously appreciate your content. Your article has really peaked my interest. I am going to book mark your blog and keep checking for new information about once per week. I subscribed to your Feed too.
It’s hard to find knowledgeable people in this particular topic, but you seem like you know what you’re talking about! Thanks
Good post. I learn something totally new and challenging on websites I stumbleupon everyday. It will always be helpful to read through articles from other authors and practice something from their websites.
Pretty! This has been an extremely wonderful post. Many thanks for providing these details.
There is certainly a great deal to learn about this issue. I like all the points you've made.