冒泡排序只会操作相邻的两个数据。每次冒泡操作都会对相邻的两个元素进行比较,看是否满足大小关系要求。如果不满足就让它俩互换。一次冒泡会让至少一个元素移动到它应该在的位置,重复n次,就完了n个数据的排序工作。
// 冒泡排序,a 表示数组,n 表示数组大小
public void bubbleSort(int[] a, int n) {
if (n <= 1) return;
for (int i = 0; i < n; ++i) {
// 提前退出冒泡循环的标志位
boolean flag = false;
for (int j = 0; j < n - i - 1; ++j) {
if (a[j] > a[j+1]) { // 交换
int tmp = a[j];
a[j] = a[j+1];
a[j+1] = tmp;
flag = true; // 表示有数据交换
}
}
if (!flag) break; // 没有数据交换,提前退出
}
}
插入排序是将数组中的数据分为两个区间,已排序区间和未排序区间。初始已排序区间只有一个元素,就是数据的第一个元素。插入算法的核心思想是取未排序区间中的元素,在已排序区间中找到合适的插入位置将其插入,并保证已排序区间一直有序。重复这个过程,直到未排序区间中元素为空,算法结束。
// 插入排序,a 表示数组,n 表示数组大小
public void insertionSort(int[] a, int n) {
if (n <= 1) return;
for (int i = 1; i < n; ++i) {
int value = a[i];
int j = i - 1;
// 查找插入的位置
for (; j >= 0; --j) {
if (a[j] > value) {
a[j+1] = a[j]; // 数据移动
} else {
break;
}
}
a[j+1] = value; // 插入数据
}
}
选择排序算法的实现思路有点类似插入排序,也分为已排序区间和未排序区间。但是选择排序每次会从未排序区间中找到最小的元素,将其放到已排序区间的末尾。
//选择排序
public void selectionSort(int[] a,int n){
if(n<=1) return;
for(int i=0;i<n-1;++i){
//查找最小值
int minIndex=i;
for(int j=i+1;j<n;++j){
if(a[j]<a[minIndex]){
minIndex=j;
}
}
//交换
int tmp=a[i];
a[i]=a[minIndex];
a[minIndex]=tmp;
}
}
归并排序:
归并排序的核心思想,是把数组从中间开始分成两部分,然后分别排序,在合并在一起。归并使用到的就是分治的思想。分治,顾名思义,就是分而治之,将一个大问题分解成小的问题。小的问题解决了,大问题也就解决了。
//递归调用函数
private static void mergeSortInternally(int[] a,int p,int r){
//递归终止条件
if(p>=r) return;
//取p到r之间的中间位置q,防止(p+r)的和超过int类型最大值
int q=p+(r-p)/2;
//分治递归
mergeSortInternally(a,p,q);
mergeSortInternally(a,q+1,r);
//将A[p...q]和A[q+1...r]合并为A[p...r]
merge(a,p,q,r);
}
private static void merge(int[] a,int p,int q,int r){
int i=p;
int j=q+1;
int k=0;//初始化变量i,j,k
int[] tmp=new int[r-p+1];//申请一个大小跟a[p...r]一样的临时数组
while(i<=q&&j<=r){
if(a[i]<=a[j]){
tmp[k++]=a[i++];
}else{
tmp[k++]=a[j++];
}
}
//判断哪个子数组中有剩余的数据
int start=i;
int end=q;
if(j<=r){
start=j;
end=r;
}
//将剩余的数据拷贝到临时数组tmp
while (start<=end){
tmp[k++] =a[start++];
}
//将tmp中的数组拷贝回a[p...r]
for(i=0;i<r-p;++i){
a[p+i]=tmp[i];
}
}
快速排序
快排的思想是这样的:如果要排序数组下标从p到r之间的一组数据,我们选择p到r之间的任意一个数据作为pivot(分区点)。
我们遍历p到r之间的数据,将小于pivot的放到左边,将大于pivot的放到右边,将pivot放到中间。经过这一步骤之后,数组p到r之间的数据就被分成了三个部分,前面p到q-1之间都是小于pivot的,中间是pivot,后面的q+1到r之间是大于pivot的。
private static void quickSortInternally(int[] a,int p,int r){
if(p>=r) return;
int q=partition(a,p,r);//获取分区点
quickSortInternally(a,p,q-1);
quickSortInternally(a,q+1,r);
}
private static int partition(int[] a,int p,int r){
int pivot=a[r];
int i=p;
for(int j=p;j<r;++j){
if(a[j]<pivot){
if(i==j){
++i;
}else{
int tmp=a[i];
a[i++]=a[j];
a[j]=tmp;
}
}
}
int tmp=a[i];
a[i]=a[r];
a[r]=tmp;
System.out.println("i="+i);
return i;
}