内部排序,指的是待排序记录存放在计算机随机存储器中进行的排序过程。外部排序,指的是待排序记录的数量很大,以致内存一次不能够容纳全部记录,在排序过程中尚需对外存进行访问的排序过程(摘自严蔚敏版数据结构)。
在待排序的文件中,经过某种排序算法后,相同关键字记录之间的相对次序保持不变,可以称该排序方法是稳定的,反之,就是不稳定的。
内部排序算法的时间复杂度和空间复杂度对比表
类别 |
排序方法 |
时间复杂度 |
空间复杂度 |
稳定性 | ||
最好情况 |
最坏情况 |
平均情况 |
辅助空间 | |||
插入 |
直接插入 |
O(n) |
O(n2) |
O(n2) |
O(1) |
稳定 |
排序 |
希尔排序 |
时间复杂度与增量序列的选取有关 |
O(1) |
不稳定 | ||
选择 |
直接选择 |
O(n2) |
O(n2) |
O(n2) |
O(1) |
不稳定 |
排序 |
堆排序 |
O(nlogn) |
O(nlogn) |
O(nlogn) |
O(1) |
不稳定 |
交换 |
冒泡排序 |
O(n) |
O(n2) |
O(n2) |
O(1) |
稳定 |
排序 |
快速排序 |
O(nlogn) |
O(n2) |
O(nlogn) |
O(nlogn) |
不稳定 |
归并排序 |
O(nlogn) |
O(nlogn) |
O(nlogn) |
O(n) |
稳定 | |
基数排序 |
O(d(n+rd)) |
O(d(n+rd)) |
O(d(n+rd)) |
O(rd) |
稳定 |
注:在上表中基数排序的复杂度中,r代表关键字的基数,d代表长度,n代表关键字的个数。基数排序的时间复杂度也可以写成O(dn),基数排序最适用于n值很大而关键字较小的序列。
下面是常会考到的一些算法。
1、直接插入排序算法
直接插入排序算法的基本原则是,将一组无序的数字排成一排,左端第一个数字为已经完成排序的数字,其他数字为未排序的数字。然后从左到右依次将未排序的数字插入到已排序的数字中。
直接插入排序的具体描述如下:
(1)从第一个元素开始,该元素可以认为已经被排序;
(2)取出下一个元素,在已经排序的元素序列中从后向前扫描;
(3)如果该元素(已排序)大于新元素,将该元素移到下一个位置;
(4)重复步骤(3),直到找到已排序的元素小于或者等于新元素的位置;
(5)将新元素插入到该位置中;
(6)重复步骤(2)。
public class InsertSort {
public static void main(String[] args) {
int[] arr = { 49, 38, 65, 97, 76, 13, 27 };
System.out.print("初始序列:");
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
System.out.println();
System.out.println("直接插入排序过程如下:");
solution(arr);
}
public static void solution(int[] arr) {
int temp, j; // temp里面存放的是本趟要进行插入的元素值
for (int i = 1; i < arr.length; i++) {
temp = arr[i];
j = i - 1; // 下标在0到j之间的元素, 可以看做是已经排好序的。
while ((j >= 0) && (arr[j] > temp)) {
arr[j + 1] = arr[j];
j--;
}
arr[j + 1] = temp;
System.out.print("第" + i + "趟排序:");
for (int l = 0; l < arr.length; l++) {
System.out.print(arr[l] + " ");
}
System.out.println();
}
}
}
运行结果
2、直接选择排序算法
直接选择排序算法的思想,数据序列共有n个元素,每一趟(假设是第m趟)处理都是从n-m+1个数据中选择一个最小的数作为有序序列第m个位置的数据。
public class SelectSort {
public static void main(String[] args) {
int[] arr = { 49, 38, 65, 97, 76, 13, 27 };
System.out.print("初始序列:");
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
System.out.println();
System.out.println("直接选择排序过程如下:");
solution(arr);
}
public static void solution(int[] arr) {
int j, min, temp;
for (int i = 0; i < arr.length - 1; i++) {
min = i;
for (j = i + 1; j < arr.length; j++) {
if (arr[min] > arr[j]) {
min = j;
}
}
if (min != i) {
temp = arr[i];
arr[i] = arr[min];
arr[min] = temp;
}
System.out.print("第" + (i + 1) + "趟排序:");
for (int l = 0; l < arr.length; l++) {
System.out.print(arr[l] + " ");
}
System.out.println();
}
}
}
运行结果
3、冒泡排序法
冒泡排序的基本处理思想是通过对相邻两个数据的比较及其交换来达到排序的目的。在冒泡排序的过程中,较小的数据好比水中的气泡逐渐向上漂浮,而较大的数据好比石头在水中下沉,每一趟都有一块“最大的石头”沉到水底。
public class BubbleSortApp {
public static void main(String[] args) {
int[] arr = { 49, 38, 65, 97, 76, 13, 27 };
System.out.print("初始序列:");
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
System.out.println();
System.out.println("冒泡排序过程如下:");
solution(arr);
}
public static void solution(int arr []){
int temp;
for(int i=arr.length-1;i>0;i--){
for(int j=0;j<i;j++){
if(arr[j]>arr[j+1]){
temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
System.out.print("第"+(arr.length-i)+"趟排序:");
for(int l=0;l<arr.length;l++){
System.out.print(arr[l]+" ");
}
System.out.println();
}
}
}
运行结果
4、快速排序算法
快速排序是对冒泡排序算法的一种改进。它的基本思想是,选择一个基准元素(通常选择第一个元素或是最后一个元素)通过一趟排序将待排序记录分割成独立的两个部分,一部分记录的关键字比基准元素小,一部分记录的关键字大于等于基准元素,然后再分别对这两个部分记录继续用相同的算法进行排序,以达到整个序列有序。
public class QuickSort {
public static void main(String[] args) {
int[] arr = { 49, 38, 65, 97, 76, 13, 27 };
System.out.print("快速排序初始序列:");
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
System.out.println();
int[] firstArr = solutionFirst(arr, 0, arr.length - 1);
System.out.print("第一个元素为基准元素排序结果:");
for (int m = 0; m < firstArr.length; m++) {
System.out.print(arr[m] + " ");
}
int[] lastArr = solutionLast(arr, 0, arr.length - 1);
System.out.print("\n最后一个元素为基准元素排序结果:");
for (int m = 0; m < lastArr.length; m++) {
System.out.print(arr[m] + " ");
}
}
public static int[] solutionFirst(int arr[], int low, int high) {
if (low < high) {
int key = arr[low];
int l = low, h = high;
while (l < h) {
// 基准元素选择第一个时,首先开始的比较是基准元素与最后一个元素的比较。
while (arr[h] >= key) {
h--;
}
if (l < h) {
int temp = arr[l];
arr[l] = arr[h];
arr[h] = temp;
l++;
}
while (arr[l] < key) {
l++;
}
if (l < h) {
int temp = arr[l];
arr[l] = arr[h];
arr[h] = temp;
h--;
}
}
solutionFirst(arr, low, l - 1);
solutionFirst(arr, l + 1, high);
}
return arr;
}
public static int[] solutionLast(int arr[], int low, int high) {
if (low < high) {
int key = arr[high];
int l = low, h = high;
while (l < h) {
// 基准元素选择最后一个时,首先开始的比较是基准元素和第一个元素的比较。
while (arr[l] < key) {
l++;
}
if (l < h) {
int temp = arr[l];
arr[l] = arr[h];
arr[h] = temp;
h--;
}
while (arr[h] >= key) {
h--;
}
if (l < h) {
int temp = arr[l];
arr[l] = arr[h];
arr[h] = temp;
l++;
}
}
solutionLast(arr, low, l - 1);
solutionLast(arr, l + 1, high);
}
return arr;
}
}
运行结果