基本排序算法(Java)

基本排序算法 (Java)

  • 经过几次笔试,发现自己的Java基础有些薄弱,基本的排序算法掌握的还不够熟练,需要多加学习总结。

1. 选择排序

思想: 给定一个整数数组,例 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;
     }

2. 直接插入排序

思想:对于给定的一个整数数组,例如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;
     }

3.冒泡排序

思想:在冒泡排序中,对于给定的整形数组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;
     }

4.归并排序

思想:归并排序,归是递归,并是合并。首先对给定的数组进行折半,直到折半成最小的子数组,比如只包含一个元素。然后再依次进行两两合并,合并时按照顺序进行排序,得到有序的子数组,再对这些子数组进行两两合并,直到生成最终的一个数组,即是有序的数组了。比如数组[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+" ");
        }

5.快速排序

思想:快速排序采用分而治之的思想,对于一个给定的数组,还是以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);
        }

6.希尔排序

思想:希尔排序也称为“缩小增量排序”,其基本思想是通过选定一个 步长序列,比如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;
                        }
                }
        }

7.堆排序

思想:对于给定的数组序列,例如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面试宝典》数据结构与算法章节,感谢作者的精彩讲解。

posted @ 2019-04-13 21:51  XK-T  阅读(189)  评论(0编辑  收藏  举报