转载:Java常用的几种排序算法 。

16 篇文章 0 订阅
常见的排序算法之Java代码解释
一 简要介绍
一般排序均值的是将一个已经无序的序列数据重新排列成有序的
常见的排序分为:
1 插入类排序
主要就是对于一个已经有序的序列中,插入一个新的记录。它包括:直接插入排序,折半插入排序和希尔排序
2 交换类排序
这类排序的核心就是每次比较都要“交换”,在每一趟排序都会两两发生一系列的“交换”排序,但是每一趟排序都会让一个记录排序到它的最终位置上。它包括:起泡排序,快速排序
3 选择类排序
每一趟排序都从一系列数据中选择一个最大或最小的记录,将它放置到第一个或最后一个为位置交换,只有在选择后才交换,比起交换类排序,减少了交换记录的时间。属于它的排序:简单选择排序,堆排序
4 归并类排序
将两个或两个以上的有序序列合并成一个新的序列
5 基数排序
主要基于多个关键字排序的。
下面针对上面所述的算法,讲解一些常用的java代码写的算法
二 插入类排序之直接插入排序
直接插入排序,一般对于已经有序的队列排序效果好。
基本思想:每趟将一个待排序的关键字按照大小插入到已经排序好的位置上。

算法思路,从后往前先找到要插入的位置,如果小于则就交换,将元素向后移动,将要插入数据插入该位置即可。时间复杂度为O(n2),空间复杂度为O(1)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package sort.algorithm;
public class DirectInsertSort {
     public static void main(String[] args) {
         // TODO Auto-generated method stub
         int data[] = { 2 , 6 , 10 , 3 , 9 , 80 , 1 , 16 , 27 , 20 };
         int temp, j;
         for ( int i = 1 ; i < data.length; i++) {
             temp = data[i];
             j = i - 1 ;
             // 每次比较都是对于已经有序的
             while (j >= 0 && data[j] > temp) {
                 data[j + 1 ] = data[j];
                 j--;
             }
             data[j + 1 ] = temp;
         }
         // 输出排序好的数据
         for ( int k = 0 ; k < data.length; k++) {
             System.out.print(data[k] + "  " );
         }
     }
}


插入类排序之折半插入排序(二分法排序)
条件: 在一个已经有序的队列中,插入一个新的元素
折半插入排序记录的比较次数与初始序列无关
思想:折半插入就是首先将队列中取最小位置low和最大位置high,然后算出中间位置mid
将中间位置mid与待插入的数据data进行比较,
如果mid大于data,则就表示插入的数据在mid的左边,high=mid-1;

如果mid小于data,则就表示插入的数据在mid的右边,low=mid+1

最后整体进行右移操作。

时间复杂度O(n2),空间复杂度O(1)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package sort.algorithm;
//折半插入排序
public class HalfInsertSort {
     public static void main(String[] args) {
         int data[] = { 2 , 6 , 10 , 3 , 9 , 80 , 1 , 16 , 27 , 20 };
         // 存放临时要插入的元素数据
         int temp;
         int low, mid, high;
         for ( int i = 1 ; i < data.length; i++) {
             temp = data[i];
             // 在待插入排序的序号之前进行折半插入
             low = 0 ;
             high = i - 1 ;
             while (low <= high) {
                 mid = (low + high) / 2 ;
                 if (temp < data[mid])
                     high = mid - 1 ;
                 else
                     // low=high的时候也就是找到了要插入的位置,
                     // 此时进入循环中,将low加1,则就是要插入的位置了
                     low = mid + 1 ;
             }
             // 找到了要插入的位置,从该位置一直到插入数据的位置之间数据向后移动
             for ( int j = i; j >= low + 1 ; j--)
                 data[j] = data[j - 1 ];
             // low已经代表了要插入的位置了
             data[low] = temp;
         }
         for ( int k = 0 ; k < data.length; k++) {
             System.out.print(data[k] + "  " );
         }
     }
}
插入类排序之希尔排序
希尔排序,也叫缩小增量排序,目的就是尽可能的减少交换次数,每一个组内最后都是有序的。
将待续按照某一种规则分为几个子序列,不断缩小规则,最后用一个直接插入排序合成
空间复杂度为O(1),时间复杂度为O(nlog2n)

