一,冒泡排序算法
1.冒泡排序的规则
冒泡排序是我们最常见的排序方式,就是将相邻的两个元素进行比较,如果前一个比后面的元素大,就将其调换位置。重复上面的动作,最后一个不进行比较,直到没有数据进行比较。
最差时间复杂度:O(n^2)
平均时间复杂度:O(n^2)
最优时间复杂度:如果能再内部循环第一次运行时,使用一个旗标来表示有无需要交换的可能,可以把最优时间复杂度降低到O(n)
稳定性:稳定
2.代码实现
public void swap(int A[],int i,int j)
{
int temp=A[i];
A[i]=A[j];
A[j]=temp;
}
public BubbleSort(int A[],int n)//A是带入的数组,n是数组长度
{
for(int j=0;j<n-1;j++) //每次最大元素就像气泡浮到数组的最后
{
for(int i=0;i<n-1-j;i++)//依次比较相邻的两个元素,使较大的那个向后移动
{
if(a[i]>a[i+1])
{
swap(A,i,i+1)
}
}
}
}
使用冒泡排序为一列数字进行排序的过程如上图所示。
冒泡排序是很好了解的也是很容易实现的,但是对于少量数据进行排序之外,是相当没有效率的。
二,鸡尾酒排序(冒泡排序的改进)
鸡尾酒排序也叫定向冒泡排序,此算法与冒泡排序的不同处在于从低到高然后从高到低,而冒泡排序的则从低到高比较有序列的去比较数组中的每个元素。
1.代码实现
public void swap(int A[],int i,int j)
{
int temp=A[i];
A[i]=A[j];
A[j]=temp;
}
public void CocktailSort(int a[],int n)
{
int left=0;//初始化边界
int right=n-1;
while(left<right)
{
for(int i=left;i<right;i++)//前半程,将最大的数字往后面放
{
if(A[i]>A[i+1])
{
swap(A,i,i+1);
}
}
right--;
for(int i=right;i>left;i--)
{
if(A[i-1]>A[i])
{
swap(A,i,i+1);
}
}
left++
}
}
比较,已序列(2,3,4,5,1)为例 ,鸡尾酒排序只需要一次就能完成排序,而冒泡就需要进行四次排序,但是在乱数的序列的状态下,鸡尾酒与冒泡排序的效率都很差劲的。
三,选择排序
1.选择排序的规则
选择排序也是一种简单直观的排序算法,工作原理就是初始时在序列找到最小(大)的元素,房贷续流的起始位置,无限次的循环下去,直到循环完成。、
注意: 选择排序与冒泡排序的区别:冒泡排序通过依次交换,相邻两个顺序不合法的元素位置,从而将当前最小(大)元素放到合适的位置。而选择排序每遍历一次都记住当前最小(大)元素的位置,最后仅需一次交换操作,即可将其放到合适的位置去。
最差时间复杂度:O(n^2)
平均时间复杂度:O(n^2)
最优时间复杂度:O(n^2)
稳定性:不稳定
2.代码实现
public void swap(int A[],int i,int j)
{
int temp=A[i];
A[i]=A[j];
A[j]=temp;
}
public void SelectionSort(int A[],int n)
{
for(int i=0;i<n-1;i++)//i为已排序序列的末尾
{
int min=i;
for(int j=i+1;j<n;j++)//未排序序列
{
if(A[j]<A[min])//找出未排序的序列中的最小值
{
min=j;
}
}
if(min!=i)
{
swap(A,min,i);//放到已排序的末尾,该操作很有可能把稳定性打乱,所以选择排序是不稳定的排序算法
}
}
}
四,插入排序
1.插入排序的规则
插入排序是一种简单直观的排序算法,工作原理类似于我们抓扑克牌
从第一个元素开始,该元素可以认为已经被排序
取出下一个元素,在已经排序的元素序列中从后向前扫描
如果该元素(已排序)大于新元素,将元素移动到下一位置
重复上述步骤,知道找到已排序的元素小于或等于新元素的位置
将元素插入到该位置
最差时间复杂度:O(n^2)
平均时间复杂度:O(n^2)
最优时间复杂度:O(n)
稳定性:稳定
{
for(int i=1;i<n;i++) //类似抓扑克牌排序
{
int get=A[i]//右手抓到一张扑克牌
int j=i-1;//拿在左手上的牌总是排序好的
while(j>=0&&A[j]>get)//将抓到的牌与手上的牌进行比较
{
A[j+1]=A[j];//如果该手牌比抓到的牌大就将其右移
j--;
}
A[j+1]=get;//直到该手牌比抓到的牌小(或二者相等),将抓到的牌插入到最右边
}
}
插入排序不适合对于数据量比较大的应用。在STL的sort算法和stdlib的qsort算法中,都将插入排序作为快速排序的补充。
五,二分插入排序
对于插入排序,如果比较操作的代价比交换操作大的话,可以采用二分查找法来减少比较操作的次数,称之为二分插入排序
最差时间复杂度:O(n^2)
平均时间复杂度:O(n^2)
最优时间复杂度:O(nlogn)
稳定性:稳定
代码实现:
public void InsertionSortDichotoomy(int A[],int n)
{
for(int i=1;i<n;i++)
{
int get=A[i]; //右手拿到一张扑克牌
int left =0; //拿在左手上的牌总是排序好的,所以可以用二分法
int right=i-1; //手牌左右界进行初始化
while(left<=right) //采用二分法定位新牌的位置
{
int mid=(left+right)/2;
if(A[mid]>get)
right=mid-1;
else
left=mid-1;
}
for(int j=i-1;j>=left;j--)//将欲插入新牌的右边整体想右移动一个位置
{
A[j+1]=A[j];
}
A[left]=get; //将抓到的牌插入手牌
}
}
当n较大时,二分插入排序的比较此时比直接插入的排序的最差情况要好。
六,快速排序
在平均状况下,排序n个元素要O(nlogn)次比较,在最坏状况下则需要O(n^2)次比较,单这种状况不常见,
1.快速排序规则
从序列中挑出一个元素,作为基准
把所有比基准小的元素放在基准前面,所有比基准大的元素放在基准的后面
对每个分区在进行上面的操作,递归的结束条件是序列的小小是0或1,这是整体已经被排序好了
最差时间复杂度:O(n^2)
平均时间复杂度:O(nlogn)
最优时间复杂度:O(nlogn)
稳定性:不稳定
2.代码实现
public void swap(int A[],int i,int j)
{
int temp=A[i];
A[i]=A[j];
A[j]=temp;
}
int Partition(int A[],int left ,int right ) //划分函数
{
int pivot=A[right]; //这里每次都选择最后一个元素作为基准
int tail=left-1; //tail为最小基准的子数索引
for(int i=left;i<right;i++)//遍历基准以外的其他元素
{
if(A[i]<=pivot) //把小于等于元素放到前一个子数组的末尾
{
swap(A,++tail,i);
}
}
swap(A,tail+1,right); //最后把基准放到前一个子数组的后边,剩下的子数组既是大于基准的子数组
//该操作很有可能把后面的稳定性打乱,所以快速排序不是稳定的算法
return tail+1; //返回基准索引
}
void QuickSort(int A[],int left,int right)
{
if(left>==right)
return;
int pivot=Partition(A,left,right);
QuickSort(A,left,pivot-1);
QuickSort(A,pivot+1,right);
}
Java系统提供的Arrays.sort函数。对于基础类型,地城使用快速排序,对于非基础类型,底层使用的是归并排序。