數據結構與算法之美學習筆記(11章) 排序,冒泡,插入,選擇,歸併,快排

第十一章 

最常用的:冒泡排序、插入排序、選擇排序、歸併排序、快速排序、計數排序、基數排序、桶排序。

 

1.冒泡排序

當某次冒泡操作已經沒有數據交換時,說明已經達到完全有序,不用再繼續執行後續的冒泡操作

代碼

/**
 * @author: xuxu
 * @date 2020/2/27 9:29
 * @Description: 冒泡排序
 */
public class BubbleSort {

    public static void bubbleSort(int[] arr){
        //控制第幾次排序
        for(int i=0;i<arr.length;i++){
            //因爲冒泡排序 如果有一次沒有發生排序證明已經完成排序,設置跳出的標誌位,默認沒發送排序
            boolean isSort=false;
            //控制每次排序數據間的比較和換位
            //最後一個位置不需要發生比較所以-1,每進行一次後最後穩定的部分新增1不需要比較所以-i
            for (int j=0;j<arr.length-i-1;j++){
                if(arr[j]>arr[j+1]){
                    int temp = arr[j+1];
                    arr[j+1]=arr[j];
                    arr[j]=temp;
                    isSort=true;
                }
            }
            if(!isSort){
                System.out.print("第"+(i+1)+"次排序後數組無變化退出");
                break;
            }
            //遍歷
            System.out.print("第"+(i+1)+"次排序後數組爲:");
            for (int k=0;k<arr.length;k++){
                System.out.print(arr[k]+" ");
            }
            System.out.println();
        }
    }

    public static void main(String[] args) {
        int[] arr = {6,3,2,1,4};
        bubbleSort(arr);
    }
}

 

2.插入排序

首先,我們將數組中的數據分爲兩個區間,已排序區間未排序區間。初始已排序區間只有一個元素,就是數組的第一個元素。插入算法的核心思想是取未排序區間中的元素,在已排序區間中找到合適的插入位置將其插入,並保證已排序區間數據一直有序。重複這個過程,直到未排序區間中元素爲空,算法結束。

插入排序也包含兩種操作,一種是元素的比較,一種是元素移動。當我們需要將一個數據 a 插入到已排序區間時,需要拿 a 與已排序區間的元素依次比較大小,找到合適的插入位置。找到插入點之後,我們還需要將插入點之後的元素順序往後移動一位,這樣才能騰出位置給元素 a 插入。

/**
 * @author: xuxu
 * @date 2020/2/27 9:56
 * @Description:插入排序
 */
public class InsertSort {

    public static void  insertSort(int[] arr){
        //這裏循環的是未排序部分
        for(int i=1;i<arr.length;i++){
            //本次循環要比較插入的數字
            int insertNum = arr[i];
            //這裏是循環已排序部分,從後往前比較
            int j=i-1;
            for(;j>=0;j--){
                //如果前一個數字大於要插入的數字則向後移動讓出位置
                if(arr[j]>insertNum){
                    arr[j+1] = arr[j];
                }else{
                    //一旦他找到對的位置,則不需要向前繼續找,因爲前面是排好序的
                    break;
                }
            }
            //將要插入的數放入前面找到的插入的位置 這裏j+1是因爲前面j--了
            arr[j+1]=insertNum;
            //遍歷
            System.out.print("第"+(i)+"次排序後數組爲:");
            for (int k=0;k<arr.length;k++){
                System.out.print(arr[k]+" ");
            }
            System.out.println();
        }
    }

    public static void main(String[] args) {
        int[] arr = {6,3,2,1,4};
        insertSort(arr);
    }
}

 

3.選擇排序

選擇排序算法的實現思路有點類似插入排序,也分已排序區間和未排序區間。但是選擇排序每次會從未排序區間中找到最小的元素,將其放到已排序區間的末尾。

/**
 * @author: xuxu
 * @date 2020/2/27 11:02
 * @Description: 選擇排序
 */
public class SelectSort {

    public static void selectSort(int[] arr){
        for (int i=0;i<arr.length;i++){
            int min=i;
            for(int j=i;j<arr.length-1;j++){
                if(arr[min]>arr[min+1]){
                    //先找出未排序區最小的一個下標
                    min = min+1;
                }
            }
            //將min放入已排序區的最後面,同時原位置的元素放在min處
            int temp = arr[i];
            arr[i]=arr[min];
            arr[min] = temp;

            //遍歷
            System.out.print("第"+(i+1)+"次排序後數組爲:");
            for (int k=0;k<arr.length;k++){
                System.out.print(arr[k]+" ");
            }
            System.out.println();
        }
    }

    public static void main(String[] args) {
        int[] arr = {6,3,2,1,4};
        selectSort(arr);
    }
}

從推薦使用的角度看 插入>冒泡>選擇

 

