e最近开始投各种技术类的岗位,于是乎觉得必须补一下之前的数据结构算法知识了,因为真的很重要!几乎每家面试都会问到其中的几个算法。本文部分算法是从其他大神博主那边搬运的,大家可以搜索相关算法查看其他大神的解说和思路~~~~
稳定性:排序算法是否稳定,根据序列中相同的元素排序前后的位置来判断,如果两个相同元素经过排序后,其相对位置未发生变化,则称该排序是稳定的。
注:对数组型序列的插入操作要考虑移位;后移:a[i+1]=a[i];前移:a[i-1]=a[i].
1.插入类排序
1)直接插入排序
定义,对于一个待排序列,如a[ ]=1,3,4,6,5,7,8;令i=1,首先取出a[1],将a[1]插入到序列中,当前a[1]只需和a[0]相比,然后将其插入适当位置,i++;每次将a[i]插入到a[0]至a[i-1]的序列中(因为a[i]之前的序列已经有序)。
对于n个数,要进行n-1趟插入排序,所以其时间复杂度为O(n^2),其空间复杂度为O(1),该排序是稳定的;
若待排序列为正序,时间复杂度为O(n),当要排序的数据量很大时,不适合用该方法。 直接插入排序常出现在笔试的选择题中。
C++代码实现:
int Insort(int A[],length){
int temp=0;
for(int i=1;i<length;i++)
{
if(A[i]<A[i-1])
{
temp=A[i];
for(int j=i-1;j>=0&&A[j]>temp;j--)
{
A[j+1]=A[j];
}
A[j+1]=temp; //将temp放在该放的位置
}
}
return A;
}
2)折半插入排序
其定义为,首先通过折半查找(转查找算法)找到数据要插入的位置,再对序列中的数据进行移动并插入待排数据;时间复杂度为O(n^2);稳定排序;
折半的概念来自于折半查找
插入类排序的空间复杂度均为O(1).
3)希尔排序
定义,是一种缩小增量的排序;将待排序列分割为几组,每次分割要基于一定的增量,每次的增量不一样;在小组内排好序后,再进行下一次分组;
第一次分组,d=10/2=5
第二次分组,d=3
最后一趟的增量d=1;
平均时间复杂度为O(1.3),最坏时间复杂度为O(n^2);不稳定排序
空间复杂度:O(1)
希尔排序的C++实现:
int Shellsort(int A[],int length){
int gap=0;
int temp=0;
for(gap=length/2;gap>0;gap/=2)
{
for(int i=gap;i<length;i++)
{
for (int j=i;j>=gap;j-=gap)
{
if(A[j]<A[j-gap])
{
temp=A[j];
A[j]=A[j-gap];
A[j-gap]=temp;
}
else
{
break;
}
}
}
}
return A;
}
说明:第二层j的值从i开始,每次跳到j-gap,且j的值大于等于gap,是因为当i往后移的时候,gap之前的数已经逐渐排好序了,后面j只需要控制在比较gap之后的元素即可。
2.交换排序:
1)冒泡排序
定义:从头开始比较序列中的元素,每次保留较大(小)的数,直到一次循环结束,选出最大(小)数;
每一趟排序会将当前序列的最大(小)值放在其相应的位置上。从算法就可以知道,执行两层循环所需要的时间即为其时间复杂度;
最坏以及平均时间复杂度为O(n^2)
空间复杂度:O(1). 稳定排序
C++实现:
每一趟排序,当j和i相遇的时候,说明前i个值已经排好了,即已经将前i次每次找出的最小值放在相应位置上,所以j的值只需大于i即可:
int BubbleSort(int []A){
int temp;
for(int i=0;i<A.length();i++)
{
for(int j=A.length()-1; j>i; j--)
{
if(A[j]<A[j-1])
{
temp=A[j];
A[j]=A[j-1];
A[j-1]=temp;
}
}
}
return A;
}
2)快速排序
定义,从序列中选出一个数作为“枢轴”,通常会选择第一个数,也可以选择中间的数;在序列的首尾各有一个指针low、high,首先从high指向的数开始,与枢轴元素做比较,若遇到比枢轴小的元素,则将其挖出来放到low指向的位置;下一步则从当前low开始往后移动,若遇到比枢轴大的元素,将其挖出来放到当前high指向的位置;这样经过一次移动完毕即low==high时,枢轴的左边的数都比它小,其右边的数都比它大。再对其左、右的子序列递归调用快排。
C++实现:
int QuickSort(int A[],int l,int r){
int low=l;
high=r;
int pivot=A[low];
if(l<r)
{
while(low<high && pivot<=A[high])
{
high--;
}
if(low<high) A[low++]=A[high];
while(low<high && pivot>=A[low])
{
low++;
}
if(low<high){
A[high--]=A[low];
}
A[low]=pivot;
QuickSort(A,int l,int low-1);
QuickSort(A,int low+1,int r);
}
return A;
}
时间复杂度推导公式可以查看相关资料,主要考虑其每次一分为二的特性。
快速排序的最好以及平均时间复杂度均为O(nlogn)
最坏时间复杂度为O(n2),对于快排,越有序的序列时间复杂度越高;
空间复杂度为O(logn)至O(n). 不稳定排序
3.选择排序
1)简单选择排序
2)堆排序
堆排序;首先创建一个大顶或小顶堆;每次则可以找到最大或最小元素;将最大或最小元素与最后一个元素交换,接着继续调整堆;
最坏时间复杂度O(nlogn),最好时间复杂度O(nlogn),平均时间复杂度O(nlogn);空间复杂度为O(1);
//调整堆中的元素时,假设没有交换的子节点保持着最大(最小)堆性质,所以只对经过交换的子节点进行调整
void swap(int &a, int &b)
{
int tmp = 0;
tmp = a;
a = b;
b = tmp;
}
void ShiftHeap(int A[],int maxSign,int n)//maxSign为当前根节点,n表示n个节点;
{
int j = 2 * maxSign + 1;
int temp=A[maxSign];
while (j < n)
{
if (j+1 < n && A[j] < A[j + 1])
{
j = j + 1;
}
if (temp > A[j]) break;
else
{
A[maxSign] = A[j];
maxSign = j;
j = 2 * maxSign + 1;
}
}
A[maxSign] = temp;
}
//正式开始堆排序,首先创建一个大顶或小顶堆,接着将顶端元素与最后一个元素交换,继续进行交换后的调整;
void Heapsort(int A[],int n)
{
for (int j = n / 2 - 1; j >= 0; j--)
{
ShiftHeap(A, j, n);
}
//交换过程:
for (int i = n - 1; i >= 1; i--)
{
swap(A[i], A[0]);
ShiftHeap(A, 0, i);
}
}
不得不说,每次只对度为2以上的节点进行调整,大大减少了时间复杂度,使其达到logn级别,真的hin棒了!
4.归并排序
5.二叉树排序