思想: 给定一个整数数组,例 int[] a ={38,65,97,76,13,27,49},首先进行第一轮比较:遍历整个数组,选出最小的数字,以及该数字所在数组的下标,然后将该元素与数组的第一个元素进行交换。第二轮比较:比较范围为从数组的第二个元素开始,选择出最小的元素,并与数组的第二个元素交换位置... 多轮比较后直到待选择元素只剩下一个。此时该数组就是升序了。
时间复杂度:最好、最坏、平均都是O(n^2)
空间复杂度:O(1)
稳定性:不稳定的排序算法:例[2(1),5,3,2(2),1] 经过选择排序后变成[1,2(2),2(1),3,5]
当n较小时,排序效果较好
代码实现如下:
public static int[] selectSort(int[] a)
{
if(a.length==1)
return a;
if(a.length==0)
return null;
int i,j,index; //index记录最小值元素所在下标
int min;
for(i=0;i<a.length-1;i++)
{
index=i;
min=a[i];
for(j=i+1;j<a.length;j++)
{
if(a[j]<min)
{
min=a[j];
index=j;
}
}
a[index]=a[i];
a[i]=min;
}
return a;
}
思想:对于给定的一个整数数组,例如a ={38,65,97,76,13,27,49},假定第一个元素自成一个有序序列,其他的元素为无序序列。则按顺序从无序序列中挑出一个元素,插入到有序序列的末尾,然后对该有序序列重新排序,生成一个新的有序序列,然后再依次这样从无序序列中挑出元素,构建新的有序序列,直到最后一个元素插入到有序序列中并重新构建出最终的有序序列。
时间复杂度: 最好:O(n),最坏:O(n^2),平均:O(n^2)
空间复杂度:O(1)
稳定性:稳定
当大部分有序时,排序效果较好
代码实现如下:
public static int[] insertSort(int[] a)
{
if(a.length==1)
return a;
if(a.length==0)
return null;
int i,j,tmp;
for(i=1;i<a.length;i++)
{
tmp=a[i];//选出的待插入元素
j=i;
if(a[j-1]>tmp)
{
//当在有序序列中出现比待插入元素大的元素时,将该元素后移一位
//j代表待插入元素在有序序列中插入的位置下标
while(j>=1&&a[j-1]>tmp)
{
a[j]=a[j-1];
j--;
}
}
a[j]=tmp;
}
return a;
}
思想:在冒泡排序中,对于给定的整形数组a={38,65,97,76,13,27,49},第一轮排序比较:从第一个元素开始,与其相邻的元素进行比较,若右边元素比左边元素大,则交换位置;然后继续从第二个元素开始进行比较,直到比较到倒数第二个元素。此时,数组中最后一个元素是最大的元素,然后再进行第二轮比较,比较范围是从当前数组第一个元素到倒数第三个元素,即找出第二大的元素,放到末尾...经过(a.length-1) 这里是6次比较后,得到了最终的升序排序结果。
时间复杂度:最好:O(n),最坏和平均都是:O(n^2)
空间复杂度:O(1)
稳定性:稳定
当n较小时,排序效果较好
代码如下:
public static int[] bubbleSort(int[] a)
{
int len=a.length,tmp;
if(len==0)
return null;
if(len==1)
return a;
for(int i=0;i<len;i++) //经过n-1次比较
for(int j=0;j<len-i-1;j++) //每次比较的范围是 a[0,n-i-1)
{
if(a[j]>a[j+1])
{
tmp=a[j];
a[j]=a[j+1];
a[j+1]=tmp;
}
}
return a;
}
思想:归并排序,归是递归,并是合并。首先对给定的数组进行折半,直到折半成最小的子数组,比如只包含一个元素。然后再依次进行两两合并,合并时按照顺序进行排序,得到有序的子数组,再对这些子数组进行两两合并,直到生成最终的一个数组,即是有序的数组了。比如数组[2,6,1,0]先对其进行不断地折半,生成[2],[6],[1],[0]四个子数组,然后再对其进行两两合并,生成[2,6],[0,1]两个数组,然后再对这两个有序数组进行两两合并,最终生成[0,1,2,6]这个最终的有序数组。
时间复杂度:最好,最坏,平均都是O(nlog(n))
空间复杂度:O(n)
稳定性:稳定
当n数目较大时较好
代码如下:
public static int[] num = {38,65,97,76,13,27,49};
public static void MergeSort(int[] a,int[] array_tmp,int p,int q,int r)
{
int i,j,k,l_end,r_end;
l_end=q+1;
r_end=r+1;
for(i=p,k=p;i<l_end;i++,k++)
array_tmp[i]=a[k];
for(j=q+1,k=q+1;j<r_end;j++,k++)
array_tmp[j]=a[k];
for(k=p,i=p,j=q+1;i<l_end&&j<r_end;k++) //执行完一次循环以后,k会自动加一,然后执行条件判断
{
if(array_tmp[i]<=array_tmp[j]) //这样便是稳定的排序了
{
a[k]=array_tmp[i];
i++;
}
else
{
a[k]=array_tmp[j];
j++;
}
}
if(i<l_end)
{
for(;i<q+1;i++,k++)
a[k]=array_tmp[i];
}
if(j<r_end)
{
for(;j<r+1;j++,k++)
a[k]=array_tmp[j];
}
}
public static void Merge(int[] a,int[] array_tmp,int p,int r)
{
if(p<r)
{
int q=(p+r)/2;
Merge(a,array_tmp,p,q);
Merge(a,array_tmp,q+1,r);
MergeSort(a,array_tmp,p,q,r);
}
}
public static void main(String[] args)
{
int[] array_tmp = new int[num.length];//使用的辅助空间大小为n
Merge(num,array_tmp,0,num.length-1);
for(int i:num)
System.out.print(i+" ");
}
思想:快速排序采用分而治之的思想,对于一个给定的数组,还是以a{38,65,97,76,13,27,49}为例,先是随机选取其中的一个数(可以是选择数组中的第一个元素,也可以是数组中的最后一个元素,还可以从左、右、中间随机数中选择大小取中的值)作为一个基准值,然后将该数组分成左右两个部分,左边的数字都比基准数字小,右边的数字都比基准数字大,此时该基准数字的位置就是其在排序后数组中的最终位置。然后对左右两边的子无序数组,再进行上述的排序过程,通过不断地拆分,直到所有元素都找到了其真正所属的位置下标,排序结束。
时间复杂度:最坏:O(n^2) [ 当每次进行选取的基准关键字都是最小的或者最大的,使得区间划分的结果一边为空,此时比较次数是1+2+3+...+n-1=n(n-1)/2即n^2 ],最好和平均:O(nlog(n)) 快速排序的平均性能是最好的
空间复杂度:O(log(n)) [ 快速排序需要一个栈空间来实现递归,当情况最好时,栈空间的大小为 log(n)+1,当情况最坏时,栈空间大小为n,平均空间复杂度为 O(log(n)) ]
稳定性:不稳定
当n较大时,排序效果较好
代码如下:
public static void QuickSort(int[] a,int start,int end)
{
if(start>=end) //一定要记得加上递归结束条件,否则会造成 StackOverflowError错误
return;
int i,j,index;
index=a[start];
i=start;
j=end;
while(i<j)
{
while(i<j&&a[j]>=index)
j--;
if(i<j)
a[i++]=a[j];
while(i<j&&a[i]<index)
i++;
if(i<j)
a[j--]=a[i];
}
a[i]=index;
QuickSort(a,start,i-1);
QuickSort(a,i+1,end);
}
思想:希尔排序也称为“缩小增量排序”,其基本思想是通过选定一个 步长序列,比如T{t1,t2,...ti...tn}。步长序列一般是逐渐缩小的,即前面的步长要比后面的步长要大。当选定步长序列以后,对每一个步长ti,根据步长将给定待排序数组分成多个子序列。比如待排序数组
a={12,11,10,9,8,7,6,5,4,3,2,1},步长此时为ti=3。则根据步长将数组a分成{12,9,6,3}、{11,8,5,2}、{10,7,4,1}这三个子序列。然后对这三个子序列进行直接插入排序,得到排序结果后再依照步长序列中后面的步长再进行直接插入排序。最后的步长tn一般取为1,也就是最后一次是将所有元素看成一组进行直接插入排序,此时一般来说整个数组基本有序,所需要的交换次数很少。
时间复杂度:最好是:O(n) 最坏:O(n^s) (1<s<2) 平均:O(nlog(n))
空间复杂度:O(1)
稳定性:不稳定
希尔排序属于插入排序的一种
代码如下:
public static void ShellSort(int[] a)
{
int len=a.length;
int i,j,tmp;
int h;//h为步长
for(h=len/2;h>0;h=h/2)
{
for(i=h;i<len;i++)
{
tmp=a[i];
for(j=i-h;j>=0;j-=h)
{
if(tmp<a[j])
a[j+h]=a[j];
else break;
}
a[j+h]=tmp;
}
}
}
思想:对于给定的数组序列,例如a={38,65,97,76,13,27,49},初始时先将其看作一棵顺序存储的二叉树,即a[0]为根节点,a[1]、a[2]为其左右子节点。然后将该树调整成为一个小顶堆,然后将堆中的最后一个元素a[n-1]与堆顶元素(即二叉树的根节点,也是最小的元素)进行交换。接着将前n-1个元素重新调整成一个小顶堆,再将堆顶元素与该堆中的最后一个元素a[n-2]进行交换,得到次最小元素,重复上述过程,直到调整的堆中只剩下一个元素为止,该元素即为数组中的最大元素。此时该数组就变成了降序数组了。
时间复杂度:最坏、最好、平均都是O(nlog(n))
空间复杂度:O(1)
稳定性:不稳定
当n较大时,排序效果较好
代码如下:
public static void sift(int[] a,int low,int high){
int i=low,j=2*i+1;
int tmp;
while(j<=high) //判断a[i]是不是父节点,若a[i]是父节点,则2*i小于high
{
if((j<high)&&(a[j]>a[j+1])) //如果存在右孩子,且左孩子小于右孩子,则指向右孩子
j++;
if(a[i]>a[j])
{ tmp=a[i];
a[i]=a[j];
a[j]=tmp;
i=j;
j=2*i+1;
}
else //别忘记 break 终止,否则会一直循环下去
break;
}
}
public static void main(String[] args)
{
int[] a = {2,5,8,6,1,4,7};
int n = a.length-1;
int i,tmp;
//构建初始堆
for(i=n/2-1;i>=0;i--)
sift(a,i,n);
for(i=n;i>=1;i--)
{
tmp=a[i];
a[i]=a[0];
a[0]=tmp;
sift(a,0,i-1);
}
for(int m:a)
System.out.println(m); //此时数组a为降序有序
}
文章内容主要参考《Java面试宝典》数据结构与算法章节,感谢作者的精彩讲解。
原文:https://www.cnblogs.com/jt-xk/p/10703101.html