算法先将要排序的一组数按某个增量d(n/2,n为要排序数的个数)分成若干组,每组中记录的下标相差d.对每组中全部元素进行直接插入排序,然后再用一个较小的增量(d/2)对它进行分组,在每组中再进行直接插入排序。当增量减到1时,进行直接插入排序后,排序完成。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package sort.algorithm;
public class ShellSort {
     public static void main(String[] args) {
         int a[] = { 1 , 54 , 6 , 3 , 78 , 34 , 12 , 45 , 56 , 100 };
         double d1 = a.length;
         int temp = 0 ;
         while ( true )
         {
             //利用这个在将组内倍数减小
             //这里依次为5,3,2,1
             d1 = Math.ceil(d1 / 2 );
             //d为增量每个分组之间索引的增量
             int d = ( int ) d1;
             //每个分组内部排序
             for ( int x = 0 ; x < d; x++)
             {
                 //组内利用直接插入排序
                 for ( int i = x + d; i < a.length; i += d) {
                     int j = i - d;
                     temp = a[i];
                     for (; j >= 0 && temp < a[j]; j -= d) {
                         a[j + d] = a[j];
                     }
                     a[j + d] = temp;
                 }
             }
                                                                                                                                     
             if (d == 1 )
                 break ;
         }
         for ( int i = 0 ; i < a.length; i++)
             System.out.print(a[i]+ "  " );
     }
}


五 交换类排序之冒泡排序

交换类排序核心就是每次比较都要进行交换
冒泡排序:是一种交换排序
每一趟比较相邻的元素,较若大小不同则就会发生交换,每一趟排序都能将一个元素放到它最终的位置!每一趟就进行比较。

时间复杂度O(n2),空间复杂度O(1)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package sort.algorithm;
//冒泡排序:是一种交换排序
public class BubbleSort {
     // 按照递增顺序排序
     public static void main(String[] args) {
         // TODO Auto-generated method stub
         int data[] = { 2 , 6 , 10 , 3 , 9 , 80 , 1 , 16 , 27 , 20 , 13 , 100 , 37 , 16 };
         int temp = 0 ;
         // 排序的比较趟数,每一趟都会将剩余最大数放在最后面
         for ( int i = 0 ; i < data.length - 1 ; i++) {
             // 每一趟从开始进行比较,将该元素与其余的元素进行比较
             for ( int j = 0 ; j < data.length - 1 ; j++) {
                 if (data[j] > data[j + 1 ]) {
                     temp = data[j];
                     data[j] = data[j + 1 ];
                     data[j + 1 ] = temp;
                 }
             }
         }
         for ( int i = 0 ; i < data.length; i++)
             System.out.print(data[i] + " " );
     }
}


六 交换类排序之快速排序
快速排序算法,初始的时候选择一个轴线,一般来说选择第一个元素,每一趟排序交换后,最后出现的就是该轴左边比它小,右边比他大!交替扫描,先从右边开始扫描,如果遇到比它小的就停止,将该元素与轴线交换,马上换成从左开始扫描,如果遇到比它大的就停止,将该元素与轴线数据交换,重复这样的!一般就是递归做的
时间复杂度O(nlog2n),平均时间是最好的

