排序算法总结
参考文献:
[1]吴伟民,李小妹,刘添添,黄剑锋,苏庆,林志毅,李杨.数据结构(校内教材修订版) 广东工业大学计算机学院,2015.6;
目录:
1、排序算法时间复杂度、空间复杂度、稳定性总结:
2、程序源码:
3、运行结果截图:
1、排序算法时间复杂度、空间复杂度、稳定性总结:
对随机产生0-99999范围内的十万条整型数据进行排序,查看测试结果,得到结果:
图1.1 排序算法时间复杂度、空间复杂度、稳定性
(1)内部排序:待排序序列完全存放在内存中所进行的排序过程,适合不太大的元素序列。
(2)外部排序:大文件的排序,待排序的文件无法一次装入内存,将待排序的记录存储在外存储器上,需要在内存和外部存储器之间进行多次数据交换,以达到排序整个文件的目的。
(3)交换排序:对无序区中记录的关键字两两进行比较,若逆序则交换,直到关键字之间不存在逆序为止。
(4)选择排序:在无序区中选出关键字最小的记录,置于有序区的最后,直到全部记录有序。
(5)插入排序:将无序区中的一个记录插入到有序区,使得有序区的长度加1,直到全部记录有序。
(6)归并排序:不断将两个或两个以上的有序区合并成一个有序区,直到全部记录有序。
(7)基数排序:不需要进行记录关键字比较的一种排序方法。
2、程序源码:
工程目录:
图2.1 工程目录图
(1)接口AlgorithmSort.java
package com.remoa.common.strategy;
import java.util.List;
public interface AlgorithmSort {
public void startAlgorithm(List<Integer> list);
}
(2)BubbleSort.javapackage com.remoa.common.strategy;
import java.util.List;
/**
* 冒泡排序法重复地走访要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来,走访数列的工作是重复地进行直到没有再需要交换,则该数列已经排序完成。
* @author Remoa
*
*/
public class BubbleSort implements AlgorithmSort{
@Override
public void startAlgorithm(List<Integer> list) {
bubbleSort(list);
}
/**
* 冒泡排序法
* @param list 待排序序列
* @return
*/
public List<Integer> bubbleSort(List<Integer> list){
int i = 0, j = 0;
for(i = 0; i < list.size(); i++){
for(j = 0; i + j < list.size() - 1; j++){
if(list.get(j).intValue() > list.get(j + 1).intValue()){
Integer temp = list.get(j);
list.set(j, list.get(j + 1));
list.set(j + 1, temp);
}
}
}
return list;
}
}
(3)HeapSort.javapackage com.remoa.common.strategy;
import java.util.List;
/**
* 堆排序利用了堆的特性进行排序。采用大顶堆可以进行升序排序。采用小顶堆可以进行降序排序。升序的堆排序,首先将待排序列建成一个大顶堆,使得堆顶结点最大。将堆顶结点与堆尾结点交换位置,堆长度减1,调整剩余结点为堆,得到次大值结点,重复这一过程,即可得到一个升序序列。
* 堆:特殊的完全二叉树,其所有非叶子结点均不大于(或不小于)其左右孩子结点。
* 小顶堆(小根堆):堆中所有非叶子结点均不大于其左右孩子结点。
* 大顶堆(大根堆):对中所有非叶子结点均不小于其左右孩子结点。
* 根结点的位置称为堆顶,最后结点的位置称为堆尾,结点个数称为堆长度。
* 完全二叉树:设二叉树的深度为h,除第h层以外,其它各层的结点数都达到最大个数,第h层所有结点都集中在最左边,这就是完全二叉树。
* @author Remoa
*
*/
public class HeapSort implements AlgorithmSort {
@Override
public void startAlgorithm(List<Integer> list) {
HeapSortVO heapSortVO = new HeapSortVO();
heapSortVO.setLength(list.size());
heapSortVO.setList(list);
heapSort(heapSortVO);
}
/**
* 堆的筛选,对对中指定的pos结点为根的子树进行堆的特性的维护,其前提是pos结点的左右子树均满足堆特性
* @param heapSortVO
* @param pos 进行筛选的节点位置
* @return HeapSortVO实体
*/
public HeapSortVO shiftDown(HeapSortVO heapSortVO, int pos){
int length = heapSortVO.getLength();
//到叶子结点则循环结束
while(pos + 1 <= length / 2){
//左右子结点都存在
if((pos + 1) * 2 <= length && (pos + 1) * 2 + 1 <= length){
int maxElementLoca = heapSortVO.getList().get((pos + 1) * 2 - 1).intValue() >= heapSortVO.getList().get((pos + 1) * 2).intValue() ? (pos + 1) * 2 - 1 : (pos + 1) * 2;
if(heapSortVO.getList().get(pos) <= heapSortVO.getList().get(maxElementLoca)){
Integer temp = heapSortVO.getList().get(pos);
heapSortVO.getList().set(pos, heapSortVO.getList().get(maxElementLoca));
heapSortVO.getList().set(maxElementLoca, temp);
pos = maxElementLoca;
}else{
return heapSortVO;
}
}
//只有左子结点存在
else{
if(heapSortVO.getList().get(pos).intValue() <= heapSortVO.getList().get((pos + 1) * 2 - 1).intValue()){
Integer temp = heapSortVO.getList().get(pos);
heapSortVO.getList().set(pos, heapSortVO.getList().get((pos + 1) * 2 - 1).intValue());
heapSortVO.getList().set((pos + 1) * 2 - 1, temp);
pos = (pos + 1) * 2 - 1;
}else{
return heapSortVO;
}
}
}
return heapSortVO;
}
/**
* 将堆顶结点转移到堆尾
* @param heapSortVO
* @return HeapSortVO实体
*/
public HeapSortVO removeFirstHeap(HeapSortVO heapSortVO){
Integer firstElement = heapSortVO.getList().get(0);
Integer temp = heapSortVO.getList().get(heapSortVO.getLength() - 1);
heapSortVO.getList().set(0, temp);
heapSortVO.getList().set(heapSortVO.getLength() - 1, firstElement);
heapSortVO.setLength(heapSortVO.getLength() - 1);
//重新筛选堆
if(heapSortVO.getLength() > 1){
shiftDown(heapSortVO, 0);
}
return heapSortVO;
}
/**
* 创建堆
* @param heapSortVO
* @return HeapSortVO实体
*/
public HeapSortVO createHeap(HeapSortVO heapSortVO){
int length = heapSortVO.getLength();
for(int i = (length - 1) / 2; i >= 0; i--){
heapSortVO = shiftDown(heapSortVO, i);
}
return heapSortVO;
}
/**
* 堆排序算法
* @param heapSortVO
* @return HeapSortVO实体
*/
public HeapSortVO heapSort(HeapSortVO heapSortVO){
heapSortVO = createHeap(heapSortVO);
while(heapSortVO.getLength() > 1){
heapSortVO = removeFirstHeap(heapSortVO);
}
return heapSortVO;
}
}
/**
* HeapSortVO实体,封装一个堆其元素序列,及无序元素长度
* @author Remoa
*
*/
class HeapSortVO{
private List<Integer> list;//待排序的序列
private int length;//还未排序序列长度
public List<Integer> getList() {
return list;
}
public void setList(List<Integer> list) {
this.list = list;
}
public int getLength() {
return length;
}
public void setLength(int length) {
this.length = length;
}
}
(4)MergingSort.java
package com.remoa.common.strategy;
import java.util.ArrayList;
import java.util.List;
/**
* 归并是指将两个或两个以上的有序表组合成一个新的有序表。
* 归并排序是指把序的待排序序列分解成若干个有序子序列,并把有序子序列合并为整体有序序列的过程。长度为1的序列是有序的。因此当分解得到的子序列长度大于1时,应继续分解,直到长度为1。采用两两分解和归并的策略简单易行,这样的归并排序称为2-路归并排序。
* @author Remoa
*
*/
public class MergingSort implements AlgorithmSort {
@Override
public void startAlgorithm(List<Integer> list) {
MergingSortVO mergingSortVO = new MergingSortVO();
mergingSortVO.setList1(list);
List<Integer> list2 = new ArrayList<Integer>();
for(int i = 0; i < mergingSortVO.getList1().size(); i++){
list2.add(i);
}
mergingSortVO.setList2(list2);
mergeSort(mergingSortVO, 0, list.size() - 1);
}
/**
* 将两个有序序列合并
* @param mergingSortVO MergingSortVO实体
* @param m 起始位置
* @param n 最后位置
* @return mergingSortVO实体,其中从i到n的位置部分已有序
*/
public MergingSortVO merge(MergingSortVO mergingSortVO, int i, int m, int n){
int j, k;
int tmp = i;
for(j = m + 1, k = i; j <= n && i <= m; k++){
if(mergingSortVO.getList1().get(i).intValue() <= mergingSortVO.getList1().get(j).intValue()){
mergingSortVO.getList2().set(k, mergingSortVO.getList1().get(i));
i++;
}else{
mergingSortVO.getList2().set(k, mergingSortVO.getList1().get(j));
j++;
}
}
while(i <= m){
mergingSortVO.getList2().set(k, mergingSortVO.getList1().get(i));
k++;
i++;
}
while(j <= n){
mergingSortVO.getList2().set(k, mergingSortVO.getList1().get(j));
k++;
j++;
}
while(tmp <= n){
mergingSortVO.getList1().set(tmp, mergingSortVO.getList2().get(tmp));
tmp++;
}
return mergingSortVO;
}
/**
* 归并排序
* @param mergingSortVO MergingSortVO实体
* @param s 起始位置
* @param t list的长度 - 1
* @return 已排序序列
*/
public MergingSortVO mergeSort(MergingSortVO mergingSortVO, int s, int t){
if(s < t){
int m = (s + t) / 2;//中间位置
mergingSortVO = mergeSort(mergingSortVO, s, m);
mergingSortVO = mergeSort(mergingSortVO, m + 1, t);
mergingSortVO = merge(mergingSortVO, s, m, t);
}
return mergingSortVO;
}
}
/**
* 归并排序实体类,封装了一个待排序序列list1,和起到归并排序时存储排序后序列值与list1相同的的list2
* @author Remoa
*
*/
class MergingSortVO{
private List<Integer> list1;
private List<Integer> list2;
public List<Integer> getList1() {
return list1;
}
public void setList1(List<Integer> list1) {
this.list1 = list1;
}
public List<Integer> getList2() {
return list2;
}
public void setList2(List<Integer> list2) {
this.list2 = list2;
}
}
(5)QuickSort.java
package com.remoa.common.strategy;
import java.util.List;
/**
* 快速排序法基本思想:首先从待排序列中选定一个枢纽关键字,通过关键字与枢纽的比较将待排序的序列划分成位于枢纽前后的两个子序列,其中枢纽之前的子序列的所有关键字都不大于枢纽,枢纽之后的子序列的所有关键字都不小于枢纽。此时枢纽已到位,再按同样方法对这两个子序列分别递归进行快速排序,最终使得整个序列有序。
* @author Remoa
*
*/
public class QuickSort implements AlgorithmSort {
@Override
public void startAlgorithm(List<Integer> list) {
QuickSortVO quickSortVO = new QuickSortVO();
quickSortVO.setList(list);
quickSortVO.setPivotloca(0);
quicksort(quickSortVO, 0, list.size() - 1);
}
/**
* 对序列进行划分
* @param quickSortVO quickSortVO实体
* @param low 位标low指向待排记录的第一个记录
* @param high 位标high指向最后一个记录
* @return QuickSortVO实体
*/
public QuickSortVO partition(QuickSortVO quickSortVO, int low, int high){
Integer pivlot = quickSortVO.getList().get(low);
while(low < high){
while(low < high && quickSortVO.getList().get(high).intValue() >= pivlot.intValue()){
high--;
}
quickSortVO.getList().set(low, quickSortVO.getList().get(high).intValue());
while(low < high && quickSortVO.getList().get(low).intValue() <= pivlot.intValue()){
low++;
}
quickSortVO.getList().set(high, quickSortVO.getList().get(low).intValue());
}
quickSortVO.getList().set(low, pivlot.intValue());
quickSortVO.setPivotloca(low);
return quickSortVO;
}
/**
* 快速排序算法
* @param quickSortVO quickSortVO实体
* @param s 位标s指向待排记录的第一个记录
* @param t 位标t指向最后一个记录
* @return QuickSortVO实体
*/
public QuickSortVO quicksort(QuickSortVO quickSortVO, int s, int t){
if(s < t){
quickSortVO = partition(quickSortVO, s, t);
int temp = quickSortVO.getPivotloca();
quickSortVO = quicksort(quickSortVO, s, temp - 1);
quickSortVO = quicksort(quickSortVO, temp + 1, t);
}
return quickSortVO;
}
}
/**
* QuickSortVO实体,封装了待排序列和枢纽位置
* @author Remoa
*
*/
class QuickSortVO{
private List<Integer> list;
private int pivotloca;//枢纽位置
public List<Integer> getList() {
return list;
}
public void setList(List<Integer> list) {
this.list = list;
}
public int getPivotloca() {
return pivotloca;
}
public void setPivotloca(int pivotloca) {
this.pivotloca = pivotloca;
}
}
(6)RadixSort.java
package com.remoa.common.strategy;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* 基数排序:先将所有关键字统一为相同的位数,位数较少的数前面补0.然后从最低位开始依次进行排序,直到按最高位排序完成,关键字序列就成为有序序列。
* 一般情况下,将记录的关键字看成由m个关键字复合而成,每个关键字可能取r个值,则只要从最低位关键字起,先按关键字的不同值将记录“分配”到r个子序列,再按从小到达将各子序列依次首尾相接“收集”在一起,如此重复m趟,最终完成整个记录序列的排序。
* 基数r指关键字的取值范围。
* @author Remoa
*
*/
public class RadixSort implements AlgorithmSort{
public static final int keyLength = 5;
@Override
public void startAlgorithm(List<Integer> list) {
List<String> strList = changeToStr(list);
RadixSortVO radixSortVO = new RadixSortVO();
radixSortVO.setList(strList);
List<String> listCopy = new ArrayList<String>();
for(Iterator<String> iter = radixSortVO.getList().iterator(); iter.hasNext(); ){
listCopy.add(iter.next());
}
radixSortVO.setListCopy(listCopy);
radixSort(radixSortVO);
}
/**
* 首先给整数位数不够者在前头进行补零处理
* @param list 待排序序列
* @return
*/
public List<String> changeToStr(List<Integer> list){
List<String> strList = new ArrayList<String>();
for(Iterator<Integer> iter = list.iterator(); iter.hasNext(); ){
Integer number = iter.next();
String str = String.valueOf(number.intValue());
int length = str.length();
for(int i = 0; i < keyLength - length; i++){
str = "0" + str;
}
strList.add(str);
}
return strList;
}
/**
* 一趟基数排序
* @param radixSortVO radixSortVO实体
* @param loc 关键字的第loc位位置
* @return RadixSortVO实体
*/
public RadixSortVO radixPass(RadixSortVO radixSortVO, int loc){
int j, k;
int[] count = new int[10];
int[] pos = new int[10];
for(int i = 0; i < count.length; i++){
count[i] = 0;
}
//分配
for(k = 0; k < radixSortVO.getList().size(); k++){
int temp = Integer.valueOf(String.valueOf(radixSortVO.getList().get(k).charAt(loc)));
//对各种取值计数
count[temp]++;
}
//起始位置
pos[0] = 0;
for(j = 1; j < 10; j++){
pos[j] = count[j - 1] + pos[j - 1];
}
//收集
for(k = 0; k < radixSortVO.getList().size(); k++){
j = Integer.valueOf(String.valueOf(radixSortVO.getList().get(k).charAt(loc)));
String str = radixSortVO.getList().get(k);
radixSortVO.getListCopy().set(pos[j], str);
pos[j]++;
}
for(k = 0; k < radixSortVO.getList().size(); k++){
radixSortVO.getList().set(k, radixSortVO.getListCopy().get(k));
}
return radixSortVO;
}
/**
* 计数基数排序
* @param radixSortVO radixSortVO实体
* @return RadixSortVO实体
*/
public RadixSortVO radixSort(RadixSortVO radixSortVO){
int i = keyLength - 1;
while(i >= 0){
radixSortVO = radixPass(radixSortVO, i--);
}
return radixSortVO;
}
}
/**
* 计数基数排序实体,封装待排序列,和对待排序列的复制
* @author Remoa
*
*/
class RadixSortVO{
private List<String> list;
private List<String> listCopy;
public List<String> getList() {
return list;
}
public void setList(List<String> list) {
this.list = list;
}
public List<String> getListCopy() {
return listCopy;
}
public void setListCopy(List<String> listCopy) {
this.listCopy = listCopy;
}
}
(7)SelectionSort.java
package com.remoa.common.strategy;
import java.util.List;
/**
* 选择排序:在要排序的一组数中,选出最小(或者最大)的一个数与第1个位置的数交换;然后在剩下的数当中再找最小(或者最大)的与第2个位置的数交换,依次类推,直到第n-1个元素(倒数第二个数)和第n个元素(最后一个数)比较为止。
* @author Remoa
*
*/
public class SelectionSort implements AlgorithmSort{
@Override
public void startAlgorithm(List<Integer> list) {
selectionSort(list);
}
/**
* 选择排序算法
* @param list 待排序序列
* @return 待排序序列
*/
public List<Integer> selectionSort(List<Integer> list){
for(int i = 0; i < list.size(); i++){
Integer min = list.get(i);
int loc = i;
for(int j = i + 1; j < list.size(); j++){
if(list.get(j) < min){
min = list.get(j);
loc = j;
}
}
Integer tmp = list.get(i);
list.set(i, min);
list.set(loc, tmp);
}
return list;
}
}
(8)ShellSort.java
package com.remoa.common.strategy;
import java.util.List;
/**
* 希尔排序是将整个待排序列按增量d划分成d个子序列,分别对各子序列进行直接插入排序,不断减小增量的,重复这一过程,直到d减少到1,对整个序列进行一次直接插入排序。
* 基于增量序列的降序特点,希尔排序也被称为“缩小增量排序”。
* @author Remoa
*
*/
public class ShellSort implements AlgorithmSort {
@Override
public void startAlgorithm(List<Integer> list) {
int d[]={13,11,7,5,3,1};//设置增量序列为9,7,5,3,1
shellSort(list, d);
}
/**
* 一趟希尔排序
* @param list 待排序序列
* @param dk 增量
* @return 该待排序序列
*/
public List<Integer> shellInsert(List<Integer> list, int dk){
int i, j;
for(i = 0; i < list.size() - dk; i++){
if(list.get(i + dk) < list.get(i)){
Integer tmp = list.get(i + dk);
j = i + dk;
do{
j = j - dk;
Integer number = list.get(j);
list.set(j + dk, number);
}while(j - dk >= 0 && tmp < list.get(j -dk).intValue());
list.set(j, tmp.intValue());
}
}
return list;
}
/**
* 希尔排序
* @param list 待排序序列
* @param d 增量数组
* @return 该待排序序列
*/
public List<Integer> shellSort(List<Integer> list, int d[]){
for(int i = 0; i < d.length; i++){
list = shellInsert(list, d[i]);
}
return list;
}
}
(9)StraightInsertionSort.java
package com.remoa.common.strategy;
import java.util.List;
/**
* 直接插入排序思想:每次将无序区的第一个记录按关键字插入到有序区的合适位置,并将有序区长度加1
* (1)查找插入位置
* (2)移动记录空出插入位置
* @author Remoa
*
*/
public class StraightInsertionSort implements AlgorithmSort {
@Override
public void startAlgorithm(List<Integer> list) {
straightInsertionSort(list);
}
/**
* 直接插入排序算法
* @param list 待排序列
* @return
*/
public List<Integer> straightInsertionSort(List<Integer> list){
int j;
Integer temp = null;
for(int i = 0; i < list.size() - 1; i++){
if(list.get(i).intValue() > list.get(i + 1).intValue()){
temp = list.get(i + 1);
j = i + 1;
do{
j--;
Integer number = list.get(j).intValue();
list.set(j + 1, number);
}while(j >= 1 && temp.intValue() < list.get(j - 1).intValue());
list.set(j, temp.intValue());
}
}
return list;
}
}
(10)Menu.javapackage com.remoa.common;
import java.util.List;
import java.util.Scanner;
import com.remoa.common.factory.ChooserFactory;
public class Menu {
public static void getMenu(List<Integer> list){
System.out.println("---------------- Menu -------------------");
System.out.println("|\t0.exit\t\t\t\t|");
System.out.println("|\t1.Bubble Sort\t\t\t|");
System.out.println("|\t2.Heap Sort\t\t\t|");
System.out.println("|\t3.Merging Sort\t\t\t|");
System.out.println("|\t4.Quick Sort\t\t\t|");
System.out.println("|\t5.Radix Sort\t\t\t|");
System.out.println("|\t6.Selection Sort\t\t|");
System.out.println("|\t7.Shell Sort\t\t\t|");
System.out.println("|\t8.StraightInsertion Sort\t|");
System.out.println("-----------------------------------------");
System.out.print("Please input your choice:");
Scanner scan = new Scanner(System.in);
String chooseStr = scan.nextLine();
ChooserFactory.runAlgorithm(chooseStr, list);
}
}
(11)RandomNumber.java
package com.remoa.common;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class RandomNumber {
public static final int RANGE = 100000;//测试样例的范围
public static final int COUNT = 100000;//测试数字的数量
public static void createRandomNumber(){
try{
Random random = new Random();
File file = new File("C:\\Users\\邓小艺\\Desktop\\random.txt");
OutputStream output = new FileOutputStream(file);
int randomNumber = 0;
String str = null;
for(int i = 0; i < COUNT; i++){
randomNumber = random.nextInt(RANGE);
str = String.valueOf(randomNumber) + " ";
output.write(str.getBytes());
}
output.close();
}catch(IOException e){
System.out.println("文件写入异常");
e.printStackTrace();
}
}
public static List<Integer> getRandomNumber(){
List<Integer> randomNumberList = null;
try {
randomNumberList = new ArrayList<Integer>();
InputStream input = new FileInputStream("C:\\Users\\邓小艺\\Desktop\\random.txt");
int size = input.available();
byte[] b = new byte[size];
input.read(b);
input.close();
String randomNumberStr = new String(b);
String[] strArray = randomNumberStr.split("\\s+");
for(int i = 0; i < strArray.length; i++){
randomNumberList.add(Integer.valueOf(strArray[i]));
}
} catch (IOException e) {
System.out.println("文件读出异常");
e.printStackTrace();
}
return randomNumberList;
}
}
(12)MainAction.java
package com.remoa.action;
import java.util.List;
import com.remoa.common.Menu;
import com.remoa.common.RandomNumber;
public class MainAction {
public static void main(String[] args) {
RandomNumber.createRandomNumber();
List<Integer> list = RandomNumber.getRandomNumber();
Menu.getMenu(list);
}
}
图3.1 菜单截图
图3.2 冒泡排序法运行结果
图3.3 堆排序法运行结果
图3.4 归并排序法运行结果
图3.5 快速排序法运行结果
图3.6 计数基数排序法运行结果
图3.7 选择排序法运行结果
图3.8 希尔排序运行结果
图3.9 直接插入排序运行结果