1. 插入排序:
先用tmp=a[j+1]保存待插入的數,在還沒有找到插入點時,直接用a[j+1] = a[j],免去每一次都交換;再插入數據後就可以跳出內部循環了。
代碼:
public class InsertSort {
public static void main(String[] args) {
int len = 8000;
int[] a = new int[len];
for ( int i = 0; i < len; i++) {
a[i] = (int)(Math.random()*2000000);
}
int cnt = sort(a);
System.out.printf("數組長度爲:%d,一共進行了:%d\n", a.length, cnt);
System.out.println(Arrays.toString(a));
}
public static int sort(int[] a) {
int stepCnt = 0;
for (int i = 1; i < a.length; i++) {
int tmp = a[i]; //預先保存好待插入待數
for (int j = i-1; j >= 0; j--) {
stepCnt++;
if (tmp < a[j]) {
a[j+1] = a[j]; //直接用前面待數據覆蓋後面的數據,不再需要交換
} else {
a[j] = tmp; //將前面保存的待插入數賦給目的位置
break; //數據已經插入,跳出內部循環
}
}
}
return stepCnt;
}
}
2. 希爾排序
間隔數組的排序也使用插入排序,才能提高速度,如sort2, sort則不是插入排序的增強,導致更慢。
代碼:
public class ShellSort {
public static void main(String[] args) {
int len = 8000;
int[] a = new int[len];
for ( int i = 0; i < len; i++) {
a[i] = (int)(Math.random()*2000000);
}
int cnt = sort2(a);
System.out.printf("數組長度:%d, 一共進行了:%d\n", a.length, cnt);
System.out.println(Arrays.toString(a));
}
public static int sort(int[] a) {
int stepCnt = 0;
int tmp = 0;
for (int gap = a.length/2; gap > 0; gap /= 2) {
for (int i = gap; i < a.length; i++) {
//遍歷各組中所有的元素(共有5組, 每組有2個元素,步長5
for (int j = i-gap; j >= 0; j -= gap) {
stepCnt++;
if (a[j] > a[j+gap]) {
tmp = a[j];
a[j] = a[j+gap];
a[j+gap] = tmp;
}
}
}
}
return stepCnt;
}
public static int sort2(int[] a) {
int stepCnt = 0;
//增量gap, 逐步縮小增量
for (int gap = a.length/2; gap > 0; gap /= 2) {
for (int i = gap; i < a.length; i++) {
int j = i;
int tmp = a[j];
if (a[j] < a[j-gap]) {
while (j-gap >= 0 && tmp < a[j-gap]) {
stepCnt++;
a[j] = a[j-gap];
j -= gap;
}
//退出while時,插入tmp
a[j] = tmp;
}
}
}
return stepCnt;
}
}
3. 快速排序
找到某個位置上面到值,將大於這個值到數都丟到這個位置到右邊,將比這個值小到數都丟到這個位置到左邊。 遞歸進行那個位置左右兩邊的數據。當數組長度比較小時(比如遞歸到 數組長度爲10時)使用插入排序更能提高速度,看我的c版本。
代碼:
public class QuickSort {
public static void main(String[] args) {
int len = 8;
int[] a = new int[len];
for ( int i = 0; i < len; i++) {
a[i] = (int)(Math.random()*20);
}
sort(a, 0, a.length-1);
System.out.println(Arrays.toString(a));
}
public static int sort(int[] a, int left, int right) {
int stepCnt = 0;
int l = left;
int r = right;
int pivot = a[ (left+right) / 2 ];
int tmp = 0;
while (l < r) {
while (a[l] < pivot) { //在pivot的左邊一直找,直到發現 >= pivot的值
l++;
}
while (a[r] > pivot) { //在pivot的右邊一直找,直到發現 <= pivot的值
r--;
}
if (l >= r) { //pivot 的左右兩邊的值已經滿足左邊都小於pivot,右邊大於=pivot
break;
}
//交換
tmp = a[l];
a[l] = a[r];
a[r] = tmp;
//交換之後,發現a[l] == pivot r--; 避免55555這種情況死循環
if (a[l] == pivot) {
r -= 1;
}
//交換之後,發現a[r] == pivot l++ 後移; 避免55555這種情況死循環
if (a[r] == pivot) {
l += 1;
}
}
//如果 l == r,避免出現棧溢出
if (l == r) {
l += 1;
r -= 1;
}
//向左遞歸
if (left < r) {
sort(a, left, r);
}
//向右遞歸
if (right > l) {
sort(a, l, right);
}
return stepCnt;
}
}
4. 歸併排序
把長數組分割成很多到小段, 直至每一個小段只有兩個數字,將這個兩個數值分割成左右兩邊,用merge方法將他們由小到大merge,然後擴大2倍,此時左右兩邊都已經按照大小排好序,再用merge方法把兩邊數據合併,依次類推。由於算法過程中使用到臨時內存,未來避免頻繁分配內存和垃圾回溯到時間損耗,再算法開始之前預分配出一個與原數組同等大小到內存棧供後面算法使用,能夠有效提高算法速度。
非遞歸實現代碼:
public class MergeSort {
public static void main(String[] args) {
int len = 8;
int[] a = new int[len];
for ( int i = 0; i < len; i++) {
a[i] = (int)(Math.random()*2000000);
}
int cnt = sort1(a);
System.out.printf("數組長度爲:%d, 一個花了:%d步\n", a.length, cnt);
System.out.println(Arrays.toString(a));
}
public static int sort() {
int stepCnt = 0;
return stepCnt;
}
/**
* 非遞歸方法實現歸併排序
* @param a
* @return
*/
public static int sort1(int a[]) {
int[] tmp = new int[a.length];
int stepCnt = 0, lStart, lEnd, rStart, rEnd, next;
for (int i = 2; (i / 2) <= a.length; i *= 2) {
for (int j = 0; j < a.length; j += i) {
lStart = j; // 8
lEnd = Math.min((j + i/2), a.length); // ) 9
rStart = lEnd;; // [ 9
rEnd = Math.min((j + i), a.length); // ) 9
stepCnt++;
next = 0;
while (lStart < lEnd && rStart < rEnd) {
if (a[lStart] < a[rStart]) {
tmp[next++] = a[lStart++];
} else {
tmp[next++] = a[rStart++];
}
}
while (lStart < lEnd) {
tmp[next++] = a[lStart++];
}
while (rStart < rEnd) {
tmp[next++] = a[rStart++];
}
for (int k = 0; k < next; k++) {
a[j+k] = tmp[k];
}
}
}
return stepCnt;
}
}