一些术语和变量解释
public static long swapCount = 0; //交换次数
public static long compareCount = 0; //比较次数
public static long moveCount = 0; //数据移动次数
先简单介绍下这次要输出的算法有哪些:
插入算法
插入算法是将未知的数按顺序取出放到另一个已排好序的队列中,将其放到合适的位置,形成新的排好序的队列。最直观的比较就是跟齐牌一样,将每一张摸好的牌插入到你前面齐好的顺序之中。
思路:
1、按顺序选取一个数
2、将选取的数与前面的数比较,比前面的数小就和前面的数交换,比前面的数大就停止比较
代码实现:
public static void insertSort(int[] arr){
for (int i = 1; i < arr.length; i++) {
int j = i,temp = arr[i],k = i;
for (; j > 0 && temp < arr[j - 1]; j--) compareCount++;
for (; k > j && k > 0; k--) {
arr[k] = arr[k - 1];
moveCount++;
};
arr[k] = temp;
swapCount++;
}
}
时间复杂度:T(n) = O(n),两次for循环
空间复杂度:S(n) = O(1),不需要额外的空间
选择排序
选择排序和插入排序正好相反,插入排序是按顺序选一个数放进排好序的队列,而选择排序是依次选一个最小或者最大的数取出来放进另一个队列。
思路:
1、从未排序的数组中选取一个最小的数
2、将选取的数直接放入前面排好序的数组
代码实现:
public static void selectSort(int[] arr) {
for (int i = 0; i < arr.length - 1; i++) {
int minIndex = i;
int tempValue = arr[i];
for (int j = i+1; j < arr.length; j++) {
if (arr[j] < tempValue) {
minIndex = j;
tempValue = arr[j];
}
compareCount++;
}
int temp = arr[i];
arr[i] = arr[minIndex];
arr[minIndex] = temp;
swapCount++;
}
}
空间复杂度:T(n) = O(n),也是两次循环导致的。
空间复杂度:S(n) = O(1),不需要额外的空间。
归并排序
归并排序是典型的采用分治算法,降低比较的层级。将一个数组从1变为2,从2变为4,一直到数组只有一个数为止,然后再依次合并排好序的数组,虽然增加了数组的个数,但减少了比较的复杂度和数量。
思路:
1、将数组以中间拆分成两个数组,不断递归拆分,知道数组大小为1
2、将两个数组一次取出数进行比较,将小的数放入一个新的数组
3、将剩余没有放完的数组全部取出放入新的数组后面
4、将新的数组数据转移回原数组
代码实现:
public static void mergeSort(int[] arr,int left,int right){
if (right <= left) {
return;
}
int mid = (left + right) / 2;
mergeSort(arr, left, mid);
mergeSort(arr,mid+1,right);
merge(arr, left, mid, right);
}
public static void merge(int[] arr, int low, int mid, int high) {
int[] temp = new int[high - low + 1];
int leftLength = mid - low + 1;
int rightLength = high - mid;
int i = 0,j = 0,tempIndex = 0;
while (i < leftLength && j < rightLength) {
if (arr[i + low] <= arr[j + mid + 1]) {
temp[tempIndex++] = arr[i + low];
i++;
} else {
temp[tempIndex++] = arr[j + mid + 1];
j++;
}
compareCount++;
moveCount++;
}
while (i < leftLength) {
temp[tempIndex++] = arr[i + low];
i++;
moveCount++;
}
while (j < rightLength) {
temp[tempIndex++] = arr[j + mid + 1];
j++;
moveCount++;
}
for (int k = low; k <= high; k++) {
arr[k] = temp[k - low];
moveCount++;
}
}
时间复杂度:T(n) = O(nlogn),采用分治算法一共分治了logn个层级,每个层级需要n次合并操作,所以时间复杂度是O(nlogn)。
空间复杂度:S(n) = O(n),需要一个额外的数组储存归并排好序的数据。
冒泡排序
冒泡排序几乎可以算得上是一个暴力算法,选取每一个数和所有数进行比较,然后不停的交换,依次将将大的数交换到后面。
思路:
1、每一个数依次全部比较,选出最大的数放在后面
2、循环中每次比较的次数少1,后面的数逐渐拍好,直到剩下一个数位置
代码实现:
public static void bubbleSort(int[] arr){
for (int i = 1; i < arr.length; i++) {
for (int j = 0; j < arr.length - i; j++) {
if (arr[j] > arr[j + 1]) {
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
swapCount++;
}
compareCount++;
}
}
}
时间复杂度:T(n) = O(n^2),两次for循环的比较。
空间复杂度:S(n) = O(1),不需要额外的空间。
快速排序
快速排序的和分治算法的思想有点类似,只不过快速排序是找一个基准数,将其分为两部分,左边的数都比它小,右边的数都比它大。然后不断的递归实现。
思路:
1、选取第一个数为基准数。
2、选取左右两个指针,左边的数比基准数大向右走,右边的数比基准数小向左走,否则交换两个数。
3、递归实现
代码实现:
public static void quickSort(int[] a,int left, int right){
if (left >= right) {
return;
}
int v = a[left];
int l = left + 1;
int r = right;
while (l < r) {
if (a[l] <= v) {
l++;
compareCount++;
continue;
}
if (a[r] >= v) {
r--;
compareCount++;
continue;
}
int temp = a[l];
a[l] = a[r];
a[r] = temp;
l++;
r--;
swapCount++;
}
if (a[left] > a[l]) {
a[left] = a[l];
a[l] = v;
}else {
a[left] = a[l - 1];
a[l - 1] = v;
l--;
}
compareCount++;
swapCount++;
quickSort(a,left,l-1);
quickSort(a,l+1,right);
}
时间复杂度:T(n) = O(nlogn),最快情况下是O(n^2),
空间复杂度:S(n) = O(1),没有额外的辅助存储空间。
希尔排序
希尔排序的思想是选取一个步长k,从中抽取不同的数组,他们都是以k为步长为一组进行内部排序,然后不断的缩小步长,缩短到1后结束排序。
思路:
1、选取一个步长
2、将每一步长的数组排序
3、缩小步长重复2步骤
4、直到步长为1结束
代码实现:
public static void shellSort(int[] a,int step) {
while (step >= 1) {
for (int i = 1; i <= step; i++) {
for (int j = i - 1; j < a.length; j+=step) {
int temp = a[j];
int k = j;
while (k >= i + step - 1 && temp < a[k - step]) {
a[k] = a[k - step];
k -= step;
moveCount++;
compareCount++;
}
a[k] = temp;
swapCount++;
}
}
step-=2;
}
}
时间复杂度:T(n) = O(nlogn),最坏情况O(n(logn)^2)。
空间复杂度:S(n) = O(1)。不需要额外的存储空间。
算法性能比较
这里的数据是java随机生成的10W个(0~100000)的数据。
比较次数是10W次。
复杂度性能比较
这里的稳定性是说如果数组中a[i]和a[j]相等且i<j,在比较结束之后a[i]和a[j]中的数据没有进行交换,不稳定性和稳定性相反。
更多精彩能容请听下回分解。
欢迎大家关注我的公众号"计算机基础爱好者"
谢谢