Java 排序算法小谈


本人第一次写技术博客,因为是感觉写在笔记本上找的时候又麻烦,而且写字真的很费劲。所以想想还是终于下定决心写下博客,希望大牛们可以多个点意见,小弟不胜感激,轻点喷,我也是没学多久,谢谢各位大牛们。

话不多说,进入正题,排序算法有很多,为了选择合适的算法,可以按照以下标准作参考: 

(1)时间复杂度
(2)空间复杂度

(3)编程工作

时间复杂度:主要分析关键字的比较次数和记录的移动次数

空间复杂度:分析算法中需要多少辅助内存

稳定性:在原序列中,ri=rj,且ri在rj之前,而在排序后的序列中,ri仍在rj之前,则称这种排序算法是稳定的;否则称为不稳定的。(下面会用到这个概念)

主要排序法有: 
一、冒泡(Bubble)排序
二、选择排序
三、插入排序
四、壳(Shell)排序——缩小增量 
五、归并排序 
六、快速排序 
七、堆排序 
八、基数排序 


以下都是从小到大排序:

一、冒泡(Bubble)排序 
 
void BubbleSort() 

       for(int i=1;i<n;i++) 
        { 
       for(int j=0;j<n-i;j++) 
        
             if(a[j]>a[j+1])//比较交换相邻元素 
               
                  int temp; 
                  temp=a[j]; 

    a[j]=a[j+1]; 

    a[j+1]=temp; 
              
        
     

很简单的交换排序,时间复杂度O(n2),冒泡排序法是一种稳定的排序,需要的辅助空间是O(1),这种排序法是当n小的时候,效率高。

二、选择排序 
void SelectSortArray() 

       int min_index; 
       for(int i=0;i<n-1;i++) 
   
        min_index=i; 
                for(int j=i+1;j<n;j++)//每次扫描选择最小项 
           if(arr[j]<arr[min_index])  min_index=j; 
        if(min_index!=i)//找到最小项交换,即将这一项移到列表中的正确位置 
        
            int temp; 
            temp=arr[i]; 

    arr[i]=arr[min_index]; 

    arr[min_index]=temp; 



每次扫描最小(大)项,排在相应的位置,比较简单,时间复杂度是O(n2),空间复杂度是O(1),是不稳定的排序。


三、直接插入排序 
void InsertSort() 
  { 
for(int i=1;i<n;i++)//循环从第二个数组元素开始,因为arr[0]作为最初已排序部分 

   int temp=arr[i];//temp标记为未排序第一个元素 
   int j=i-1; 
while (j>=0 && arr[j]>temp)/*将temp与已排序元素从小到大比较,寻找temp应插入的位置*/ 
{ 
   arr[j+1]=arr[j]; 
   j--; 

arr[j+1]=temp; 

  }

直接插入排序:将待排序的元素插入到先前的有序序列中。最佳效率O(n);最糟O(n2),也要考虑移动的次数,故时间复杂度为O(n2)。空间复杂度为O(1),并且排序是稳定的。当数据有一定顺序的情况下,插入排序效率较好,如果无序则和冒泡,选择一样。


四、壳(Shell)排序
void ShellSort() 

 for(int h=3;h<0;h--)//增量递减,以增量3,2,1为例 

      for(int L=0;L<(n-1)/h;L++)//重复分成的每个子列表 

  for(int i=L+h;i<n;i+=h)//对每个子列表应用插入排序 
  { 
     int temp=arr[i]; 
     int j=i-h; 
     while(j>=0&&arr[j]>temp) 
     
         arr[j+incr]=arr[j]; 
          j-=h; 

arr[j+h]=temp; 




shell排序属于插入排序,对直接插入进行了改进,其实就是引入了增量(增量一般计算公式为h = (n-1)/3),可以是数据大跨度的移动,减小了比较次数。最差为O(ns)(1<s<2)最优为O(log2n),空间复杂度为O(1),也是不稳定排序。


五、归并排序 
   void MergeSort(int low,int high) 
{ 
  if(low>=high)  

       return;//每个子列表中剩下一个元素时停止 
  else

 int mid=(low+high)/2;/*将列表划分成相等的两个子列表,若有奇数个元素,则在左边子列表大于右侧子列表*/ 
  MergeSort(low,mid);//子列表进一步划分 
  MergeSort(mid+1,high); 
  int [] B=new int [high-low+1];//新建一个数组,用于存放归并的元素 
  for(int i=low,j=mid+1,k=low;i<=mid && j<=high;k++)/*两个子列表进行排序归并,直到两个子列表中的一个结束*/ 
  
      if (arr[i]<=arr[j];) 

   B[k]=arr[i]; 
     i++; 

else
   { B[k]=arr[j];

 j++; 

         } 

for(   ;j<=high;j++,k++)//如果第二个子列表中仍然有元素,则追加到新列表 
     B[k]=arr[j]; 
  for(   ;i<=mid;i++,k++)//如果在第一个子列表中仍然有元素,则追加到新列表中 
     B[k]=arr[i]; 
  for(int z=0;z<high-low+1;z++)//将排序的数组B的 所有元素复制到原始数组arr中 
     arr[z]=B[z]; 

 归并排序:即将数据分成若干个子列,子列是有序的,然后再把有序子列合并。归并排序是稳定的,数组需要的额外空间为O(n),链表需要的额外空间为O(log2n),时间复杂度为O(nlog2n).归并的最佳、平均和最糟用例效率之间没有差异。 适用于排序大列表,基于分治法。 

O(nlog(n))


六、快速排序 
  void swap(int a,int b){int t;t =a ;a =b ;b =t ;} 
        int Partition(int [] arr,int low,int high) 
        { 
            int pivot=arr[low];//采用子序列的第一个元素作为枢纽元素 
            while (low < high) 
            { 
                //从后往前栽后半部分中寻找第一个小于枢纽元素的元素 
                while (low < high && arr[high] >= pivot) 
                { 
                    --high; 
                } 
                //将这个比枢纽元素小的元素交换到前半部分 
                swap(arr[low], arr[high]); 
                //从前往后在前半部分中寻找第一个大于枢纽元素的元素 
                while (low <high &&arr [low ]<=pivot ) 
                { 
                    ++low ; 
                } 
                swap (arr [low ],arr [high ]);//将这个枢纽元素大的元素交换到后半部分 
            } 
            return low ;//返回枢纽元素所在的位置 
        } 
        void QuickSort(int [] a,int low,int high) 
        { 
            if (low <high ) 
            { 
                int n=Partition (a ,low ,high ); 
                QuickSort (a ,low ,n ); 
                QuickSort (a ,n +1,high ); 
            } 
        } 
快速排序法:从待排的数据序列中任取一个数据作为分界值,小的在左,大的在右,进行排序。最好情况(每次选到中间值)时间复杂度为 O(nlog2n),最坏情况(每次总是选到最小或最大)O(n2),平均效率O(nlog2n),辅助空间O(logn)(每次都要分给一个额外空间,而总共有logn次), 适用于大的排列表
七、堆排序

 void resetHeap(int[] num, int s, int t) {

   int i = s;
   int x = num[s];
   for (int j = 2 * i; j <= t; j = 2 * j) {
    if (j < t && num[j] < num[j + 1])
     j = j + 1;// 找出较大者把较大者给num[i]
    if (x > num[j])
     break;
     num[i] = num[j];
    i = j;

   }

   num[i] = x;

 }

 

 void heapsort(int[] num, int n) {
   // 初始建堆从n/2开始向根调整

   int i;
   for (i = n / 2; i >= 1; i--) {

    resetHeap(num, i, n);//初始堆过程
   }

   for (i = n; i > 1; i--) {
    num[0] = num[i];// 将堆顶元素与第n,n-1,.....2个元素相交换
   num[i] = num[1];
    num[1] = num[0];// 从num[1]到num[i-1]调整成新堆
    resetHeap(num, 1, i - 1);

   }

  }


堆排序是指利用堆积术这种结构所设计的一种排序算法,可以利用数组的特点快速定位指定索引的元素。堆排序是不稳定的排序方法,空间复杂度为O(1),最坏时间复杂度为O(nlog2n),堆排序的平均性能接近于最坏性能,主要是建最大堆和反复建堆这两部分开销组成。建最大堆的时间为O(n),反复建堆的时间为O(log2n)。


八、基数排序


 int[] array = {3,2,9,2,5,322,1566,1178,78,990,12,432,56}; 

 radixSort(array,10,4);

 void radixSort(int[] array,int radix, int distance) { 

  //array为待排序数组  

  //radix,代表基数  

  //代表排序元素的位数  

 int length = array.length; 

 int[] copy= new int[length];//用于暂存元素  

 int[] count = new int[radix];//用于计数排序

 int divide 1;  

 for (int i = 0; i < distance; i++) { 

 System.arraycopy(array, 0,copy0, length);  

 Arrays.fill(count, 0);

 for (int j = 0; j < length; j++) {     

 int tempKey = (copy[j]/divide)%radix;  

 count[tempKey]++;  

}

for (int j = 1; j < radix; j++) { 

count [j] = count[j] + count[j-1];

}

for (int j = length - 1; j >= 0; j--) {  

int saveKey = (copy[j]/divide)%radix; 、

count[saveKey]--; 

array[count[saveKey]] = copy[j]; 

}

divide = divide * radix;

}

基数排序思路是将待排序数据拆分成多个关键字进行排序。(基数排序对任一子关键字进行排序时,必须借助另一种排序算法,而且算法必须是稳定的)。假定,待排序咧为n,k个关键值,关键值的取值范围为m,则时间复杂度为O(k(n+m)),其中一趟分配的时间复杂度O(n),一趟收集的时间复杂度为O(n),空间复杂度:O(mk+n),算法是稳定的。



评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值