Java内部排序之插入排序与交换排序详解
作者:菜鸟-小胖
内部排序
排序:将任意序列重新排列按照关键字有序;
排序根基存储器的不同分为:内部排序、外部排序;(这里指的都是内部排序)
排序根据关键字分为:稳定排序、不稳定排序
排序根据不同的原则分为:插入排序、交换排序、选择排序、归并排序、基数排序;
排序根据时间复杂度分为:简单排序(O(N2))、先进排序(O(nlogn))、基数排序(O(d*n))
排序的操作:比较、移动 被称为基于比较的排序;
假设这里使用的存储结构均为:顺序存储结构;
内部排序:
1、插入排序:直接插入排序、折半插入、2-路插入排序、希尔排序
2、交换排序:冒泡排序、快速排序
3、选择排序:简单选择排序、堆排序
4、归并排序
5、基数排序
插入排序
1直接插入排序
由n-1趟排序组成; 对于P=1趟到P=N-1趟,插入排序保证从位置0到位置P上的元素为已排序状态;
第P趟,我们将位置P上的元素向左移动到它在前P+1个元素中正确位置上。(这里需要使用一个哨兵元素)
直接插入程序示例:
public class InserSort { public static void insertSort(int a[]){ int temp;//哨兵 int i;//执行趟数 int j; for (i = 1; i < a.length; i++) {//执行N-1趟 temp=a[i]; for ( j = i-1; (j >=0)&&(a[j]>temp); j--) { a[j+1]=a[j]; } a[j+1]=temp; } } public static void main(String[] args) { int a[]={9,6,3,2,7,5}; InserSort.insertSort(a); for (int i = 0; i < a.length; i++) { System.out.print(a[i]); } } }
算法分析:
排序的操作:比较、移动
1、空间复杂度: 只需要一个记录的辅助空间;
2、时间复杂度:O(N2) 如果已有序时间复杂度为:O(N)
3、适应场景:数据量不是很大 、基本有序
2 折半插入排序
将查找工作交与“折半查找”来实现。定位后,后续移动元素。
public class BInsertSort { /** * 折半插入排序 * @param a */ public static void BinsertSort(int a[]){ int temp;//哨兵 for (int i = 1; i < a.length; i++) { temp=a[i]; int low=0; int hight=i-1; //查询 while (low<=hight) { int mad=(low+hight)/2; if (a[mad]>a[i]) { hight=mad-1; } if (a[mad]<a[i]) { low=mad+1; } } //移动 for (int k = i-1; k >=hight+1; k--) { a[k+1]=a[k]; } a[hight+1]=temp; } } public static void main(String[] args) { int a[]={9,6,3,2,7,5}; BInsertSort.BinsertSort(a); for (int i = 0; i < a.length; i++) { System.out.print(a[i]); } } }
折半插入排序减少关键字的比较次数,但是记录的移动次数没有改变。
时间复杂度:O(N2)
3 希尔排序(缩小增量排序)
基本思想:先将整个待排序记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录“基本有序”时,在对整个记录进行一次直接插入排序。
希尔排序使用一个序列,叫做增量序列。
序列最终值为1 PS:增量序列中的值没有除1之外的公因子,并且最后一个增量必须等于1;
算法示例: 增量序列最流行的选择是使用shell建议的序列N/2
public class ShellSort { /** * 希尔排序 * @param a */ public static void shellSort(int a[]){ int i,j; int inc;//增量 int temp;//哨兵 for ( inc = a.length/2; inc >0; inc/=2) {//设置增量队列 //一趟希尔排序 for ( i = inc; i < a.length; i++) { temp=a[i]; for ( j = i; j >= inc; j-=inc) { if (temp<a[j-inc]) { a[j]=a[j-inc]; }else { break; } } a[j]=temp; } } } public static void main(String[] args) { int a[]={9,6,3,2,7,5}; shellSort(a); for (int i = 0; i < a.length; i++) { System.out.print(a[i]); } } }
算法分析: 希尔排序的运算时间依赖于增量序列的选择,最坏情况O(N2)
使用场景: 适度地大量的输入数据
交换排序
交换排序:冒泡排序、快速排序
1 冒泡排序
思路: 一趟排序:将第一个记录的关键字与第二个关键字进行比较,若为逆序记录交换,然后比较第二个与第三个,以此类推,直到地N-1个记录与N个记录比较位置。
其结果使得最大的记录安置在最后一个记录位置; 第二趟对n-1个记录进行冒泡排序。
判断冒泡排序结束的条件:在一趟排序中没有进行过交换记录的操作。 算法演示:
public class BubbleSort { /** * 冒泡排序 * @param a */ public static void bubbleSort(int a[]){ int temp=0; int log=0;//1表示有交换 0表示没有交换 作为结束标识 for (int i = a.length-1; i >1; i--) { for (int j = 0; j < i; j++) { if (a[j+1]<a[j]) { temp=a[j+1]; a[j+1]=a[j]; a[j]=temp; log=1; } } if (log==0) { break; } } } public static void main(String[] args) { int a[]={2,3,5,6,7,9}; BubbleSort.bubbleSort(a); for (int i = 0; i < a.length; i++) { System.out.print(a[i]); } } }
算法分析: 时间复杂度:O(N2)
2快速排序
快速排序(Quick Sort)的基本思想:通过一趟排序将待排记录分割成独立的两部分,其中一部分记录的关键字均比另一部分记录的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。
首先选择一个记录(可选择第一个记录,不是最佳的做法)作为支点(pivot),然后按下述原则重新排列其余记录:将所有关键字较它小的记录都安置在它的位置之前,将所有关键字较它大的记录都安置在它位置之后。
由此可以该支点记录最后所落的位置i作为分界线,这就是一趟快速排序。 具体做法:附设两个指针low和high,它们的初值分别指向序列的前端与后端,设支点的记录关键字为pivotkey,则首先从high所指位置起向前搜索找到第一个关键字小于pivotkey的记录和支点记录交换,然后从low所指位置起向后搜索,找到第一个关键字大于pivotkey的记录和支点记录相互交换,重复这两步直到low=high为止。
算法示例:
public class QuickSort { /** * 一趟快排序 */ public static int partition(int a[],int low ,int high) { int temp = a[low];// 支点 while (low < high) { while ((low < high)&&(a[high] >= temp) ) { high--; } a[low]=a[high]; while ((low < high)&&(a[low] <= temp)) { low++; } a[high]=a[low]; } a[low]=temp; return low; } /** * 递归排序 * @param a * @param low * @param high */ public static void QSort(int a[],int low,int high){ //对子序列进行排序 if (low<high) { int pivot=partition(a, low , high); QSort(a,low, pivot-1); QSort(a,pivot+1,high); } } /** * 快速排序 * @param a */ public static void QuickSort(int a[]){ QSort(a, 0, a.length-1); } //测试 public static void main(String[] args) { int a[]={7,6,3,8,4,9,7}; QuickSort.QuickSort(a); for (int i = 0; i < a.length; i++) { System.out.print(a[i]); } } }
算法分析:
1、时间复杂度: 快速排序的平均时间为:O(nlogn) 就平均时间而言,快排序是目前被认为最好的一种内部排序方法; 较坏的情况:当关键字基本有序或者有序时,快速排序将托变为冒泡排序,时间复杂度:O(n2); 支点的选取可以优化该算法,一般认为“三者取中”的做法最合适:即a[i],a[j],a[(i+j)/2],取三者的中值。这样可以改善最坏情况下的性能。
2、空间复杂度: 平均空间复杂度为:O(log2N) 因为快速排序需要一个栈空间来实现递归。 一般如果一趟排序记录均匀分布在支点两侧,栈的深度为log2N+1 最坏情况,一趟排序后,都跑到一端空间,深度为N 降低空间复杂度的方法: 在一趟排序之后比较分割所得两部分的长度,且先对长度短的子序列中的记录进行快速排序,则栈的最大深度可降为O(logn).
到此这篇关于Java内部排序之插入排序与交换排序详解的文章就介绍到这了,更多相关Java插入排序与交换排序内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!