剑指offer之partition算法
1 問題
?partition 算法:
從無序數組中選出樞軸點 pivot,然后通過一趟掃描,以 pivot 為分界線將數組中其他元素分為兩部分,使得左邊部分的數小于等于樞軸,右邊部分的數大于等于樞軸(左部分或者右部分都可能為空),最后返回樞軸在新的數組中的位置。
如果原始數組為[5,9,2,1,4,7,5,8,3,6],那么整個處理的過程如下圖
Partition 可不只用在快速排序中,還可以用于?Selection algorithm(在無序數組中尋找第K大的值)中.
?
?
?
?
?
?
2 代碼實現
我們按照算法需求簡單實現如下
void swap(int* a, int* b) {int temp = *a;*a = *b;*b = temp; }int partition(vector<int>& vector, int start, int end) {if (vector.size() < 1){std::cout << "vector is null or vector size is not normal" << std::endl;return -1;}//一般我們寫代碼不要這樣寫死,pivot = vector[0],如果遇到這種寫死數字的時候我們確認下是否是可以用變量更加合適int pivot = vector[start];int index = 0;for (int i = start + 1; i < end; ++i){if (vector[i] <= pivot){++index;swap(vector[index], vector[i]);}}swap(vector[0], vector[index]);return index; }?
?
?
?
?
?
?
3 優化
上面實現的效率很低,我們需要優化,如果我們考慮用2個指針的思想,保持頭尾兩個指針向中間掃描,每次在頭部找到大于pivot的值,同時在尾部找到小于pivot的值,然后將它們做一個交換
1)第一種優化代碼實現
#include <iostream> #include <vector>using namespace std;/**partition算法 記得如果這里是C++我們傳遞的是vector類型,我們記得要加引用,*不然改變不了數據,這里和java傳遞ArrayList不一樣,ArrayList作為參數可以改變集合里面的值,*所以C++如果函數傳遞非基本數據類型,一半都是帶引用的*/ int partitionOne(vector<int>& vector, int start, int end) {if (start > end){std::cout << "vector is empty or start > end" << std::endl;return -1;}int pivot = vector[start];while (start < end){//我們先從尾巴開始while (start < end && pivot <= vector[end]){--end;}//這里用的數組賦值,而不是直接用swap交換函數,那么下面的2步也是用數組賦值,而不是用swap交換函數vector[start] = vector[end];while (start < end && pivot >= vector[start]){++start;}vector[end] = vector[start];}vector[start] = pivot;return start; }void printVector(vector<int> v) {for (int i = 0; i < v.size(); ++i){std::cout << v[i] << "\t";}std::cout << std::endl; }int main() {vector<int> v1;//[5,9,2,1,4,7,5,8,3,6]v1.push_back(5);v1.push_back(9);v1.push_back(2);v1.push_back(1);v1.push_back(4);v1.push_back(7);v1.push_back(5);v1.push_back(8);v1.push_back(3);v1.push_back(6);std::cout << "old data print " << std::endl;printVector(v1);partitionOne(v1, 0, v1.size() - 1);std::cout << "after partitionOne" << std::endl;printVector(v1);return 0; }運行結果如下
old data print 5 9 2 1 4 7 5 8 3 6 after partitionOne 3 4 2 1 5 7 5 8 9 6然后圖解每一步如下
?
?
?
?
2)第二種優化代碼實現
我們使用交換函數,而不是數組賦值,和上面差不多,我們用swap函數的時候,我們單獨定義了2個變量i和j保存了start和end,這樣在后面的最后一個swap函數的時候進行swap(vector[i], vector[start]);還有這個函數是返回i,而不是start,不然就有問題。
#include <iostream> #include <vector>using namespace std;void swap(int* a, int* b) {int temp = *a;*a = *b;*b = temp; }void printVector(vector<int> v) {for (int i = 0; i < v.size(); ++i){std::cout << v[i] << "\t";}std::cout << std::endl; }/**partition算法 記得如果這里是C++我們傳遞的是vector類型,我們記得要加引用,*不然改變不了數據,這里和java傳遞ArrayList不一樣,ArrayList作為參數可以改變集合里面的值,*所以C++如果函數傳遞非基本數據類型,一半都是帶引用的*/ int partitionTwo(vector<int>& vector, int start, int end) {if (start > end){return -1;}int i = start;int j = end;int pivot = vector[start];while (i < j){//我們先從尾巴開始while (i < j && pivot <= vector[j]){--j;}//這里用的數組賦值,而不是直接用swap交換函數,那么下面的2步也是用數組賦值,而不是用swap交換函數while (i < j && pivot >= vector[i]){++i;}swap(vector[i], vector[j]);}swap(vector[i], vector[start]);//printVector(vector);return i; }int main() {vector<int> v1;//[5,9,2,1,4,7,5,8,3,6]v1.push_back(5);v1.push_back(9);v1.push_back(2);v1.push_back(1);v1.push_back(4);v1.push_back(7);v1.push_back(5);v1.push_back(8);v1.push_back(3);v1.push_back(6);std::cout << "old data print " << std::endl;printVector(v1);partitionTwo(v1, 0, v1.size() - 1);std::cout << "after partitionOne" << std::endl;printVector(v1);return 0; }運行結果如下
old data print 5 9 2 1 4 7 5 8 3 6 after partitionOne 4 3 2 1 5 7 5 8 9 6?
?
?
?
4 總結
我們使用partition算法的時候,從我們上面代碼第一次調用來看,我們選擇的第一個數字5作為中間軸,然后執行一次后,我們的?partition函數返回的start或者i值都是4,然后我們最后一步把5也插入了vector[4]那里,就是說明我們左邊有4個值比當前數字5作為中間軸都小,也能說明這左邊的4個值和中間軸數5都是數組里面最小的5個值,如果我們需要求出一個數組里面最小的5個值,我們只需要partition算法返回值是4就行,然后在左邊的數組的前5個數字就是這個數組里面最小的5個數,所以這里的數組里面最小的多少K個數確保partition返回的index或者start的關系是:index = K - 1; 或者start = K -1關系,也就是說partition函數返回index或者start值的時候,數組里面從坐標0到index或者start的值就是數組里面最小的元素,也就是index+1個元素。
?
我們使用partition算法是雙指針思想
總結
以上是生活随笔為你收集整理的剑指offer之partition算法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux shell之删除当前文件夹不
- 下一篇: 剑指offer之快速排序