冒泡排序、插入排序、選擇排序這三種排序算法,它們的時間複雜度都是 O(n2),比較高,適合小規模數據的排序。

歸併排序快速排序兩種時間複雜度爲 O(nlogn) 。這兩種排序算法適合大規模的數據排序

歸併排序和快速排序都用到了分治思想

 

4.歸併排序

分治算法一般都是用遞歸來實現的。分治是一種解決問題的處理思想,遞歸是一種編程技巧,這兩者並不衝突。

遞歸公式

遞推公式:
merge_sort(p…r) = merge(merge_sort(p…q), merge_sort(q+1…r))
 
終止條件:
p >= r 不用再繼續分解

merge_sort(p…r) 表示,給下標從 p 到 r 之間的數組排序。我們將這個排序問題轉化爲了兩個子問題,merge_sort(p…q) 和 merge_sort(q+1…r),其中下標 q 等於 p 和 r 的中間位置,也就是 (p+r)/2。當下標從 p 到 q 和從 q+1 到 r 這兩個子數組都排好序之後,我們再將兩個有序的子數組合並在一起,這樣下標從 p 到 r 之間的數據就也排好序了

合併過程

我們申請一個臨時數組 tmp,大小與 A[p…r] 相同。我們用兩個遊標 i 和 j,分別指向 A[p…q] 和 A[q+1…r] 的第一個元素。比較這兩個元素 A[i] 和 A[j],如果 A[i]<=A[j],我們就把 A[i] 放入到臨時數組 tmp,並且 i 後移一位,否則將 A[j] 放入到數組 tmp,j 後移一位。

繼續上述比較過程,直到其中一個子數組中的所有數據都放入臨時數組中,再把另一個數組中的數據依次加入到臨時數組的末尾,這個時候,臨時數組中存儲的就是兩個子數組合並之後的結果了。最後再把臨時數組 tmp 中的數據拷貝到原數組 A[p…r] 中。

/**
 * @author: xuxu
 * @date 2020/2/27 14:32
 * @Description: 歸併排序
 * f(a,p) =f(a,q)+f(q+1,p)
 */
public class MergeSort {

    public static void mergeSort(int[] arr,int begin,int end ){
        if(begin>=end){
            return ;
        }
        //中間位置
        int mid = (begin+end)/2;
        //遞歸分解合併
        mergeSort(arr, begin, mid);
        mergeSort(arr, mid+1 ,end);
        merge(arr,begin,mid,end);
    }

    /**
     * 合併操作
     */
    public static void merge(int[] arr, int begin, int mid, int end){
        int[] mergeArr=new int[arr.length];
        //數組1的指針
        int p1 = begin;
        //數組2的指針
        int p2 = mid+1;
        //合併的數組起始位置
        int index=begin;
        while(p1<=mid && p2<=end){
            if(arr[p1]>=arr[p2]){
                mergeArr[index++]=arr[p2++];
            }else{
                mergeArr[index++]=arr[p1++];
            }
        }
        while(p1<=mid){
            mergeArr[index++] = arr[p1++];
        }
        while(p2<=end){
            mergeArr[index++] = arr[p2++];
        }

        for(int i=begin;i<=end;i++){
            arr[i] = mergeArr[i];
        }
    }

    public static void main(String[] args) {
        int[] arr = {6,5,3,4,2,1,7};
        mergeSort(arr, 0, arr.length-1);
        System.out.print("排序後數組元素爲:");
        for (int i=0;i<arr.length;i++){
            System.out.print(arr[i]);
        }
    }
}

 

5.快速排序

基本思想:選擇一個基準元素,通常選擇第一個元素或者最後一個元素,通過一趟掃描,將待排序列分成兩部分,一部分比基準元素小,一部分大於等於基準元素,此時基準元素在其排好序後的正確位置,然後再用同樣的方法遞歸地排序劃分的兩部分

實現

public class QuickSort {
	
	public static int getMid(int[] nums,int low,int high){
		int temp=nums[low];
		while(low<high){
			while(low<high && nums[high]>=temp){
				high--;
			}
			nums[low]=nums[high];//如果右邊小於中間數 移動到左邊
			while(low<high && nums[low]<=temp){
				low++;			
			}
			nums[high]=nums[low];	//如果左邊有大於中間數 的數移動到右邊
		}
		nums[low]=temp;	//賦值中間數
		return low;
	}
	
	public static void quick(int[] nums , int low,int high){
		if(low<high){
		int mid = getMid(nums,low,high);
		//遞歸調用左右兩邊繼續排序
		quick(nums,low,mid-1);
		quick(nums,mid+1,high);
		}
	}
	
	public static void main(String[] args) {
		int[] nums = {22,11,33,66,77,55,44,99,88};
		quick(nums,0,nums.length-1);
		for (int i = 0; i < nums.length; i++) {
			System.out.print(nums[i]+" ");
		}
	}
}

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章