空间复杂度O(long2n),快速排序需要递归用到了栈

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
package sort.algorithm;
//快速排序算法:是一种交换排序
public class QuikSort {
     public static void main(String[] args) {
         // TODO Auto-generated method stub
         int data[] = { 2 , 6 , 10 , 3 , 9 , 80 , 1 , 16 , 27 , 20 , 105 , 34 , 44 , 19 };
         QuikSort sort = new QuikSort();
         sort.sortArray(data, 0 , data.length - 1 );
         for ( int i = 0 ; i < data.length; i++)
             System.out.print(data[i] + " " );
     }
     // 这里不用返回值,直接对传入的数组进行操作
     public void sortArray( int data[], int first, int end) {
         int temp;
         int i = first, j = end;
         if (first < end) {
             temp = data[i];
             // 当i=j的时候,则说明扫描完成了
             while (i < j) {
                 // 从右边向左边扫描找到一个小于temp的元素
                 while (j > i && data[j] > temp)
                     j--;
                 if (i < j) {
                     // 将该元素赋值给temp
                     data[i] = data[j];
                     // 赋值后就应该将i+1指向下一个序号
                     i++;
                 }
                 // 然后从左边向右边开始扫描,找到一个大于temp的元素
                 while (i < j && temp > data[i])
                     i++;
                 if (i < j) {
                     // 将该元素赋值给temp
                     data[j] = data[i];
                     // 赋值后就应该将j-1指向前一个序号
                     j--;
                 }
             }
             // 将轴数据放在i位置中
             data[i] = temp;
             sortArray(data, first, i - 1 );
             sortArray(data, i + 1 , end);
         }
     }
}


七 选择类排序之简单选择排序

简单选择排序,每一趟从数据中选择一个最小值放到最前面,但是不需要交换位置,只记录该交换的位置,只有找到后才做一次交换!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package sort.algorithm;
//简单选择排序:是一种选择排序
public class SelectSort {
     public static void main(String[] args) {
         // TODO Auto-generated method stub
         int data[] = { 2 , 6 , 10 , 3 , 9 , 80 , 1 , 16 , 27 , 20 , 11 , 3 , 1 , 100 , 89 };
         int temp, k;
         // 开始时间
         long start = System.nanoTime();
         // 选择的每一趟数,每一趟都会将一个最小的放在最前面。
         for ( int i = 0 ; i < data.length - 1 ; i++) {
             // 使用k来记录要交换的位置,且k在比较的过程不断变化
             k = i;
             // 由于每一趟都会将最小的放在最前面,所以索引+1
             for ( int j = i; j < data.length; j++)
                 // 这里始终要与k比较
                 if (data[j] < data[k])
                     k = j;
             // k已经存放了交换的位置了
             temp = data[i];
             data[i] = data[k];
             data[k] = temp;
         }
         System.out.println(System.nanoTime() - start);
         // 输出排序好的数据
         for ( int m = 0 ; m < data.length; m++) {
             System.out.print(data[m] + "  " );
         }
     }
}


选择类排序之堆排序

堆排序就是建立大顶堆或者小顶堆,若建立大顶堆,每次对于建好的大顶堆将根元素与最后一个元素交换,无序的数目减少,有序的数目增加。

对于求N个数据中的前n个最小的数据,首先就是建立一个n个的大顶堆,然后让其余的元素来进行与这堆顶元素比较,如果小于则与堆顶互换元素。

这里采用数组存储节点,并且下标统一从0,length-1,所以对于这样处理的左孩子节点下标为

