(一)(1)直接插入排序
直接插入排序(Straight Insertion Sorting)的基本思想:在要排序的一组数中,假设前面(n-1) [n>=2] 个数已经是排好顺序的,现在要把第n个数插到前面的有序数中,使得这n个数也是排好顺序的。如此反复循环,直到全部排好顺序。
复杂度:时间复杂度 O(n2) ,空间复杂度O(1)
稳定性: 插入排序是稳定的,排序前后两个相等元素相对次序不变(能保证排序前2个相等的数其在序列的前后位置顺序和排序后它们两个的前后位置顺序相同。在简单形式化一下,如果Ai = Aj, Ai原来在位置前,排序后Ai还是要在Aj位置前。)
结构的复杂性及适用情况:是一种简单的排序方法,不仅适用于顺序存储结构(数组),而且适用于链接存储结构,不过在链接存储结构上进行直接插入排序时,不用移动元素的位置,而是修改相应的指针。
哨兵的作用
算法中引进的附加记录R[0]称监视哨或哨兵(Sentinel)。
哨兵有两个作用:
① 进人查找(插入位置)循环之前,它保存了R[i]的副本,使不致于因记录后移而丢失R[i]的内容;
② 它的主要作用是:在查找循环中"监视"下标变量j是否越界。一旦越界(即j=0),因为R[0].key和自己比较,循环判定条件不成立使得查找循环结束,从而避免了在该循环内的每一次均要检测j是否越界(即省略了循环判定条件"j>=1")。
注意:
① 实际上,一切为简化边界条件而引入的附加结点(元素)均可称为哨兵。
【例】单链表中的头结点实际上是一个哨兵
② 引入哨兵后使得测试查找循环条件的时间大约减少了一半,所以对于记录数较大的文件节约的时间就相当可观。对于类似于排序这样使用频率非常高的算法,要尽可能地减少其运行时间。所以不能把上述算法中的哨兵视为雕虫小技,而应该深刻理解并掌握这种技巧。
JAVA源代码(成功运行):
- public static void straightInsertionSort(int []array){
- int sentinel , j;
- for (int i = 1; i < array.length; i++) {
- j = i - 1;
- sentinel = array[i];//哨兵位
- while (j >= 0 && sentinel < array[j]) {
- array[j+1] = array[j];//将大于sentinel的值整体后移一个单位
- j--;
- }
- array[j+1] = sentinel;
- }
- }
(2)直接插入排序
插入排序算法是一个对少量元素进行排序的有效算法。插入排序的工作原理与打牌时整理手中的牌的做法类似,开始摸牌时,我们的左手是空的,接着一次从桌上摸起一张牌,并将它插入到左手的正确位置。为了找到这张牌的正确位置,要将它与手中已有的牌从右到左进行比较,无论什么时候手中的牌都是排序好的。
JAVA实现该算法如下:
- public void insertSort(int a[]){
- int length=a.length; //数组长度
- int j; //当前值的位置
- int i; //指向j前的位置
- int key; //当前要进行插入排序的值
- //从数组的第二个位置开始遍历值
- for(j=1;j<length;j++){
- key=a[j];
- i=j-1;
- //a[i]比当前值大时,a[i]后移一位,空出i的位置,好让下一次循环的值后移
- while(i>=0 && a[i]>key){
- a[i+1]=a[i]; //将a[i]值后移
- i--; //i前移
- }//跳出循环(找到要插入的中间位置或已遍历到0下标)
- a[i+1]=key; //将当前值插入
- }
- }
public void insertSort(int a[]){
int length=a.length; //数组长度
int j; //当前值的位置
int i; //指向j前的位置
int key; //当前要进行插入排序的值
//从数组的第二个位置开始遍历值
for(j=1;j<length;j++){
key=a[j];
i=j-1;
//a[i]比当前值大时,a[i]后移一位,空出i的位置,好让下一次循环的值后移
while(i>=0 && a[i]>key){
a[i+1]=a[i]; //将a[i]值后移
i--; //i前移
}//跳出循环(找到要插入的中间位置或已遍历到0下标)
a[i+1]=key; //将当前值插入
}
}
事例图如下:
insertSort在数组A={5,2,4,6,1,3}上的处理过程,数组的下标出现在巨型的上方,黑色框的值即为key=a[j]。
a[i]即为a[j]左边的值,每次循环key与a[i]进行比较,如果key<a[i],则a[i]移到 i+1处,同时i--向左移动,直到找到a[i]<=key或者i<0,此时将 key插入到a[i+1]处。
问题1:a[i]第一次进行右移时覆盖了a[i+1]值,是否造成了数据丢失?
答:不会造成数据丢失,应为第一次右移前的i+1=j,此时a[j]的值存在了key里了。每次右移都为下一次右移或插入留出了位置,这就是插入排序的关键点所在。
![](http://images.cnitblog.com/blog/420264/201307/24170221-5e84d6b12ea545618b673783789b5803.png)
1 package com.sort; 2 3 //不稳定 4 public class 简单的选择排序 { 5 6 public static void main(String[] args) { 7 int[] a={49,38,65,97,76,13,27,49,78,34,12,64,1,8}; 8 System.out.println("排序之前:"); 9 for (int i = 0; i < a.length; i++) { 10 System.out.print(a[i]+" "); 11 } 12 //简单的选择排序 13 for (int i = 0; i < a.length; i++) { 14 int min = a[i]; 15 int n=i; //最小数的索引 16 for(int j=i+1;j<a.length;j++){ 17 if(a[j]<min){ //找出最小的数 18 min = a[j]; 19 n = j; 20 } 21 } 22 a[n] = a[i]; 23 a[i] = min; 24 25 } 26 System.out.println(); 27 System.out.println("排序之后:"); 28 for (int i = 0; i < a.length; i++) { 29 System.out.print(a[i]+" "); 30 } 31 } 32 33 }
4、分析
简单选择排序是不稳定的排序。
时间复杂度:T(n)=O(n2)。
(三)冒泡排序法:关键字较小的记录好比气泡逐趟上浮,关键字较大的记录好比石块下沉,每趟有一块最大的石块沉底。
算法本质:(最大值是关键点,肯定放到最后了,如此循环)每次都从第一位向后滚动比较,使最大值沉底,最小值上升一次,最后一位向前推进(即最后一位刚确定的最大值不再参加比较,比较次数减1)
复杂度: 时间复杂度 O(n2) ,空间复杂度O(1)
3、java实现
1 package com.sort; 2 3 //稳定 4 public class 冒泡排序 { 5 public static void main(String[] args) { 6 int[] a={49,38,65,97,76,13,27,49,78,34,12,64,1,8}; 7 System.out.println("排序之前:"); 8 for (int i = 0; i < a.length; i++) { 9 System.out.print(a[i]+" "); 10 } 11 //冒泡排序 12 for (int i = 0; i < a.length; i++) { 13 for(int j = 0; j<a.length-i-1; j++){ 14 //这里-i主要是每遍历一次都把最大的i个数沉到最底下去了,没有必要再替换了 15 if(a[j]>a[j+1]){ 16 int temp = a[j]; 17 a[j] = a[j+1]; 18 a[j+1] = temp; 19 } 20 } 21 } 22 System.out.println(); 23 System.out.println("排序之后:"); 24 for (int i = 0; i < a.length; i++) { 25 System.out.print(a[i]+" "); 26 } 27 } 28 }
4、分析
冒泡排序是一种稳定的排序方法。