简单排序的Java实现与效率分析
2012-12-03 15:14
232 查看
简单排序应该是编程中最基本的,一般在大学课本中有讲解,但是应该有许多同学和我一样没有在意,现在只好返工了。本次分析的简单排序包括冒泡排序、选择排序、插入排序。
首先我们准备一个要排序的数组,当然还有一些方法,基本如下:
冒泡排序是最简单的排序方法,基本上每个程序员都能不假思索,信手拈来。其算法简单,基本逻辑如下:
1. 取数组第一个数为基数,并将数组最后一个数标记为标志数。
2. 比较基数右边的数,如果基数大于此数,那么就交换两个数,否则不处理。
3. 令基数右边的数为新基数,重复2-3步,直到新基数为标志数为止(此数需执行2-3过程)。
4. 取数组第一个数为基数,,将原标志数左边的数标记为新标志数,重复2-4步,直到新标志数为第一个数为止(此数不执行2-4过程)。
其代码如下:
选择排序是冒泡排序的优化,虽然比较次数上没有改变,但在交换次数上大大减少。其逻辑如下:
1. 在数组取第一个数为基数,令标识位等于基数的下标。
2. 依次比较基数右边的数,如果此数小于标识位所指向的数,则令标识位等于此数的下标,直到数组最后一个为止(此数也要比较)。
3. 交换基数与标志位所指向的数。
4. 取基数右边的数为新基数,令标志数等于新基数的下标,重复2-4过程,直到数组倒数第二个数(此数要比较)。
其代码如下:
1. 在数组中取第二个数为基数,令缓冲数等于基数值。
2. 依次比较基数左边的数,若此数大于基数,则将此数右移(令此数右边的数等于此数。)直到有数小于基数为值(此数不要移动)。
3. 令数组最后右移的数等于缓冲数。
4. 取数组第三个数为新基数,令缓冲数等于基数值,重复2-4过程,直到最后一个数(此数需执行243过程)。
其代码如下:
从上面看,好像插入排序的算法并不比冒泡和选择简单。但是请注意,如果数组局部有序(那怕只有两个数是有序的),情况就大大不同。例如数组{2,3,4,5,6,1,0},数组中2,3,4,5,6属于局部有序。插入排序时,基数为2,3,4,5,6时,都不用处理,当基数为1时,方法只需要移动6次,基数为0时,移动7次。选择排序需要交换7次,冒泡排序需要交换21次,而一次交换至少需要移动2次。
为了测试效率,我们可以添加一些代码,整个类的代码如下:
当测试数为10时,结果如下:
当测试数为100时:结果如下:
当测试数为1000时:结果如下:
当测试数为10000时:结果如下:
参考《Java数据结构和算法》一书,冒泡排序交换和比较操作次数为 N*(N-1)/2,是与N2成正比(记作O(N2));选择排序交换虽然为O(N),但是比较时间与冒泡排序相同,还是O(N2);插入排序比较时间为 N*(N-1)/4。而移到次数与插入次数大致相同,两者近似O(N2),但是如果数据局部有有序,While循环基本为假,则算法所需的时间就能蜕变成O(N)。
简单排序一般适用于小数据量,大数据量排序还得高级排序(如快速排序等)。如果大家想深入研究可以参考《Java数据结构和算法》一书。
首先我们准备一个要排序的数组,当然还有一些方法,基本如下:
private int[] sortInts;//排序数组 // 初始化sortInts public SimpleSorts(int[] sortInts) { this.sortInts = sortInts; } // getter public int[] getSortInts() { return sortInts; } // setter public void setSortInts(int[] sortInts) { this.sortInts = sortInts; } // 显示数组 public void display() { System.out.println("数组:" + Arrays.toString(sortInts)); } // 交换元素 public void exchange(int first, int last) { int temp = sortInts[first]; sortInts[first] = sortInts[last]; sortInts[last] = temp; }
冒泡排序是最简单的排序方法,基本上每个程序员都能不假思索,信手拈来。其算法简单,基本逻辑如下:
1. 取数组第一个数为基数,并将数组最后一个数标记为标志数。
2. 比较基数右边的数,如果基数大于此数,那么就交换两个数,否则不处理。
3. 令基数右边的数为新基数,重复2-3步,直到新基数为标志数为止(此数需执行2-3过程)。
4. 取数组第一个数为基数,,将原标志数左边的数标记为新标志数,重复2-4步,直到新标志数为第一个数为止(此数不执行2-4过程)。
其代码如下:
// 冒泡排序 public void bubbleSort() { for (int i = sortInts.length - 1; i > 0; i--) { // 元素遍历 for (int j = 0; j < i; j++) { // 元素比较,比i大的已经排好 if (sortInts[j] > sortInts[j + 1]) // 比较大小,交换 { exchange(j, j + 1); } } } }
选择排序是冒泡排序的优化,虽然比较次数上没有改变,但在交换次数上大大减少。其逻辑如下:
1. 在数组取第一个数为基数,令标识位等于基数的下标。
2. 依次比较基数右边的数,如果此数小于标识位所指向的数,则令标识位等于此数的下标,直到数组最后一个为止(此数也要比较)。
3. 交换基数与标志位所指向的数。
4. 取基数右边的数为新基数,令标志数等于新基数的下标,重复2-4过程,直到数组倒数第二个数(此数要比较)。
其代码如下:
// 选择排序 public void selectSort() { for (int i = 0; i < sortInts.length; i++)// 元素遍历 { int temp = i; for (int j = i + 1; j < sortInts.length; j++)// 元素比较 { if (sortInts[temp] > sortInts[j])// 比较大小,缓存下标 { temp = j; } } if (temp != i)// 若下标有变,交换 { exchange(i, temp); } } }插入排序可以说是三者中最好的算法,尤其是在数据局部有序的情况下,其算法逻辑如下:
1. 在数组中取第二个数为基数,令缓冲数等于基数值。
2. 依次比较基数左边的数,若此数大于基数,则将此数右移(令此数右边的数等于此数。)直到有数小于基数为值(此数不要移动)。
3. 令数组最后右移的数等于缓冲数。
4. 取数组第三个数为新基数,令缓冲数等于基数值,重复2-4过程,直到最后一个数(此数需执行243过程)。
其代码如下:
//插入排序 public void InsertSort() { for (int i = 1; i < sortInts.length; i++) //元素遍历 { int temp = sortInts[i]; int j = i; while(j > 0 && temp < sortInts[j-1]) //元素比较,直到比temp小,停止循环 { sortInts[j] = sortInts[j-1]; j--; } if( j != i)//若下标有变,交换 { sortInts[j] = temp;//交换停止处元素与sortInts[i] } } }
从上面看,好像插入排序的算法并不比冒泡和选择简单。但是请注意,如果数组局部有序(那怕只有两个数是有序的),情况就大大不同。例如数组{2,3,4,5,6,1,0},数组中2,3,4,5,6属于局部有序。插入排序时,基数为2,3,4,5,6时,都不用处理,当基数为1时,方法只需要移动6次,基数为0时,移动7次。选择排序需要交换7次,冒泡排序需要交换21次,而一次交换至少需要移动2次。
为了测试效率,我们可以添加一些代码,整个类的代码如下:
import java.util.Arrays; import java.util.Random; public class SimpleSorts { // 排序数组、比较次数、复制次数 private int[] sortInts; private Long compareNum = 0L; private Long copyNum = 0L; // 初始化sortInts、setter、 public SimpleSorts(int[] sortInts) { this.sortInts = sortInts; } // getter public int[] getSortInts() { return sortInts; } // setter public void setSortInts(int[] sortInts) { compareNum = 0L; copyNum = 0L; this.sortInts = sortInts; } // 显示数组 public void display() { System.out.println("数组:" + Arrays.toString(sortInts)); } // 显示比较信息 public void displayNum() { System.out.println("统计: = 比较 " + compareNum + " 次, 复制 = " + copyNum + "次"); System.out.println(); } // 复制次数 public void addCopyNum() { copyNum++; } // 比较次数 public void addCompareNum() { compareNum++; } // 交换元素 public void exchange(int first, int last) { int temp = sortInts[first]; addCopyNum(); sortInts[first] = sortInts[last]; addCopyNum(); sortInts[last] = temp; addCopyNum(); } // 冒泡排序 public void bubbleSort() { for (int i = sortInts.length - 1; i > 0; i--) { // 元素遍历 for (int j = 0; j < i; j++) { // 元素比较,比i大的已经排好 if (sortInts[j] > sortInts[j + 1]) // 比较大小,交换 { exchange(j, j + 1); } addCompareNum(); } } } // 选择排序 public void selectSort() { for (int i = 0; i < sortInts.length; i++)// 元素遍历 { int temp = i; for (int j = i + 1; j < sortInts.length; j++)// 元素比较 { if (sortInts[temp] > sortInts[j])// 比较大小,缓存下标 { temp = j; } addCompareNum(); } if (temp != i)// 若下标有变,交换 { exchange(i, temp); } } } // 插入排序 public void InsertSort()// 元素遍历 { for (int i = 1; i < sortInts.length; i++) { int temp = sortInts[i]; addCopyNum(); int j = i; addCompareNum(); while (j > 0 && temp < sortInts[j - 1]) // 元素比较,直到比temp小,停止循环 { if (compareNum != 1) { addCompareNum(); } sortInts[j] = sortInts[j - 1]; addCopyNum(); j--; } if (j != i)// 若下标有变,交换 { sortInts[j] = temp;// 交换停止处元素与sortInts[i] addCopyNum(); } } } // 产生随机数组 public static int[] randomInts(int initNum) { int[] ri = new int[initNum]; for (int i = 0; i < ri.length; i++) { Random rd = new Random(); ri[i] = rd.nextInt(10000); } return ri; } // 主方法 public static void main(String[] args) { int[] a = randomInts(1000); int[] b = a.clone(); int[] c = a.clone(); // 冒泡 SimpleSorts ss = new SimpleSorts(a); ss.bubbleSort(); ss.displayNum(); // 选择 ss.setSortInts(b); ss.selectSort(); ss.displayNum(); // 插入 ss.setSortInts(c); ss.InsertSort(); ss.displayNum(); } }
当测试数为10时,结果如下:
统计: 比较 = 45 次, 复制 = 66次 统计: 比较 = 45 次, 复制 = 24次 统计: 比较 = 31 次, 复制 = 36次
当测试数为100时:结果如下:
统计: 比较 = 4950 次, 复制 = 6489次 统计: 比较 = 4950 次, 复制 = 291次 统计: 比较 = 2261 次, 复制 = 2356次
当测试数为1000时:结果如下:
统计: 比较 = 499500 次, 复制 = 773589次 统计: 比较 = 499500 次, 复制 = 2991次 统计: 比较 = 258861 次, 复制 = 259853次
当测试数为10000时:结果如下:
统计: = 比较 49995000 次, 复制 = 74298648次 统计: = 比较 49995000 次, 复制 = 29979次 统计: = 比较 24776215 次, 复制 = 24786200次通过比较我们可以看出,大数据下冒泡排序效率最低,选择排序虽然移动次数最少,但是比较次数高,而插入排序是三者中综合效率最好的。
参考《Java数据结构和算法》一书,冒泡排序交换和比较操作次数为 N*(N-1)/2,是与N2成正比(记作O(N2));选择排序交换虽然为O(N),但是比较时间与冒泡排序相同,还是O(N2);插入排序比较时间为 N*(N-1)/4。而移到次数与插入次数大致相同,两者近似O(N2),但是如果数据局部有有序,While循环基本为假,则算法所需的时间就能蜕变成O(N)。
简单排序一般适用于小数据量,大数据量排序还得高级排序(如快速排序等)。如果大家想深入研究可以参考《Java数据结构和算法》一书。
相关文章推荐
- 一步步学习数据结构和算法之常用排序效率分析及java实现
- 一步步学习数据结构和算法之选择排序效率分析及java实现
- 一步步学习数据结构和算法之快速排序效率分析及java实现
- 简单排序的Java实现与效率分析
- java实现三种冒号算法的效率分析
- 一步步学习数据结构和算法之快速排序效率分析及java实现
- 一步步学习数据结构和算法之直接插入排序效率分析及java实现
- 一步步学习数据结构和算法之希尔排序效率分析及java实现
- 一步步学习数据结构和算法之归并排序效率分析及java实现
- 一步步学习数据结构和算法之折半插入排序效率分析及java实现
- 一步步学习数据结构和算法之冒泡排序效率分析及java实现
- 一步步学习数据结构和算法之堆排序效率分析及java实现
- java String与StringBuffer的效率分析
- java实现网上在线支付--03_分析易宝支付网关的请求协议
- 数据结构java实现之简单排序
- Kafka中时间轮分析与Java实现
- Java原子类实现原理分析
- Java 内部类实现原理简单分析
- Java NIO原理图文分析及代码实现