2 * i+1,右孩子的节点下标为2 * i+2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
package sort.algorithm;
public class HeapSort {
     public static int heap_size;
     // 左孩子编号
     public static int leftChild( int i) {
         return 2 * i+ 1 ;
     }
     // 右孩子编号
     public static int rightChild( int i) {
         return 2 * i + 2 ;
     }
     /**
      * 保持最大堆的性质
      * 堆中的数组元素
      * 对以该元素为根元素的堆进行调整,假设前提:左右子树都是最大堆
      * 由于左右孩子都是最大堆,首先比较根元素与左右孩子,找出最大值,假如大于根元素,则调整两个元素的值;
      * 由于左孩子(右孩子)的值与根元素交换,有可能打破左子树(右子树)的最大堆性质,因此继续调用,直至叶子元素。
      */
     public static void max_heapify( int [] a, int i) {
         int left = leftChild(i);
         int right = rightChild(i);
         int largest = 0 ;
                                                                                                                                                                                                                                                                                  
         if (left < heap_size && a[i] < a[left]) {
             largest = left;
         } else {
             largest = i;
         }
                                                                                                                                                                                                                                                                                  
         if (right < heap_size && a[right] > a[largest]) {
             largest = right;
         }
         if (largest == i) {
             return ;
         } else {
             int temp = a[i];
             a[i] = a[largest];
             a[largest] = temp;
             max_heapify(a, largest);
         }
     }
     /**
      * 建立最大堆。在数据中,下标a.length/2+1一直到最后的元素a.length-1都是叶子元素
      * 因此从其前一个元素开始,一直到
      * 第一个元素,重复调用max_heapify函数,使其保持最大堆的性质
      */
     public static void build_max_heap( int [] a) {
         //从0~a.length/2中建立最大堆
         for ( int i = a.length / 2 ; i >= 0 ; i--)
         {
             max_heapify(a, i);
         }
     }
     /**
      * 堆排序:首先建立最大堆,然后将堆顶元素(最大值)与最后一个值交换,同时使得 堆的长度减小1
      * 调用保持最大堆性质的算法调整,使得堆顶元素成为最大值,此时最后一个元素已被排除在外、
      */
     public static void heapSort( int [] a) {
         //构建最大堆
         build_max_heap(a);
         for ( int i = a.length - 1 ; i >= 0 ; i--)
         {
             //将第一个元素和最后一个元素进行互换
             int temp = a[ 0 ];
             a[ 0 ] = a[i];
             a[i] = temp;
                                                                                                                                                                                                                                                                                      
             heap_size--;
             //调整堆为最大堆
             max_heapify(a, 0 );
         }
     }
     public static void main(String[] args) {
         int a[] = { 5 , 4 , 1 , 3 , 2 , 16 , 9 , 10 , 14 , 8 , 7 };
         heap_size = a.length; //最大数
         heapSort(a);
         //输出结果
         for ( int i = 0 ; i < a.length; i++) {
             System.out.print(a[i] + "  " );
         }
     }
}


九 二路归并排序

归并排序主要分为分割和归并,每次分割后,对于每一个部分进行排序,然后进行归并,建立一个临时表存储归并后的结果,在将两路进行归并的时候,每一路都已经是有序的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
package sort.algorithm;
import java.util.Arrays;
//二路归并排序主要分为
//分割和合并
public class MergeSort {
     public static void main(String[] args) {
         int data[] = { 2 , 6 , 10 , 3 , 9 , 80 , 1 , 16 , 27 , 20 };
         mergeSort(data, 0 ,data.length- 1 );
         //直接打印
         System.out.println(Arrays.toString(data));
     }
     //二路归并的分割处理
     public static void mergeSort( int [] array, int start, int end)
     {
         if (start<end)
         {
             //划分为两部分,每次两部分进行归并
             int mid=(start+end)/ 2 ;
             //两路归并
             //先递归处理每一个部分
             mergeSort(array,start,mid);
             mergeSort(array,mid+ 1 ,end);
             //然后将已经排序好的,两两归并排序再进行合并处理
             merge(array,start,mid,mid+ 1 ,end);
         }
     }
     //二路归并两个部分的时候进行排序
     public static void merge( int [] array, int start1, int end1, int start2, int end2)
     {
         int i=start1; //左路起始索引
         int j=start2; //右路起始索引
         int k= 0 ;
         //归并的时候,会将两个数组数据按照大小输入到一个临时数组中
         //建立临时长度为两个子列表长度的数组
         int [] temp= new int [end2-start1+ 1 ];
         //循环遍历,按顺序找出两个表中的最小数据依次放入临时表中
         //注意此时左路和右路已经是有序的了。
         //当一路有一个小的,则会索引加1,继续喝另外一路的上次索引进行比较
         while (i<=end1&&j<=end2)
         {
             //这里确定归并的次序大小
             if (array[i]>array[j])
                 temp[k++]=array[j++];
             else
                 temp[k++]=array[i++];
         }
         //把剩下的元素放入临时数组中,只有一路的
         while (i<=end1)
             temp[k++]=array[i++];
         while (j<=end2)
             temp[k++]=array[j++];
         k=start1;
         for ( int item:temp)
             array[k++]=item;
     }
}



