1.冒泡排序
//冒泡排序,依次比较相邻的两个元素的大小,如果前面的大于后面的,那么交换两者位置public static void bubbleSort(int[] a) {int temp;for (int i = 1; i < a.length; i++) {for (int j = 0; j < a.length - i; j++) {if (a[j] > a[j + 1]) {temp = a[j];a[j] = a[j + 1];a[j + 1] = temp;}}System.out.print("第" + i + "步排序结果");for (int k = 0; k < a.length; k++) {System.out.print(" " + a[k]);}System.out.print("\n");}}
调用:
int a[] = {118, 101, 105, 127, 112};bubbleSort(a);
输出
第1步排序结果 101 105 118 112 127第2步排序结果 101 105 112 118 127第3步排序结果 101 105 112 118 127第4步排序结果 101 105 112 118 127
2.选择排序
/*** 选择排序,找出最小的,和第一个元素交换顺序,再找出剩下的里面最小的那个和第二个元素交换位置,以此类推*/public static void selectSort(int[] a) {int index, temp;for (int i = 0; i < a.length; i++) {index = i;for (int j = 1 + 1; j < a.length; j++) {if (a[j] < a[index]) {index = j;}}if (index != i) {temp = a[i];a[i] = a[index];a[index] = temp;}System.out.print("第" + i + "步排序结果");for (int k = 0; k < a.length; k++) {System.out.print(" " + a[k]);}System.out.print("\n");}}
调用:
int a[] = {118, 101, 105, 127, 112};selectSort(a);
输出:
第0步排序结果 105 101 118 127 112第1步排序结果 105 101 118 127 112第2步排序结果 105 101 112 127 118第3步排序结果 105 101 127 112 118第4步排序结果 105 101 127 118 112
3.插入排序
//插入排序,先将前两个元素进行排序,然后将第三个和前两个比较插入合适的位置,以此类推public static void insertionSort(int[] a) {int temp, j;for (int i = 1; i < a.length; i++) {temp = a[i];j = i - 1;while (j >= 0 && temp < a[j]) {a[j + 1] = a[j];j--;}a[j + 1] = temp;System.out.print("第" + i + "步排序结果");for (int k = 0; k < a.length; k++) {System.out.print(" " + a[k]);}System.out.print("\n");}}
调用:
int a[] = {118, 101, 105, 127, 112};insertionSort(a);
输出:
第1步排序结果 101 118 105 127 112第2步排序结果 101 105 118 127 112第3步排序结果 101 105 118 127 112第4步排序结果 101 105 112 118 127
4. shell排序算法
/*shell排序算法(缩小增量排序、希尔排序),将有n个元素的数组分为n/2个数字序列,第一个和第n/2+1个元素为一对进行排序,然后再分为n/4个序列,重复上述步骤进行排序*/public static void shellSort(int[] a) {int temp, m = 0;for (int r = a.length / 2; r >= 1; r /= 2) {for (int i = r; i < a.length; i++) {temp = a[i];int j = i - r;while (j >= 0 && temp < a[j]) {a[j + r] = a[j];j -= r;}a[j + r] = temp;}m++;System.out.print("第" + m + "步排序结果");for (int k = 0; k < a.length; k++) {System.out.print(" " + a[k]);}System.out.print("\n");}}
调用:
int a[] = {118, 101, 105, 127, 112};shellSort(a);
输出:
第1步排序结果 105 101 112 127 118第2步排序结果 101 105 112 118 127
5. 快速排序算法
/*** 快速排序算法* 设定一个分界值,通过该分界值将数组分为左右两个部分* 将大于等于分界值的数据集中到数组右边,小于分界值的数据集中到数组左边。* 然后左右的数据可以分别重复上面的步骤进行独立排序*/public static void quickSort(int[] arr, int left, int right) {int lTemp = left, rTemp = right;int temp;int f = arr[(left + right) / 2];//分界值System.out.print("当前排序:");for (int anArr : arr) {System.out.print(" " + anArr);}System.out.print("\n");System.out.println("分界值:" + f+ " left:"+left+ " right:"+right);while (lTemp < rTemp) {while (arr[lTemp] < f) {++lTemp;}while (arr[rTemp] > f) {--rTemp;}if (lTemp <= rTemp) {temp = arr[lTemp];arr[lTemp] = arr[rTemp];arr[rTemp] = temp;--rTemp;++lTemp;}}if (lTemp == rTemp) {lTemp++;}if (left < rTemp) {quickSort(arr, left, lTemp - 1);//递归调用}if (right > lTemp) {quickSort(arr, rTemp + 1, right);//递归调用}}
调用:
int a[] = {118, 101, 105, 127, 112};quickSort(a, 0, a.length - 1);System.out.print("\n");System.out.print("排序结果 " );for (int anA : a) {System.out.print(" " + anA);}
输出:
当前排序: 118 101 105 127 112分界值:105 left:0 right:4当前排序: 105 101 118 127 112分界值:105 left:0 right:1当前排序: 101 105 118 127 112分界值:127 left:2 right:4当前排序: 101 105 118 112 127分界值:118 left:2 right:3排序结果 101 105 112 118 127
6. 堆排序算法
/*堆排序算法将数组构建为一个二叉树(大根堆),然后将每个节点和两个子节点进行比较,父节点必须必两个子节点都大,否则交换顺序。最终根节点便是最大的那个值,拿出这个值,放入数组最末端,将二叉树的终结点放入根节点位置,再重复之前的操作,拿出剩下的值中最大的,放入之前那个值的前面,。再重复这种操作。。最终完成排序,*/public static void heapSort(int[] a, int n) {for (int i = n / 2; i >= 0; i--) {//将a[0.n-1]建成大根堆while (2 * i + 1 < n) {//第i个节点的右子树(关于某个节点的左右子树编号计算公式请查看《java常用算法手册》第66页 第二章 数据结构 2.7 树结构 )int j = 2 * i + 1;if ((j + 1) < n) {if (a[j] < a[j + 1]) {//若左子树小于右子树,则需要比较右子树j++;//序号加1,指向右子树,(左子树序号+1=右子树序号,详情参考《java常用算法手册》)}}if (a[i] < a[j]) {//比较i与j为序号的数据(父节点于子节点的值)int temp = a[i];a[i] = a[j];a[j] = temp;i = j;//堆被破坏,需要重新调整} else {break;}}}//输出构成的堆System.out.print("原数据构成的堆:");for (int h = 0; h < n; h++) {System.out.print(" " + a[h]);}System.out.print("\n");for (int i = n - 1; i > 0; i--) {int temp = a[0];//与第i个记录交换a[0] = a[i];a[i] = temp;int k = 0;while (2 * k + 1 < i) {//第i个节点有右子树int j = 2 * k + 1;if ((j + 1) < i) {if (a[j] < a[j + 1]) {//若左子树小于右子树,则需要比较右子树j++;//序号增加1,指向右子树}}if (a[k] < a[j]) {//比较k与j为序号的数据(父节点于子节点中的那个较大值)temp = a[k];//小于,交换位置a[k] = a[j];a[j] = temp;k = j;} else {//父节点就是三个节点中的最大值,跳出break;}}System.out.print("第" + (n - i) + "步排序结果");for (int h = 0; h < n; h++) {System.out.print(" " + a[h]);}System.out.print("\n");}}
调用:
int a[] = {118, 101, 105, 127, 112};heapSort(a, a.length);
输出:
原数据构成的堆: 127 118 105 101 112第1步排序结果 118 112 105 101 127第2步排序结果 112 101 105 118 127第3步排序结果 105 101 112 118 127第4步排序结果 101 105 112 118 127
7. 合并排序
/*** 合并排序* 将待排序数据序列看做有n个长度为1的有序字表组成,* 将他们两两合并,得到长度为2的坐杆有序字表* 然后再将这些字表两两合并,得到长度为4的有序子表,* 一直重复到最后字表长度为n,完成排序。* 此合并方法以二路合并为例,这种排序算法往往需要申请较大的辅助空间,这个辅助空间和待排序原始序列空间一样多。*/public static void mergeSort(int a[], int n) {int count = 0;//排序步骤int len = 1;//有序序列的长度int f = 0;//变量f做标志int[] p = new int[n];while (len < n) {if (f == 1) {mergeOne(p, a, n, len);//p合并到a} else {mergeOne(a, p, n, len);//a合并到p}len *= 2;//增加有序序列长度f = 1 - f;count++;System.out.print("第" + count + "步排序结果:");for (int anA : a) {System.out.print(" " + anA);}System.out.print("\n");}if (f == 1) {// for (int h = 0; h < n; h++) {//将p中的数据复制回数组a// a[h] = p[h];// }System.arraycopy(p, 0, a, 0, n);//将p中的数据复制回数组a}}/*** 合并一次,合并一次后的结果主要看b,因为是从a合并到b** @param a 待合并的序列a,从a合并到b* @param b 待合并的序列b,从a合并到b* @param n 原数组的长度* @param len 有序序列的长度*/static void mergeOne(int a[], int b[], int n, int len) {int s = 0;while (s + len < n) {int e = s + 2 * len - 1;if (e >= n) {//最后一段可能少于len个节点e = n - 1;}//相邻有序段合并int k = s, i = s, j = s + len;while (i < s + len && j <= e) {//如果两个有序表都未结束时,循环比较if (a[i] <= a[j]) {//将较小的元素复制的b中b[k++] = a[i++];} else {b[k++] = a[j++];}}while (i < s + len) {//未合并的部分复制到数据b中b[k++] = a[i++];}while (j <= e) {b[k++] = a[j++];}s = e + 1;}if (s < n) {for (; s < n; s++) {b[s] = a[s];}}}
调用:
int a[] = {118, 101, 105, 127, 112};mergeSort(a, a.length);
输出:
第1步排序结果: 118 101 105 127 112第2步排序结果: 101 105 118 127 112第3步排序结果: 101 105 118 127 112
8. 排序算法的计算复杂度
每一种排序算法都各有优缺点,我们要根据实际问题的需要选择合适的、高效的算法进行排序。下面是这几种排序算法的计算复杂度。
冒泡排序:平均速度为O(n²),最坏情况下的速度为O(n²);选择排序:平均速度为O(n²),最坏情况下的速度为O(n²);快速排序:平均速度为O(n²),最坏情况下的速度为O(n²);插入排序:平均速度为O(nlogn),最坏情况下的速度为O(n²);堆 排 序:平均速度为O(nlogn),最坏情况下的速度为O(nlogn);合并排序:平均速度为O(nlogn),最坏情况下的速度为O(nlogn);Shell排序:平均速度为O(n^(3/2)),最坏情况下的速度为O(n²);
函数O(n²)说明:
一个算法执行所耗费的时间,从理论上是不能算出来的。因为不同的机器,硬件和软件环境不一样,另外还有一些其他因素的影响,所以只能用一个函数来表示出来O(n²)=C*n²,C代表一个常数举例说明:比如,一个长度为5的数组,进行冒泡排序,最坏的情况就是将已有数组进行逆序。每一个元素需要和其他元素比较一次,如果第一个和第二个比较过了,那么第二个就不用和第一个再进行比较,所以两两比较的次数也就是4+3+2+1 = 10次;如果数组的长度为n,那比较次数就是 (n-1)+(n-2)+......+1 = n(n-1)/2次;n(n-1)/2 = 0.5*n*(n-1);这里0.5就是上面的常数C,因为并不一定都是最坏的情况,还有其他各种因素的影响,这个常数不一定都是0.5,所以可以用C来代替0.5表示一个常数。而当n足够大时,n-1 ≈ n,所以0.5*n*(n-1) ≈ C*n*n,也就是C*n²,用函数表示就是O(n²)
