排序算法第四講 --- 快速排序(Python、C++、C)

題目描述:

給你一個整數數組 nums,請你將該數組採用快速排序方式進行升序排列。

輸入示例: [1,8,6,2,5,4,9,3,7]
輸出示例: [1,2,3,4,5,6,7,8,9]


解題思路:

快速排序使用分治法來把一個串(list)分爲兩個子串(sub-lists)。具體算法描述如下:

  • 從數列中挑出一個元素,稱爲 “基準”(pivot);
  • 重新排序數列,所有元素比基準值小的擺放在基準前面,所有元素比基準值大的擺在基準的後面(相同的數可以到任一邊)。在這個分區退出之後,該基準就處於數列的中間位置。這個稱爲分區(partition)操作;
  • 遞歸地(recursive)把小於基準值元素的子數列和大於基準值元素的子數列排序。

栗子描述:

來源於:快速排序

  假設我們現在對 "6 1 2 7 9 3 4 5 10 8" 這個10個數進行排序。首先在這個序列中隨便找一個數作爲基準數。爲了方便,就讓第一個數6作爲基準數吧。接下來,需要將這個序列中所有比基準數大的數放在6的右邊,比基準數小的數放在6的左邊,類似3 1 2 5 4 6 9 7 10 8
這種排列。那要如何做到呢?

方法:
  分別從初始序列"6 1 2 7 9 3 4 5 10 8"兩端開始" 探測 "。先從右往左找一個小於6的數,再從左往右找一個大於6的數,然後交換他們。這裏可以用兩個變量 iijj,分別指向序列最左邊和最右邊。剛開始的時候讓ii指向序列的最左邊(即i=1i=1),指向數字6。讓哨兵jj指向序列的最右邊(即 j=10j=10),指向數字8。

  首先jj開始出動。因爲此處設置的基準數是最左邊的數,所以需要讓jj先出動,這一點非常重要(請自己想一想爲什麼)。jj一步一步地向左挪動(即jj--),直到找到一個小於6的數停下來。接下來ii再一步一步向右挪動(即i++i++),直到找到一個數大於6的數停下來。最後jj停在了數字5面前,ii 停在了數字7面前。

  現在交換iijj所指向的元素的值。交換之後的序列爲6 1 2 5 9 3 4 7 10 8

  到此,第一次交換結束。接下來開始jj繼續向左挪動。他發現了4(比基準數6要小,滿足要求)之後停了下來。ii也繼續向右挪動的,他發現了9(比基準數6要大,滿足要求)之後停了下來。此時再次進行交換,交換之後的序列爲6 1 2 5 4 3 9 7 10 8

  第二次交換結束,“探測"繼續。jj 繼續向左挪動,他發現了3(比基準數6要小,滿足要求)之後又停了下來。ii 繼續向右移動,糟啦!此時 iijj 相遇了,iijj 都走到3面前。說明此時” 探測 "結束。我們將基準數6和3進行交換。交換之後的序列爲3 1 2 5 4 6 9 7 10 8

  到此第一輪“探測”真正結束。此時以基準數6爲分界點,6左邊的數都小於等於6,6右邊的數都大於等於6。回顧一下剛纔的過程,其實jj的使命就是要找小於基準數的數,而ii的使命就是要找大於基準數的數,直到 iijj 碰頭爲止。

  OK,解釋完畢。現在基準數6已經歸位,它正好處在序列的第6位。此時我們已經將原來的序列,以6爲分界點拆分成了兩個序列,左邊的序列是“3 1 2 5 4”,右邊的序列是“9 7 10 8”。接下來還需要分別處理這兩個序列。因爲6左邊和右邊的序列目前都還是很混亂的。不過不要緊,我們已經掌握了方法,接下來只要模擬剛纔的方法分別處理6左邊和右邊的序列即可。現在先來處理6左邊的序列現吧。

  如果你模擬的沒有錯,調整完畢之後的序列的順序應該是2 1 3 5 4

最終得到正確的排序結果:1 2 3 4 5 6 7 8 9 10


代碼:

Python寫法:

class Solution(object):
    def solution(self,  nums):
        if len(nums) < 2:
            return nums
        else:
            tmp = nums[0]
            less = [num for num in nums[1:] if num <= tmp]
            big = [num for num in nums[1:] if num > tmp]
            return self.solution(less) + [tmp] + self.solution(big)

C++寫法:

#include<iostream>
using namespace std;

int a[100],n;  //定義全局變量

void quicksort(int left, int right) {
	int i, j, t, temp;
	if(left > right)
		return;
    temp = a[left];  //temp中存的就是基準數
    i = left;
    j = right;
    while(i != j) {  //順序很重要,要先從右邊開始找
    	while(a[j] >= temp && i < j)
    		j--;
    	while(a[i] <= temp && i < j) //再找右邊的
    		i++;       
    	if(i < j)    //交換兩個數在數組中的位置
    	{
    		t = a[i];
    		a[i] = a[j];
    		a[j] = t;
    	}
    }
    //最終將基準數歸位
    a[left] = a[i];
    a[i] = temp;
    quicksort(left, i-1);  //繼續處理左邊的,這裏是一個遞歸的過程
    quicksort(i+1, right); //繼續處理右邊的 ,這裏是一個遞歸的過程
}
int main() {
	int i;
    
	cin >> n;                //讀入數據

	for(i = 1; i <= n; i++)
		cin >> a[i];

    quicksort(1, n);         //快速排序調用
    
	
    for(i = 1; i <= n; i++)  //輸出排序後的結果
    	cout << a[i] <<" ";

    cout << "\n";
    return 0;
}

C語言:

#include <stdio.h>

int a[100],n;  //定義全局變量

void quicksort(int left, int right) {
	int i, j, t, temp;
	if(left > right)
		return;
    temp = a[left];  //temp中存的就是基準數
    i = left;
    j = right;
    while(i != j) {  //順序很重要,要先從右邊開始找
    	while(a[j] >= temp && i < j)
    		j--;
    	while(a[i] <= temp && i < j) //再找右邊的
    		i++;       
    	if(i < j)    //交換兩個數在數組中的位置
    	{
    		t = a[i];
    		a[i] = a[j];
    		a[j] = t;
    	}
    }
    //最終將基準數歸位
    a[left] = a[i];
    a[i] = temp;
    quicksort(left, i-1);  //繼續處理左邊的,這裏是一個遞歸的過程
    quicksort(i+1, right); //繼續處理右邊的 ,這裏是一個遞歸的過程
}
int main() {
	int i;
    //讀入數據
	scanf("%d", &n);

	for(i = 1; i <= n; i++)
		scanf("%d", &a[i]);
    quicksort(1, n);   //快速排序調用
    
	//輸出排序後的結果
    for(i = 1; i <= n; i++)
    	printf("%d ", a[i]);

    printf("\n");
    return 0;
}


題目來源:

http://lab.csdn.net/#/question/38?tagId=16

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