一. 冒泡排序
思想:大的沉下去,小的升上来。
public class TestDemo1 {
//冒泡排序优化
public static void bubbleBetterSort(int[] array){
int tmp = 0;//交换用于存值
int count = 0;//用于判断数组是否有序
for(int i = 0;i <array.length-1;i++){
for(int j = 0;j < array.length-i-1;j++){
if(array[j] > array[j+1]){
tmp = array[j];
array[j] = array[j+1];
array[j+1] = tmp;
count++;//如果交换过,count自增
}
}
if(count == 0)//当前数组已经有序
break;
}
System.out.println(Arrays.toString(array));
}
这里写的是已经优化好的冒泡排序,如果数组 本身有序,就不需要进行那么多趟的比较。因此用count来记录第一趟里if执行的次数,一次都没有执行,说明数组本身有序。
二. 选择排序
思想:每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完。
public static void selectSort(int[] array){
int min=0;//找最小值的下标
int tmp;//用于交换
int j;
for(int i = 0;i < array.length-1;i++){
min = i;
for(j = i+1;j < array.length;j++){//从i+1位置开始找最小值
if(array[j]<array[min]){
min = j;
}
}
//将最小值放到本趟比较的最前面
tmp = array[i];
array[i] = array[min];
array[min] = tmp;
}
System.out.println(Arrays.toString(array));
}
三. 直接插入排序
思想:假设待排序的数据是数组A[1….n]。初始时,A[1]自成1个有序区,无序区为A[2….n]。在排序的过程中,依次将A[i] (i=2,3,….,n)从后往前插入到前面已排好序的子数组A[1,…,i-1]中的适当位置,当所有的A[i] 插入完毕,数组A中就包含了已排好序的输出序列。
public class TestDemo3 {
public static void insrtSort(int[] array){
int tmp = 0;
int j;
for(int i = 1;i < array.length;i++){//从i号位值开始
tmp = array[i];
for(j = i-1;j >= 0;j--){//找合适的位置进行插入
if(array[j] > tmp){
array[j+1] = array[j];
}
//在循环中找到了比tmp小的第一个元素跳出循环
else{
break;
}
}
//把tmp赋值到最小值的后面
array[j+1] = tmp;
}
System.out.println(Arrays.toString(array));
}
四. 希尔排序(shell排序)
思想:是直接插入排序的一种更高效的版本,又称“缩小增量排序”。希尔排序是把记录按下标的一定增量分组,组内采用直接插入排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个数组恰被分成一组,算法便终止。
增量序列如何选取?
①互为质数 ②最后一个增量为一
一般情况下选取[5,3,1]作为增量序列
接下来以图来进行希尔排序
原始序列:
①将序列分为5组
如图,相同颜色的线为同一组。接下来组内进行直接插入排序。
排序后结果:
②:第一次排序后的序列分为三组
排序后:
③最后对这个序列整个进行直接插入排序
排序后结果:
接下来实现shell排序
public static void shell(int[] array,int gap){//gap表示分成几组
int tmp = 0;//存放要交换的数据元素
for(int i = gap;i < array.length;i++){//第一次要排序的元素是gap,不能写成i=i+gap
tmp = array[i];
int j = 0;
for(j = i-gap;j >= 0;j -= gap){
if(array[j] > tmp){
array[j+gap] = array[j];
}else{
break;
}
}
array[j+gap] = tmp;
}
}
public static void shellSort(int[] array){
int[] d = {5,3,1};//增量序列
for(int i = 0;i < d.length;i++){
shell(array,d[i]);//每一次分的组进行直接插入排序
}
}
五. 快速排序
思想:①在数组中找一个数作为基准(我们以零号数组下标作为第一个基准)
②比基准小的放在它的左边,比基准大的放在它的右边
③对左右区域分别重复第二步操作,直至左右区域都只剩下一个元素。
1.快速排序的递归实现
原始序列:
①:从high开始往前找,找到第一个比基准21小的,将high指向的4放在low指向的地方。执行后如图:
②:从low往后找,找到第一个比基准21大的,将low指向的32放在high指向的位置。执行后如图:
③:high继续往前找比基准小的,执行如图:
因为high和low相遇,此时将low(或high)位置的数用基准替代。执行后如图
现在发现基准21左边都比它小,右边都比它大。这样第一趟快速排序就结束了。
之后再分别对左右两边用与上述相同的方法排序,就会得到一个有序的序列了。
代码实现:
public static int partition(int[] array,int start,int end){//返回基准的下标
int low = start;
int high = end;
int tmp = array[low];
while(low < high){
while(low < high&&array[high] >= tmp){//从后往前找比基准小的后退出循环
--high;
}
if(low >= high){//判断low与high是否相遇
break;
}else{
array[low] = array[high];
}
while(low < high&&array[low] <= tmp){//从前往后找比基准大的退出循环
++low;
}
if(low >= high){
break;
}else{
array[high] = array[low];
}
}
array[low] = tmp;
return low;
}
public static void quick(int[] array,int start,int end){
int par = partition(array, start, end);
if(par > start+1){//左半部分在进行快排
quick(array, start, par-1);
}
if(par < end - 1){//右半部分进行快排
quick(array, par+1, end);
}
}
public static void quickSort(int[] array){
int low = 0;
int high = array.length - 1;
quick(array, low, high);
}
2.非递归实现
用栈来实现
public static int partition(int[] array,int start,int end){//返回基准的下标
int low = start;
int high = end;
int tmp = array[low];
while(low < high){
while(low < high&&array[high] >= tmp){
--high;
}
if(low >= high){
break;
}else{
array[low] = array[high];
}
while(low < high&&array[low] <= tmp){
++low;
}
if(low >= high){
break;
}else{
array[high] = array[low];
}
}
array[low] = tmp;
return low;
}
public static void quickSort(int[] array){
int[] stack = new int[array.length];
int top = 0;
int low = 0;
int high = array.length-1;
//入栈
int par = partition(array,low,high);
if(par > low+1){
stack[top++] = low;
stack[top++] = par-1;
}
if(par < high-1){
stack[top++] = par+1;
stack[top++] = high;
}
//出栈
while(top > 0){
high = stack[--top];
low = stack[--top];
par = partition(array, low, high);
if(par >low+1){
stack[top++] = low;
stack[top++] = par-1;
}
if(par < high-1){
stack[top++] = par+1;
stack[top++] = high;
}
}
}
以上排序的复杂度与稳定性:
时间复杂度 | 稳定性 | |
冒泡排序 | O(n²) | 稳定 |
选择排序 | O(n²) | 不稳定 |
直接插入排序 | O(n²)(最好O(n)) | 稳定 |
希尔排序 | O(n^1.3)最坏(O(n²)) | 不稳定 |
快速排序 | O(nlogn)(最坏O(n²)) | 不稳定 |