冒泡,插入,歸併,快速排序(優化)

 1. 冒泡排序

(1)如果在子循環中,不存在任何交換,說明數組已經排好序了;

(2)可以記錄子循環中交換的最大索引,次索引之後的元素都已經排好了。

// 冒泡排序
    void bubbleSort(vector<int> &nums) {
        int n = nums.size();
        cout << n << endl;
        int pos = 0;
        for(int i = 0; i < n; i++) {
            bool isSwap = false;
            if(i < pos) {
                i = pos;
            }
            for(int j = 0; j < n - i - 1; j++) {
                if(nums[j] > nums[j+1]) {
                    swap(nums[j], nums[j+1]);
                    isSwap = true;
                    pos = n - j;
                }
            }
            if(!isSwap) {
                break;
            }
        }
    }

2. 插入排序 

(1)插入排序比一般O(n2)的排序算法快,因爲它會提前終止,這在實現時一定要加上;

(2)一種優化的方式:插入當前元素時,先找到位置,再交換,而不是一直交換,這樣效率高一些;

(3)對於基本有序的數組,插入排序特別高效,會進化成接近O(n)。

    //插入排序
    void insertionSort(vector<int> &nums) {
        int n = nums.size();
        for(int i = 0; i < n-1; i++) {
            int pos = i+1;
            int v = nums[pos];
            for(int j = i + 1; j > 0; j--) {
                if(v < nums[j-1]) {
                    //swap(nums[j], nums[j-1]);
                    nums[j] = nums[j-1];
                    pos--;
                } else {
                    break;
                }
            }
            nums[pos] = v;
        }
    }

3. 歸併排序

(1)優化1: 當左邊的最大元素小於右邊的最小元素時,無需排序;

(2)優化2:當待排序區間小於某個數時,使用插入排序完成(因爲數據越小,有序性越好,插入排序效果更好)。

//歸併排序
    void mergeSort(vector<int> &nums) {
        __mergeSort(nums, 0, nums.size()-1);
    }

    void __mergeSort(vector<int> &nums, int l, int r) {
        if(l >= r) {
            return;
        }
        int mid = (l + r) / 2;
        __mergeSort(nums, l, mid);
        __mergeSort(nums, mid+1, r);
        if(nums[mid] > nums[mid+1])
            merge(nums, l, mid, r);
    }

    void merge(vector<int> &nums, int l, int mid, int r) {
        vector<int> tmp(r-l+1, 0);
        int i = l, j = mid+1;
        int k = 0;
        while(i <= mid && j <= r) {
            if(nums[i] <= nums[j]) {
                tmp[k++] = nums[i++];
            } else {
                tmp[k++] = nums[j++];
            }
        }
        while(i <= mid) {
            tmp[k++] = nums[i++];
        }
        while(j <= r) {
            tmp[k++] = nums[j++];
        }

        for(k = l; k <= r; k++) {
            nums[k] = tmp[k-l];
        }
        return;
    }

4. 快速排序

(1)快速排序的本質是每次排序找到key所在的位置,key位置左邊的元素小於它,右邊的元素大於它;

(2)快速排序的partition函數可以通過多種方式實現,又分爲普通partition,二路partition,三路partition;

(3)快速排序對於基本有序的數組排序效果很差,一種優化方法是對key進行隨機化(見代碼);

(4)當數組中存在大量重複元素時,普通快速排序效果很差,插入排序效果好。這時應該使用三路快速排序,效果比較好

(5)同樣地,一種優化方法是當排序區間小於某個數時,使用插入排序。

//快速排序(二路)
    void quickSort(vector<int> &nums) {
        srand(time(NULL));
        __quickSort(nums, 0, nums.size() - 1);
    }

    void __quickSort(vector<int> &nums, int l, int r) {
        if(l >= r) {
            return;
        }
        int p = partition(nums, l, r);
        __quickSort(nums, l, p - 1);
        __quickSort(nums, p + 1, r);
    }

    int partition(vector<int> &nums, int l, int r) {
        // 隨機在arr[l...r]的範圍中, 選擇一個數值作爲標定點pivot
        swap(nums[l] , nums[rand()%(r-l+1)+l] );

        int key = nums[l];
        int i = l+1, j = r;
        while(true) {
            
            while(i <= r && nums[i] < key) {
                i++;
            }
            while(j >= l+1 && nums[j] > key) {
                j--;
            }
            if(i > j) {
                break;
            }
            swap(nums[i], nums[j]);
            i++;
            j--;
        }
        swap(nums[l], nums[j]);
        return j;
    }

 

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