1.直接插入排序
算法原理: 每次在无序序列中选取首个元素,依次与有序元素进行比较,然后将其放在合适的位置。该算法稳定
Code:
void insertSort(int arr[],int n)
{
int temp,i,j;
for(i=1;i<n;++i)
{
temp = arr[i];
j=i-1;
while(j>=0 && temp<arr[j])
{
arr[j+1] = arr[j];
--j;
}
arr[j+1] = temp;
}
}
2.简单选择排序
算法原理: 每次在无序数组中挑出最小元素将其直接放置在其最终位置。注意此处为简单选择排序算法之对换法,常用于数组结构,是一种不稳定的排序算法。与之相对应的是移动元素法,常用于链表的排序操作,是一种稳定的算法。
Code:
void simpleSelectSort(int arr[] , int n)
{
int i,j,k;
int temp;
for(i=0;i<n;++i)
{
k = i;
for(j=i+1;j<n;++j)
{
if(arr[k]>arr[j])
{
k = j;
}
}
tmep = arr[k];
arr[k] = arr[i];
arr[i] = temp;
}
}
3.冒泡排序
算法原理: 冒泡排序是指将最大或者最小元素通过依次交换使其放在其有序数组的最终位置,然后缩小无序数组的表示范围,通过n次二重循环,应该做到将数组完成排序。是稳定算法
Code:
void bubbleSort(int arr[],int n)
{
int i,j,temp;
int flag;
for(i=n-1;i>=0;--i)
{
flag = 0;
for(j=0;j<i;++j)
{
if(arr[j]>arr[j+1])
{
temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = arr[j];
flag = 1;
}
}
if(flag==0)
break;
}
}
4.希尔排序
算法原理:
将待排序列划分成若干子序列,子序列中元素的距离为一增量gap
,在每次排序过程中将该子序列排列有序,然后以一定的规律减小gap
的值,在本次代码实现中将在每次取gap值的1/2
。设存在下图所示的数据数组,下面使用希尔排序依次演示
- 原表
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|
49 | 38 | 65 | 97 | 76 | 13 | 27 | 49 | 55 | 4 |
- 第一次(
gap=10/2=5
)
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|
13 | 27 | 49 | 55 | 4 | 49 | 38 | 65 | 97 | 76 |
- 第二次(
gap=5/2=2
)
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|
4 | 27 | 13 | 49 | 38 | 55 | 49 | 65 | 97 | 76 |
- 第三次(
gap=2/2=1
)
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|
4 | 13 | 27 | 38 | 49 | 49 | 55 | 65 | 76 | 97 |
此时整个序列业已有序,已经完成了排序,可以发现希尔排序的最后一轮排序为冒泡排序,显然其相比于单纯的bubbleSort算法要更加优秀。需要注意的是虽然在此实例中两个49
前后相对位置虽然未发生变化,看起来好像仍然是稳定的算法,但实际上由于其实现手段的原因,其实其是一个不稳定的算法。
代码:
void shellSort(int arr[],int n)
{
int i,j,temp;
for(int gap=n/2;gap>0;gap/=2)
{
for(i=gap;i<n;++i)
{
temp = arr[i];
for(j=i;j>=gap&&arr[j-gap]>arr[j];j-=gap)
arr[j] = arr[j-gap];
arr[j] = temp;
}
}
}
5.快速排序
算法原理: 快排的partition函数举世闻名,不多说了,溜了。时间复杂度:O(NlogN)
6.堆排序
算法原理: 将待排序列搞为大根堆或者小根堆之后,将根顶元素放到最后面,然后重新调节为小一个元素的堆即可,首先瞅一瞅从数组下标为1开始存储数据的大根堆的插入操作:
code:
class Heap{
vector<int> a;//存储数据的数组
public:
Heap(){
a.push_back(0);//从1号位置开始
}
void insert(int data){
a.push_back(data);
int i = a.size()-1;
while(i/2>0 && a[i]>a[i/2]){//插入,自下向上堆化
swap(a[i],a[i/2]);
i /= 2;
}
}
void heapify(int n, int i);//从上至下堆化过程
void removeMax(); //删除堆中最大元素
void buildHeap(vector<int> &a);//建堆
heapsort(vector<int> &a);//堆法排序
};
从上至下堆化过程
void Heap::heapify(int n,int i){
while(true){
int maxPos = i;
if(i*2<=n && a[i]<a[i*2]) maxPos = i*2;
if(i*2+1<=n && a[i*2+1]>a[maxPos]) maxPos = i*2+1;
if(maxPos==i) break;
swap(a[i],a[maxPos]);
i = maxPos;
}
}
删除堆顶元素
void Heap::removeMax(){
if(a.size()==1)//下标为0处不放数据
return -1;
a[1] = a[a.size()-1];
a.pop_back();
heapify(a.size()-1,1);
}
建堆
void Heap::buildHeap(vector<int> &a){
for(int i=(a.size()-1)/2;i>0;--i){
heapify(a.size()-1,i);
}
}
堆排序
void Heap::heapsort(vector<int> &a){
bulidHeap(a);
int k = a.size()-1;
while(k>1){
swap(a[1],a[k]);
--k;
heapify(k,1);
}
}
好了,以上就是一些关于堆排序的基本操作,谨记堆排序插入时自下向上最好,而删除以及建堆时自上至下的调整是科学的否则会造成空洞现象,最后,建堆时间复杂度为O(n),而堆排序为O(NlogN).
7.归并排序
递归的思路
void merge(int arr[],int low,int mid,int high){
int help[high-low+1];
int i=0;
int p1 = low;
int p2 = mid+1;
while(p1<=mid&&p2<=high){
help[i++] = arr[p1]<=arr[p2]?arr[p1++]:arr[p2++];
}
while(p1<=mid){
help[i++] = arr[p1++];
}
while(p2<=high){
help[i++] = arr[p2++];
}
for(i=0;i<high-low+1;i++){
arr[low+i] = help[i];
}
}
归并排序主代码
void mergeSort(int arr[],int low,int high)
{
if(low<high)
{
int mid = (low+high)/2;
mergeSort(arr,low,mid);
mergeSort(arr,mid+!,high);
merge(arr,low,mid,high);
}
}
8.桶排序
基数排序原理: 时间复杂度O(N)
核心思想是将要排序的数据划分到几个有序的桶里面,然后对每一个桶我们使用快排或者归并使他们有序,然后取出每一个桶里面的元素即可。是外部排序的利器
9.计数排序
只是用在数据范围不大的场景之中,且只能给非负整数排序,局限性有点大。但好处是比较快。实现特别巧妙,极客时间上讲的不错。O(N)
10.基数排序
按每一数位进行排序,最知名的例子就是手机号码排序,就是从号码低位到高位,每次按这一位使用桶排序进行排序,最终就会完成排序。
后面三个排序可参考以下链接极客时间:数据结构与算法