简单排序
对数据排序可能是检索的一个初始步骤,例如:二分查找法比线性查找法要快的多,然而它只能应用于有序的数据。
先介绍三种简单排序:冒泡,选择,插入。算法比较简单,执行速度也就慢一些,在某些情况下比那些复杂的算法实际上还好一些。比如小规模的文件以及基本有序的文件,插入排序算法能比快速排序算法更为有效,实际上,插入排序通常也作为快速排序算法实现的一部分。
简单排序算法大致上都包含如下操作:
- 1.比较两个数据项
- 2.交换两个数据项,或复制其中一项
冒泡排序
算法描述(以数组为例,从小到大排序)
- 1.从数组最左侧开始对元素依次进行两两比较
- 2.如果比较的两元素左边的较大,那就和右边的元素进行交换。
- 3.向右移动一个位置,按照第2步操作,依次执行第3步,一直比较到数组的最右端。(结果:比较完之后,数组中最大的数据项一定在最右端)
- 4.循环步骤1,2,3。当碰到最右边有序的元素后,(每一次执行,都能将一个元素排成有序),就从数组左侧重新开始
案例
package test29;
/**
* 冒泡排序
*/
public class ArrayBub {
private int[] a;
//记录数组的实际长度
private int nElems;
public ArrayBub(int max) {
a = new int[max];
nElems = 0;
}
/**
* 插入操作
*
* @param value
*/
public void insert(int value) {
a[nElems] = value;
nElems++;
}
/**
* 展示
*/
public void display() {
for (int i = 0; i < nElems; i++) {
System.out.print(a[i] + " ");
}
System.out.println();
}
public void bubbleSort() {
int out, in;
int temp;
for (out = nElems - 1; out > 0; out--) {
for (in = 0; in < out; in++) {
if (a[in] > a[in + 1]) {
temp = a[in];
a[in] = a[in + 1];
a[in + 1] = temp;
}
}
}
}
}
package test29;
/**
* 冒泡排序
*/
public class BubbleSortApp {
public static void main(String[] args) {
int maxSize = 100;
ArrayBub arr = new ArrayBub(maxSize);
arr.insert(99);
arr.insert(88);
arr.insert(77);
arr.insert(66);
arr.insert(55);
arr.insert(44);
arr.insert(33);
arr.insert(22);
arr.insert(11);
arr.insert(00);
arr.display();
arr.bubbleSort();
arr.display();
}
}
特点
out右边的所有数据项是有序的
效率
一般来说,数组中有N个数据项,则第一遍排序中有N-1次比较,第二遍有N-2次,如此类推
(N-1)+(N-2)+(N-3)+…+1=N*(N-1)/2
这样算法做了约N²/2次比较。
当比较的两个元素,左边大于右边时,需要交换,否则不交换。
如果数据是随机的,大约有一半的元素可能需要交换,则交换的次数为N²/4
交换和比较的次数都和N²成正比,使用大O表示法可知冒泡排序法的时间复杂度为O(N²)
选择排序
对冒泡排序法进行改进,将交换次数从O(N²)减少到O(N),而比较次数仍然是O(N²)
算法描述(以数组为例,从小到大排序)
- 从数组最左边开始,以第一个元素为基准(设为最小值),把数组中所有的元素扫描一遍和最小值比较,一轮比较结束之后,找到了数组中最小的元素。
- 然后最小元素和基准位置的数据进行交换
- 从左侧第二个元素开始,继续此操作。。。
案例
package test31;
/**
* 选择排序
*/
public class ArraySelect {
private int[] a;
//记录数组的实际长度
private int nElems;
public ArraySelect(int max) {
a = new int[max];
nElems = 0;
}
/**
* 插入操作
*
* @param value
*/
public void insert(int value) {
a[nElems] = value;
nElems++;
}
/**
* 展示
*/
public void display() {
for (int i = 0; i < nElems; i++) {
System.out.print(a[i] + " ");
}
System.out.println();
}
public void selectionSort() {
int out, in, min;
int temp;
for (out = 0; out < nElems-1; out++) {
//每次以out索引记为最小
min = out;
for (in = out+1; in < nElems; in++) {
//每次找到最小的
if (a[in] < a[min]) {
min = in;
}
}
//里层循环完毕,将最小值的索引位置上的数据和out索引位置的数据进行交换
//因此交换的次数成了O(N),这就是比冒泡排序优越的地方
temp = a[out];
a[out] = a[min];
a[min] = temp;
}
}
}
package test31;
/**
* 选择排序
*/
public class SelectionSortApp {
public static void main(String[] args) {
int maxSize = 100;
ArraySelect arr = new ArraySelect(maxSize);
arr.insert(99);
arr.insert(88);
arr.insert(77);
arr.insert(66);
arr.insert(55);
arr.insert(44);
arr.insert(33);
arr.insert(22);
arr.insert(11);
arr.insert(00);
arr.display();
arr.selectionSort();
arr.display();
}
}
特点
左侧的数据总是有序的,即下标小于或等于out的位置的数据项总是有序的
效率
选择排序和冒泡排序执行了相同次数的比较:N*(N-1)/2.但选择排序更快,因为它进行的交换次数少的多。
使用大O表示法可知选择排序法的时间复杂度为O(N²)
插入排序
算法描述(以数组为例,从小到大排序)
- 标记变量,一般标记数组中的第二个数据,标记变量左边的数据有序,右边的无序。
- 将标记变量存储到一个临时变量中,并将标记变量与其左侧的数据进行比较,插入到适当的位置。
- 每一轮过后,标记变量向右移动一个位置
案例
package test32;
/**
* 插入排序
*/
public class ArrayInsert {
private int[] a;
//记录数组的实际长度
private int nElems;
public ArrayInsert(int max) {
a = new int[max];
nElems = 0;
}
/**
* 插入操作
*
* @param value
*/
public void insert(int value) {
a[nElems] = value;
nElems++;
}
/**
* 展示
*/
public void display() {
for (int i = 0; i < nElems; i++) {
System.out.print(a[i] + " ");
}
System.out.println();
}
public void insertionSort() {
int out, in;
int temp;
//out变量从1开始,向右移动,他标记了未排序部分的最左端的数据
for (out = 1; out < nElems; out++) {
temp = a[out];
in = out;
//in变量从out变量开始,向左移动,直到temp变量小于in所指的数组数据项
//或者它已经不能向左移动为止。
//while循环的每一趟都向右移动了一个已排序的数据项
while (in > 0 && a[in - 1] >= temp) {
a[in] = a[in - 1];
--in;
}
a[in] = temp;
}
}
}
package test32;
/**
* 插入排序
*/
public class InsertionSortApp {
public static void main(String[] args) {
int maxSize = 100;
ArrayInsert arr = new ArrayInsert(maxSize);
arr.insert(99);
arr.insert(88);
arr.insert(77);
arr.insert(66);
arr.insert(55);
arr.insert(44);
arr.insert(33);
arr.insert(22);
arr.insert(11);
arr.insert(00);
arr.display();
arr.insertionSort();
arr.display();
}
}
特点
每一趟结束后,在将temp位置的项插入后,比outer变量下标小的数据项都是局部有序的。
效率
在第一趟排序中,它比较1次,第二趟比较2次,以此类推,第N趟,比较N-1次,因此N*(N-1)/2
实际情况下,如果是随机的数据,平均有一半的需要比较,所以N*(N-1)/4。
复制的次数大致等于比较的次数。但是,一次复制与一次交换的时间耗费不同,所以相对于随机数据,这个算法比冒泡排序快一倍,比选择排序略快。
时间复杂度也是O(N²)
对于已经有序或基本有序的数据来说,插入排序要好得多。