十 各种排序总结:

时间复杂度:巧记“快些以nlog2n归队”,快代表快速排序,些代表希尔排序,归代表归并排序,队代表堆排序
算法稳定性:巧记“心情不稳定,快些选人吧”,快代表快速排序,些代表希尔排序,选代表选择排序,队代表堆排序
从一大堆中选择最大的几个或者最小的几个数,直接用堆排序
原始序列有序号,直接用插入排序
经过一趟排序能使一个元素达到它最终位置的是交换类排序(冒泡,快速)和选择类排序(简单选择,堆)。
排序方法元素比较次数与原始序列无关---简单选择排序,折半插入排序
排序方法的排序趟数和原始队列无有关--交换类排序

附带一份排序算法好坏情况表图

wKioL1M4MjKyMmCXAAMuifz5OeI331.jpg

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在信号处理领域,DOA(Direction of Arrival)估计是一项关键技术,主要用于确定多个信号源到达接收阵列的方向。本文将详细探讨三种ESPRIT(Estimation of Signal Parameters via Rotational Invariance Techniques)算法在DOA估计中的实现,以及它们在MATLAB环境中的具体应用。 ESPRIT算法是由Paul Kailath等人于1986年提出的,其核心思想是利用阵列数据的旋转不变性来估计信号源的角度。这种算法相比传统的 MUSIC(Multiple Signal Classification)算法具有较低的计算复杂度,且无需进行特征值分解,因此在实际应用中颇具优势。 1. 普通ESPRIT算法 普通ESPRIT算法分为两个主要步骤:构造等效旋转不变系统和估计角度。通过空间平移(如延时)构建两个子阵列,使得它们之间的关系具有旋转不变性。然后,通过对子阵列数据进行最小二乘拟合,可以得到信号源的角频率估计,进一步转换为DOA估计。 2. 常规ESPRIT算法实现 在描述中提到的`common_esprit_method1.m`和`common_esprit_method2.m`是两种不同的普通ESPRIT算法实现。它们可能在实现细节上略有差异,比如选择子阵列的方式、参数估计的策略等。MATLAB代码通常会包含预处理步骤(如数据归一化)、子阵列构造、旋转不变性矩阵的建立、最小二乘估计等部分。通过运行这两个文件,可以比较它们在估计精度和计算效率上的异同。 3. TLS_ESPRIT算法 TLS(Total Least Squares)ESPRIT是对普通ESPRIT的优化,它考虑了数据噪声的影响,提高了估计的稳健性。在TLS_ESPRIT算法中,不假设数据噪声是高斯白噪声,而是采用总最小二乘准则来拟合数据。这使得算法在噪声环境下表现更优。`TLS_esprit.m`文件应该包含了TLS_ESPRIT算法的完整实现,包括TLS估计的步骤和旋转不变性矩阵的改进处理。 在实际应用中,选择合适的ESPRIT变体取决于系统条件,例如噪声水平、信号质量以及计算资源。通过MATLAB实现,研究者和工程师可以方便地比较不同算法的效果,并根据需要进行调整和优化。同时,这些代码也为教学和学习DOA估计提供了一个直观的平台,有助于深入理解ESPRIT算法的工作原理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值