Maple's Blog.

排序算法

字数统计: 1.7k阅读时长: 7 min
2019/02/24 Share

有时候自己学了很久很久,学习的效果还是不好,慢慢反思,后来发现自己一直把大量的时间来学习新的知识,旧的知识基本上没有留什么时间来消化,最后的最后就是“感觉自己什么都学了,但是自己什么都不会”。学习有点乱的时候停一停,给了自己一些缓解的时间,找到问题的症结除治,然后努力在原有的基础上有新的突破……众所周知,我们学了很多的排序算法,真的学多了之后会特别的乱,所以特别的想花点时间整理一下O(∩_∩)O~

首先,是最简单的初级排序算法

1.选择排序
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//选择排序的时间复杂度为O(n^2);
//本排序是由小到大排的
void SelectionSort(int *arr,int n)
{
for(int i=0;i<n;i++)
{
int k=i; //假设最小元素就是为arr[0]
for(int j=i+1;j<n;j++) //从k下标后面的那个元素开始
{
if(arr[j]<arr[k])//如果j下标后面的数比arr[k]小
k=j; //改变最小元素下标
}
swap(arr[k],arr[i]);//第一轮找到数组中最小的元素,然后交换在第1位,第二轮放第2位,依次类推...
}
}
//选择排序由于要完完全全执行两层循环,排序效率低
2.插入排序
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
void insertsort(int *arr,int n)
{
for(int i=1;i<n;i++)//当前排序的元素下标
{
for(int j=i;j>0&&arr[j-1]>arr[j];j--)//最多执行到j=1与arr[0]比较,看是否需要交换位置;如果arr[j]前面的元素值一直比arr[j]值大,不断执行j--
{
swap(arr[j-1],arr[j]);
}
}
}

插入排序的优化
void Insertsort(int *arr,int n)
{
for(int i=1;i<n;i++)
{
int e=arr[i];
int j;
for(j=i;j>0&&arr[j-1]>e;j--)
{
arr[j]=arr[j-1]; //挪动一次的时间复杂度要优于交换一次的时间复杂度
}
arr[j]=e;
}
}
//插入排序对于最坏的情况虽然时间复杂度是O(n^2),但是插入排序对于近乎有序的数组来说,排序的效率非常之高,甚至比O(nlogn)的效率还要高;

此外还有一些初级的算法比如像”冒泡排序“,“希尔排序”…在此就不再列出了,因为它们的排序效率也不是很高

以下为时间复杂度为O(nlogn)的高级排序

3.归并排序
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
void __Merge(int*arr,int l,int mid,int r)
{
int aux[r-l+1];
for(int i=l;i<=r;i++)
{
aux[i-l]=arr[i];
}
int i=l,j=mid+1;
for(int k=l;k<=r;k++)
{
if(i>mid) //左半部分元素已经全部处理完毕
{
arr[k]=aux[j-l];
j++;
}
else if(j>r) //右半部分元素已经全部处理完毕
{
arr[k]=aux[i-l];
i++;
}
else if(aux[i-l]<=aux[j-l]) //左半部分所指元素 <=右半部分所指元素,这里的等于号使得归并排序有了稳定性
{
arr[k]=aux[i-l];
i++;
}
else //左半部分所指元素 > 右半部分所指元素
{
arr[k]=aux[j-l];
j++;
}
}
}

void Mergesort(int *arr,int l,int r) //递归,此处的r为有效下标
{
if(l>=r)
return;
int mid=(r-l)/2+l;
Mergesort(arr,l,mid);
Mergesort(arr,mid+1,r);
if(arr[mid]>arr[mid+1])
__Merge(arr,l,mid,r);
}

void MergeSort(int *arr,int n) //此时的n为数组的最大下标数+1
{
Mergesort(arr,0,n-1);
}
4.快速排序
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
//三路快排
void Quicksort(int *arr,int l,int r)
{
if(l>=r)
return ;
int v=arr[l];
int lt=l;
int gt=r+1;
int i=l+1;
while(i<gt)
{
if(arr[i]<v) //比v小的移到左边位置
{
swap(arr[i],arr[lt+1]);
i++;
lt++;
}
else if(arr[i]>v) //比v大的移到右边位置
{
swap(arr[i],arr[gt-1]);
gt--;
}
else //和v相等的不用移动,放在中间位置
{
i++;
}
}
swap(arr[l],arr[lt]); //这里不要写swap(v,arr[lt]);因为v和arr[l]地址不同,我们要做的是交换数组中两个元素的位置;
Quicksort(arr,l,lt-1);
Quicksort(arr,gt,r);
}

void QuickSort(int *arr,int n)
{
Quicksort(arr,0,n-1);
}


5.堆排序
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
void shiftDown(int *data,int k,int count)   //用来维护最大堆
{
while( 2*k <= count ){
int j = 2*k;
if( j+1 <= count && data[j+1] > data[j] ) j ++;
if( data[k] >= data[j] ) break;
swap( data[k] , data[j] );
k = j;
}
}


void MaxHeap(int *arr,int n ) //arr为用户传入的一个数组,n为数组的大小
{
int *data=new int [n+1];
for(int i=0;i<n;i++)
{
data[i+1]=arr[i];
}
for(int i=n/2;i>=1;i--) //从非叶子节点的元素开始进行shiftDown操作
{
shiftDown(data,i,n);
}
while(n>=1) //每次将堆中最大的那一个也就是data[1]给了arr[n-1](arr数组中倒数第一、倒数第二...的位置)
{
arr[n-1]=data[1];
swap(data[1],data[n]);
n--; //每次传出了一个元素后n需减一
shiftDown(data,1,n);
}
delete []data;
}
------------------------------------------------------
//1.以上完成了第一种方式的堆排序⤴️
//2.下面来实现下第二种方式的堆排序,大同小异,没有开辟额外的空间,直接是原地堆排序,性能上稍微好了点⬇️
-----------------------------------------------------
void shiftDown(int *data,int k,int count)
{
while( 2*k+1<count ){
int j = 2*k+1;
if( j+1 < count && data[j+1] > data[j] ) j ++;
if( data[k] >= data[j] ) break;
swap( data[k] , data[j] );
k = j;
}
}
void MaxHeap(int *arr,int n )
{
for(int i=(n-2)/2;i>=0;i--)
{
shiftDown(arr,i,n);
}
for(int i=n-1;i>=0;i--) {
swap(arr[0],arr[i]);
shiftDown(arr, 0, i);
}
}
几种排序算法的性能

当然还有许多的排序,比如桶排序、shellsort等等,我就不继续写了,毕竟在涉及排序问题的算法时我们只会选择一类来进行解题(我个人是这么理解的…)排序算法看似简单,即使这样我们在考虑数组下标的时候也要注意,谨防越界,不然debug真的会很痛苦的。我在写这几个排序算法的时候同样我又发现了好几个自身的问题,真的是复习旧的知识确实能发现新大陆。同时在这里也要感谢bobo老师的教学指导,他的课让我受益匪浅——蒻蒻的我,继续加油吧!

CATALOG
  1. 1. 1.选择排序
  2. 2. 2.插入排序
  3. 3. 3.归并排序
  4. 4. 4.快速排序
  5. 5. 5.堆排序
  6. 6. 几种排序算法的性能