冒泡排序(Bubble Sort)
-思想
每兩個數比較大小,大的數下沉,小的數上升。
-過程
(1)從前向後,逐位取相鄰兩個元素,arr[i]和arr[i+1]。
(2)如果arr[i] > arr[i+1],那麼交換位置,否則,重新取新的兩位元素;
-圖示
圖片來源:http://www.cnblogs.com/ysocean/p/7896269.html#_label2,侵刪
-java代碼
public class BubbleSort {
public static void bubbleSort(int[] unsorted) {
int uslen = unsorted.length;
int temp = 0;
for(int i = 0; i < uslen - 1; i++) {//表示趟數,也可以理解爲需要爲uslen個位置確定值
for(int j = 1; j< uslen -i;j++) {//在每一趟中,從前向後,選擇相鄰的兩個元素比較大小,大的在後,小的在前
if(unsorted[j] < unsorted[j -1]) {
temp = unsorted[j];
unsorted[j] = unsorted[j-1];
unsorted[j-1] = temp;
}
}
}
}
public static void main(String[] args) {
int[] testunsorteday = {1,23,45,21,567,34,45,1,3,78};
bubbleSort(testunsorteday);
for(int ele : testunsorteday) {
System.out.print(ele +" ");
}
}
}
-時間複雜度:O(n2)
-算法優化
針對問題:如果在其中的某一輪操作後,數組已經是有序的,那麼之後的比較過程不需要進行
改進方法:加入標誌信息flag,初始值爲false,如果在某一趟比較中,發現並沒有發生交換操作,那麼表示數組已經排好序,flag = true。
java代碼
public class BubbleSort {
public static void bubbleSort(int[] unsorted) {
int uslen = unsorted.length;
int temp = 0;
boolean flag = false;
for(int i = 0; i < uslen -1 ; i++) {//表示趟數,也可以理解爲需要爲uslen個位置確定值
flag = false;
for(int j = 1; j< uslen -i;j++) {//在每一趟中,從前向後,選擇相鄰的兩個元素比較大小,大的在後,小的在前
if(unsorted[j] < unsorted[j -1]) {
temp = unsorted[j];
unsorted[j] = unsorted[j-1];
unsorted[j-1] = temp;
flag = true;
}
}
if(flag != true) {
break;
}
}
}
public static void main(String[] args) {
int[] testunsorteday = {1,23,45,45,78,89,100};
bubbleSort(testunsorteday);
for(int ele : testunsorteday) {
System.out.print(ele +" ");
}
}
}
插入排序(Insertion Sort)
-思想
將待排序的元素插入到已排好序的子數組中。
-過程
(1)位置i的元素arr[i],那麼i之前的元素已經排好序了;
(2)arr[i]和前面的元素逐個比較,找到自己的新位置後插入,由於會佔用原來元素的位置,所以該位置之後的元素均向後移動。
-圖示
圖片來源:http://www.runoob.com/w3cnote/sort-algorithm-summary.html,侵刪
-java實現
public class InsertSort {
public static void insertSort(int[] unsorted) {
int uslen = unsorted.length;
int temp;
for(int i = 1; i<uslen;i++) {//待排序的元素位置
if(unsorted[i] < unsorted[i-1]) {
temp = unsorted[i];
int j = i -1;
while(j >= 0 && temp < unsorted[j]) {//待排序的元素和前面的元素逐個比較大小
unsorted[j+1] = unsorted[j];
j--;
}
unsorted[j+1] = temp;
}
}
}
public static void main(String[] args) {
int[] testarr = {45,23,56,1,4,67,88,345,43};
insertSort(testarr);
for(int ele : testarr) {
System.out.print(ele + " ");
}
}
}
-時間複雜度:O(n2)
希爾排序(Shell Sort)
-思路
希爾排序(Shell Sort)是插入排序的一種。也稱縮小增量排序,是直接插入排序算法的一種更高效的改進版本。希爾排序是非穩定排序算法。該方法因DL.Shell於1959年提出而得名。希爾排序是記錄按下標的一定增量分組,對每組使用直接插入排序算法排序;隨着增量逐漸減少,每組包含的關鍵詞越來越多,當增量減至1時,整個文件恰被分成一組,算法便終止。
我們分割待排序記錄的目的是減少待排序記錄的個數,並使整個序列向基本有序發展。而如上面這樣分完組後,就各自排序的方法達不到我們的要求。因此,我們需要採取跳躍分割的策略:將相距某個“增量”的記錄組成一個子序列,這樣才能保證在子序列內分別進行直接插入排序後得到的結果是基本有序而不是局部有序。
-過程
初始時,有一個大小爲 10 的無序序列。
(1)在第一趟排序中,我們不妨設 gap1 = N / 2 = 5,即相隔距離爲 5 的元素組成一組,可以分爲 5 組。
(2)接下來,按照直接插入排序的方法對每個組進行排序。
在第二趟排序中,我們把上次的 gap 縮小一半,即 gap2 = gap1 / 2 = 2 (取整數)。這樣每相隔距離爲 2 的元素組成一組,可以分爲 2 組。
(3)按照直接插入排序的方法對每個組進行排序。
(4)在第三趟排序中,再次把 gap 縮小一半,即gap3 = gap2 / 2 = 1。 這樣相隔距離爲 1 的元素組成一組,即只有一組。
(5)按照直接插入排序的方法對每個組進行排序。此時,排序已經結束。
-java實現
package sortalg;
public class ShellSort {
public static void shellSort(int[] unsorted) {
int uslen = unsorted.length;
int hip = uslen/2;
while(hip > 0) {
for(int i = 0;i<hip;i++) {
for(int j =i+hip; j<uslen;j+=hip) {
int m = j;
int base = unsorted[j];
while((m -= hip)>=0 && base <= unsorted[m]) {
unsorted[m+hip] = unsorted[m];
}
unsorted[m+hip] = base;
}
}
hip /= 2;
}
}
public static void main(String[] args) {
int[] testarr = {1,4,3,7,4,0,5,8,3,9,6};
shellSort(testarr);
for(int ele : testarr) {
System.out.print(ele + " ");
}
}
}
-時間複雜度O(nlogn)(此處原理還沒解決??)
-參考資料
選擇排序
-思路
找i位置後所有元素的最小值與位置i上的數比較,較小的值留在i位置。
-過程
0位置上元素的確定:尋找[1,length-1]中最小的元素,然後和0位上的值比較,如果最小的元素小於0位上的值,那麼互換。
1位置上元素的確定:尋找[2,length-1]中最小的元素,然後和1位上的值比較,如果最小的元素小於1位上的值,那麼互換。
....
length -2位置上元素的確定:尋找尋找[length - 1,length-1]中最小的元素,然後和(length -2)位上的值比較,如果最小的元素小於(length -2)位上的值,那麼互換。
-java代碼
public class SelectionSort {
public static void selectionSort(int[] unsorted) {
int uslen = unsorted.length;
int mindex = 0;
int temp =0;
for(int i = 0; i< uslen - 1;i++) {
mindex = i+1;
for(int j = i+1; j < uslen;j++) {
if(unsorted[j] < unsorted[mindex] ) {
mindex = j;
}
}
if(unsorted[i] > unsorted[mindex]) {
temp = unsorted[i];
unsorted[i] = unsorted[mindex];
unsorted[mindex] = temp;
}
}
}
public static void main(String[] args) {
int[] testarray = {1,34,23,12,56,35,789,3};
selectionSort(testarray);
for(int ele : testarray) {
System.out.print(ele + " ");
}
}
}
-時間複雜度:O(n2)
快速排序(Quick Sort)
-思路
快速排序使用分治思想實現,即選擇一個比較基準,經過一趟比較後,小於基準的元素位於基準的左邊,大於基準的元素位於基準的右邊。然後對基準左右兩邊的子數組再分別進行排序。
-過程
(1)選擇基準base,一般選擇第一個元素值作爲基準。low = 0,high = length-1;
(2)從後向前,取每個元素和base比較(即high--),當元素值<base時,將元素值和base位置互換,此時high位置上的值爲base;
(3)從前向後,low++,取每個元素和base比較,當元素值>base時,將元素值和base互換,此時low位置上的值爲base;
(4)重複(2)(3)步驟,直到low==high。
-圖示
圖片來源:https://blog.csdn.net/u010853261/article/details/54884784,侵刪
-java實現
public class QuickSort {
public static void quickSort(int[] unsorted,int low,int high) {
int bsidx;
if(high -low < 1) {//high- low = 0 :子數組中只有一個元素; high -low = -1:子數組中沒有元素
return;
}
bsidx = partition(unsorted,low,high);
quickSort(unsorted,low,bsidx -1);
quickSort(unsorted,bsidx+1,high);
}
//找到基準在有序結果中的位置
public static int partition(int[] unsorted,int low, int high) {
int base = unsorted[low];
while(low < high) {
//從後面向前遍歷的元素,都是大於基準值的 ,那麼high--
//有一種情況:low位置後的元素均大於基準值,那麼high>low保證了base不會和基準值之前的元素做比較了
//high > low保證了經過自減操作的high,不會超出數組下界
while(base <= unsorted[high] && high > low) {
high--;
}
unsorted[low] = unsorted[high];
//從前向後遍歷的元素,都是小於基準值的 ,那麼low++
//有一種情況:high位置前的元素均小於基準值,那麼high>low保證了base不會和基準值之後的元素做比較了
//high > low保證了經過自加操作的low,不會超出數組上界
while(base >= unsorted[low] && high > low) {
low++;
}
unsorted[high] = unsorted[low];
}
unsorted[high] = base;
return high;
}
//測試
public static void main(String[] args) {
int[] testarr = {12,3,567,89,34,65,86,2,9,46,58};
quickSort(testarr,0,testarr.length - 1);
for(int ele : testarr) {
System.out.print(ele + " ");
}
}
}
-時間複雜度O(nlogn)
-參考鏈接
歸併排序(Merge Sort)
-前言
歸併排序是建立在歸併操作上的一種有效的排序算法。該算法是採用分治法的一個非常典型的應用。
首先考慮下如何將2個有序數列合併。這個非常簡單,只要從比較2個數列的第一個數開始,誰小就先取誰,取了後就在對應數列中刪除這個數。然後再進行比較,如果有數列爲空,那直接將另一個數列的數據依次取出即可。
//將有序數組a[]和b[]合併到c[]中
void MemeryArray(int a[], int n, int b[], int m, int c[])
{
int i, j, k;
i = j = k = 0;
while (i < n && j < m)
{
if (a[i] < b[j])
c[k++] = a[i++];
else
c[k++] = b[j++];
}
while (i < n)
c[k++] = a[i++];
while (j < m)
c[k++] = b[j++];
}
-思路
歸併排序算法,引用了歸併的思想,基本思想是:將數組分成2組A,B,如果這2組組內的數據都是有序的,那麼就可以很方便的將這2組數據進行排序。如何讓這2組組內數據有序了?
可以將A,B組各自再分成2組。依次類推,當分出來的小組只有1個數據時,可以認爲這個小組組內已經達到了有序,然後再合併相鄰的2個小組就可以了。這樣通過先遞歸的分解數列,再合併數列就完成了歸併排序。
-過程
-java實現
public class MergeSort {
public static void mergesort(int[] unsorted,int start,int end,int[] temp) {
if(start < end) {
int middle = (start + end)/2;
mergesort(unsorted,start,middle,temp);
mergesort(unsorted,middle+1,end,temp);
mergearray(unsorted,start,middle,end,temp);
}
}
//將排好序的子數組[start,middle]和[middle+1,end]合併爲一個有序的數組
public static void mergearray(int[] unsorted,int start,int middle,int end,int[] temp) {
int i = start;
int j = middle+1;
int k = 0;
while(i <= middle && j <= end) {
if (unsorted[i] < unsorted[j]) {
temp[k++] = unsorted[i++];
} else {
temp[k++] = unsorted[j++];
}
}
if(i <= middle) {
for(;i<=middle;i++) {
temp[k++] = unsorted[i];
}
}
if(j <= end) {
for(;j<=end;j++) {
temp[k++] = unsorted[j];
}
}
for(int m =0,l=start;m<= end - start;m++) {//temp是每次存儲有序子數組的,其大小雖然爲unsorted.length,但是其中有效的只有前end-start+1位。
unsorted[l++] = temp[m];
}
}
public static void main(String[] args) {
int[] testarr = {12,34,2,56,678,45,65,7,9,87};
int[] temp = new int[testarr.length];
mergesort(testarr,0,testarr.length -1,temp);
for(int ele : testarr) {
System.out.print(ele +" ");
}
}
}
-時間複雜度O(nlogn)
基數排序()
-BinSort
基本思想:
BinSort想法非常簡單,首先創建數組A[MaxValue](MaxValue爲原數組中最大的元素值);然後將每個數放到相應的位置上(例如17放在下標17的數組位置);最後遍歷數組,即爲排序後的結果。圖示:
BinSort
- 問題: 當序列中存在較大值時,BinSort 的排序方法會浪費大量的空間開銷。
RadixSort
- 基本思想: 基數排序是在BinSort的基礎上,通過基數的限制來減少空間的開銷。
- 過程:
過程1
過程2
(1)首先確定基數爲10,數組的長度也就是10。每個數(比如34)都會在這10個數中尋找自己的位置。
(2)不同於BinSort會直接將數34放在數組的下標34處,基數排序是將34分開爲3和4,第一輪排序根據最末位放在數組的下標4處,第二輪排序根據倒數第二位放在數組的下標3處,然後遍歷數組即可。
public static void RadixSort(int A[],int temp[],int n,int k,int r,int cnt[]){
//A:原數組
//temp:臨時數組
//n:序列的數字個數
//k:最大的位數2
//r:基數10
//cnt:存儲bin[i]的個數
for(int i=0 , rtok=1; i<k ; i++ ,rtok = rtok*r){
//初始化
for(int j=0;j<r;j++){
cnt[j] = 0;
}
//計算每個箱子的數字個數
for(int j=0;j<n;j++){
cnt[(A[j]/rtok)%r]++;
}
//cnt[j]的個數修改爲前j個箱子一共有幾個數字
for(int j=1;j<r;j++){
cnt[j] = cnt[j-1] + cnt[j];
}
for(int j = n-1;j>=0;j--){ //重點理解
cnt[(A[j]/rtok)%r]--;
temp[cnt[(A[j]/rtok)%r]] = A[j];
}
for(int j=0;j<n;j++){
A[j] = temp[j];
}
}
}
-參考鏈接
堆排序(Heap Sort)
-關於堆數據結構的基礎知識,可以參考博客資源:堆數據結構
-堆排序的實現:
1.利用BUILD-MAX-HEAP將輸入數組A[1..n]建成最大堆,n=A.length,h = A.heapsize=A.length;
2.在生成的最大堆中,根結點即A[1]存放的是最大的元素,那麼可以將A[1]和A[h]互換,使A[h]的值爲最大值;
3.在第二步的基礎上,可以去掉結點h(通過減少A.heapsize的值來實現,即h--)。在剩餘的結點中,原來根的孩子結點仍然是最大堆,而新的根結點可能會違背最大堆的性質。
4.爲了維護最大堆的性質,調用MAX-HEAPIFY(A,1),從而在A[1,h]上構造一個新的最大值。
5,不斷重複以上2-4步,直到h==1的時候。
-圖例
給定數組內容爲:16,14,10,8,7,9,3,2,4,1
-時間複雜度:O(nlgn)
-java實現
package sortalg;
public class HeapSort {
/*
* 功能:建堆,數組中的元素滿足最大堆性質
*/
public void buildMaxHeap(int[] arr) {
int len = arr.length;
for(int i = len/2 - 1 ;i >= 0; i--) {
maxHeapify(arr,i,true,0);
}
}
/*
* 功能:維護最大堆性質
*/
public void maxHeapify(int[] arr,int index,boolean flag,int heapsize) {
int largest = index;
int temp;
int arrange;
if(flag == true) {//表示數組中所有的元素都是可操作的堆元素
arrange = arr.length - 1;
}else {//表示數組中只有[0,heapsize]中的元素是可操作的堆元素
arrange = heapsize;
}
if(index * 2 +1 <= arrange && arr[index] < arr[index *2 +1]) {
largest = index * 2 +1;
}
if(index * 2 +2 <= arrange && arr[largest] < arr[index * 2 +2]) {
largest = index *2 +2;
}
if(largest != index) {
temp = arr[index];
arr[index] = arr[largest];
arr[largest] = temp;
maxHeapify(arr,largest,flag,heapsize);
}
}
/*
* 功能:堆排序實現
*/
public void heapSort(int[] arr) {
int hsize = arr.length - 1;
int temp;
while(hsize > 0) {
temp = arr[hsize];
arr[hsize] = arr[0];
arr[0] = temp;
hsize--;
maxHeapify(arr,0,false,hsize);
}
}
/*
* 測試
*/
public static void main(String[] args) {
HeapSort hs = new HeapSort();
int[] test = {16,14,10,8,7,9,3,2,4,1};
hs.buildMaxHeap(test);
for(int i:test) {
System.out.print(i+ " ");
}
System.out.println();
hs.heapSort(test);
for(int i:test) {
System.out.print(i+ " ");
}
}
}