開發中用到的一個快速排序法

實際上在,作web開發,比較少遇到使用一些算法之類的,畢竟不是作搜索引擎,也不是寫底層(好比寫個相似於mysql這樣的數據庫,裏面須要本身實現排序算法),另外,每種語言,好比java,php都或多或少已經封裝好排序函數給程序員使用。好比有個共識,你們作web開發的基本都明白,業務邏輯多比較簡單,不是很複雜的業務邏輯。咱們做爲web開發的程序員,基本是是web架構,對數據庫增刪查改數據,而後把數據展現在頁面中,大多就是涉及性能優化,緩存等等。php

 

學學一些常見的算法,對於實現特殊的應用仍是有幫助的。好比有些時候咱們依賴於數據庫中order by來實現排序了,因此很是習慣直接接下交給數據庫實現排序了。java

接下來,我就遇到須要本身實現排序了。mysql

 

由於咱們在實際開發中,遇到一個問題,徹底須要我本身實現排序。需求以下:程序員

 

在商品表裏面,有一個字段是goods_price(商品價格),如今要開發一個促銷價功能。促銷價有個時間範圍設置。在前臺頁面中,展現商品的時候。若是當前時間符合促銷時間。就要按照促銷價格執行。因而促銷價就單獨增長了一個字段來保存,叫作promote_price,促銷時間配置信息好比什麼時間,天天幾點到幾點之類的時間設置信息暫時無論,存儲在其餘字段中的,展現的時候,要用當前時間跟配置的時間進行比較。

單條商品展現的時候,就直接判斷是否在促銷時間內便可了。沒遇到排序的問題。web

 

而是在作商品列表頁面的時候,一個這樣的小細節就讓我發現需求:用戶能夠選擇商品價格按照"從高到低"也能夠選擇"從低到高"排序。

若是是單純排序,以往是直接交給數據庫去排序,通常咱們習慣了sql中使用"order by goods_price DESC"之類的語句就能實現按照價格降序仍是升序進行。算法

 

如今,不能簡單就按照goods_price(商品價格)排序就ok。好比當前時間有的商品是符合促銷時間的,那麼促銷價也是要做爲排序的。sql

 

簡單的 order by goods_price DESC,promote_price DESC 這種作法的話徹底是不對路如今的需求。數據庫

 

因此呢,須要先對交給數據庫的order by goods_price DESC 排序一次,列出數據。
而後遍歷,看哪些商品數據是符合促銷價格的。而後本身編寫代碼實現排序。
 

我初期想法是:拿到當前頁的數據,裏面判斷每行是否符合促銷價時間點數組

foreach(通過數據庫按照價格字段排序的結果)
{
if ($v['promote_price'] > 0 && $promote_class->promtoe_validate($food_info)) {
            $v['is_promote'] = true;             $v['price']= $v['promote_price'];//將原價改成促銷價顯示
        }緩存

}


//對上面的列表,由於上面的列表通過mysql排序一次後,還通過了促銷價。因此還須要再次編寫一個排序算法排序一次。這樣就能夠把促銷價低的放到前面去了

 
 
 

其實,mysql數據庫就是用c語言編寫的。我理解數據庫order by,它的排序也就是用c語言實現對數組的排序(關係表裏面返回的的行列表就是一個二維數組)

 

只是,平時咱們排序是交給數據庫去實現了。不多本身編寫,因此由於接觸很少,就覺得這些算法本身用不上,如今仍然須要用php語言對數據去實現排序。

 

數據庫中的 order by a DESC,b ASC  的實現原理猜想?
 
第一種理解:先按照a字段進行排序。而後又對數據按照b字段進行排序。
第二種理解:先按照a字段進行排序 ,若是遇到兩個值相同的,沒法肯定誰在前在後時,則使用b asc來肯定兩個數據的前後順序。
 
我是第一種理解,後來糾正,第二種理解纔是對符合對的,由於這才比較符合設計的考慮點:

爲何要設計能夠多個字段進行排序?難道是爲了相互覆蓋掉嗎?好比先按照a字段排序了。某兩項數據原本是一個在前一個在後,若是又按照b asc進行排序,那麼可能原來這兩項數據的順序就可能錯位,就是可能致使後面的排序規則應用後的結果覆蓋前面的。

 

 

假設數據庫排序是這樣子設計的話就沒實際意義了。之因此設計多個字段進行排序。就是爲了解決,遇到兩行中a字段的值都2,2的時候,怎麼肯定前後?這個時候就調用後面的排序規則對這兩項數據排序。因此order by 後面的字段前後順序不一樣形成的效果是不一樣的。

現實生活例子:假設要排名100個學生的英語成績,假設排序的時候,遇到三個學生都是88分。誰排名在前呢?這個時候能夠附加一種新的排序方式,對這三個學生看他們的品行分排序。這樣子就好肯定了。

 

 

 

 

網上的快速排序法,實現都是針對一維數組來實現的。如今我要模擬數據庫中的行,也就是二維數組做爲參數,而且能夠指定任意字段做爲排序方式。

好比從數據庫中查詢出一個數據列表,原封不動的對這個列表能夠指定某個字段進行排序(數據庫就是實現這個需求吧。固然他們要先進得些。人家牛逼些 呵呵。

 

具體,看下面

 

/*

 * 排序:此函數是一個通用函數,只要是二維數組的排序均可以調用。初衷是解決價格快速排序(涉及到促銷價,沒法使用order by解決)

 * +--------------------------------------------------------------------------

 * @param $arr 要排序的數組,二維數組。對應就是數據庫中的多行數據 array(

 * 0=>array("字段1"=>'','字段2'=>''...)

 * 1=>array("字段1"=>'','字段2'=>''...)

 * 2=>array("字段1"=>'','字段2'=>''...)

 * )

 * +--------------------------------------------------------------------------

 * @param $key_field 按照哪一個字段進行排序,不要傳入一個並不存在的字段。會打亂原來的順序

 * +--------------------------------------------------------------------------

 * @param $sort_type = asc or desc  排序方式。從小大到大,仍是從大到小

 */

 

function quickSort($arr, $key_field, $sort_type = "asc") {

    if (count($arr) > 1) {

        //使用哪一個字段排序,先獲得該字段全部數據,目的是轉換成一維數組進行排序

        $key_value_arr = array();

        $return_arr = array();

 

        //先判斷排序的字段是否存在

 

 

        foreach ($arr as $k => $v) {

            $key_value_arr[$k] = $v[$key_field]; //獲得這個字段的值

        }

 

        //php內置函數實現了按降序仍是升序排,可是隻支持一維數組

        if ($sort_type == 'desc') {

            arsort($key_value_arr);

        } else {

            asort($key_value_arr);

        }

        reset($key_value_arr);

        foreach ($key_value_arr as $k => $v) {

            $return_arr[$k] = $arr[$k]; //獲得行

        }

 

        return $return_arr;

    } else {

        return $arr;

    }

}

 

---------------------------------------------------------------------------

總結一下我對快速排序法的理解

--------------------------------------------------------------------------

 

假設有100個元素,對此進行排序。那麼須要遍歷多少次呢?仍然須要遍歷至少100次。由於確實都免不了,逐個去掃描每一個元素,丟到左邊,仍是右邊。當第一次分割以後。還要繼續對分割後兩邊的進行重複這一步驟。

 

當元素數量小的時候,是體會不到區別的。若是數量很大,達到上萬個元素。須要進行排序,則須要涉及到算法了

 

好比比較高矮,現實中狀況,咱們人能夠用眼睛來看,哪一個更小,而後認爲的排序出來。可是計算機則不一樣。咱們必須編寫程序來告訴它要什麼樣的方法實現。

快速排序體現的思想是:分治法。分割成小塊,逐個解決。

 

大致的思路描述:

一、從一堆數據裏面找到一個基準的數據。按照這個數據標準分割開來。現實例子,一堆人100我的,比較高矮。如今我找出一個高度的人,我按照這我的的身高,分紅a,b兩組。比他矮的都站到a組,比他高的都站到b(跟他同樣高的隨便放哪一邊均可以),這樣子可將100我的分割成兩組人。

 

結果是,a組裏面的全部人身高都要<=b組裏面的人。

 

二、對a組裏面的人重複第一步。對b組裏面的人也重複第一步。

 

三、直到最後只剩下一個(由於已經無法在繼續切割了),才分組。

 

 

 

我學到一個思想:先切成大塊,而後對每一個大塊單獨處理。最後把各個塊的處理結果都合併起來。

 

function  quickSort( $arr ) {
   if ( count ( $arr ) > 1) {
     $k = $arr [0];
     $x = array ();
     $y = array ();
     $_size = count ( $arr );    
     for ( $i =1; $i < $_size ; $i ++) {
       if ( $arr [ $i ] <= $k ) {
         $x [] = $arr [ $i ];//小的放這邊
       } else {
         $y [] = $arr [ $i ];//大的放這邊。這樣子是從小到大排序,若是想從大到小返回,那麼調換位置與$x[] =$arr[$i];的位置便可
       }
     }
     //獲得分割看來左右兩邊的數據
     $x = quickSort( $x );//左邊的數據,對這些數據再次使用分割法排序,返回的結果就是排序後的數據
     $y = quickSort( $y );//右邊的數據
     return array_merge ( $x , array ( $k ), $y );
   } else {
     return $arr ;
   }

}

 

不正確之處,歡迎指正!

 

 

代碼備份:

複製代碼

<?php

//大致思路:因爲是二維數組。因此先獲得指定key的全部值。也就是轉換爲一維數組了。

/*
不過這個一維數組的key要使用二維數組的key。這樣子一維數組排序後,方便對應到二維數組中去。就是靠這個key。


一維數組以下:
array('1'=>'a','4'=>''b','3'=>'c','5'=>'d');

1,2,4這些key值,到時候就是對應到裏面去的證據


思考,若是還要加一個條件呢好比像sql那樣子的:order by a,b,c
當a字段的值都相等的狀況下,就啓用b字段進行排序。若是仍是相等,則啓用c字段進行排序。

*/




/*
$keys = array();

$keys['gg'] = '8.9';
$keys[1] = '8.8';

$keys[5] = '7.5';




asort($keys);//排序有個特色,原來的key值不會改變的。只是把位置換一下。我以前覺得是調換了key值。這樣子,0,1,2,3,4

reset($keys);
var_dump($keys);

*/




/*
 * +-------------------------------------------------------
 * 快速排序
 * @author wangtao 2015.6.10
 * +-------------------------------------------------------
 * @param $arr 要排序的數組,二維數組。對應就是數據庫中的多行數據
  array(
 * 0=>array("字段1"=>'','字段2'=>''...)
 * 1=>array("字段1"=>'','字段2'=>''...)
 * 2=>array("字段1"=>'','字段2'=>''...)
 * )
 * @param $key_field 按照哪一個字段進行排序
 * @param $sort_type = asc or desc  排序方式。從小大到大,仍是從大到小
 * +-------------------------------------------------------
 * return 按照指定排序後的一個新數組。原來的key仍然會保留
 * 如:1=>array("字段1"=>'','字段2'=>''...),2=>array("字段1"=>'','字段2'=>''...)  
 * 按照"字段2"排序後,key爲2元素可能在前面前面了,可是key值不會被修改,會原樣保留
 * +-------------------------------------------------------
 */

function quick_sort($arr, $key_field, $sort_type = "asc") {
    if (count($arr) > 1) {
        //使用哪一個字段排序,先獲得該字段全部數據,目的是轉換成一維數組進行排序
        $key_value_arr = array();
        $return_arr = array();
        //先判斷排序的字段是否存在,若是字段根本不存在,避免打亂原來數組的順序

    
        foreach ($arr as $k => $v) {
            @ $key_value_arr[$k] = $v[$key_field]; //獲得這個字段的值

        }

        //php內置函數實現了按降序仍是升序排,可是隻支持一維數組
        if ($sort_type == 'desc') {
            arsort($key_value_arr);
        } else {
            asort($key_value_arr);
        }


        reset($key_value_arr);

        foreach ($key_value_arr as $k => $v) {
            $return_arr[$k] = $arr[$k]; //獲得行
        }
        //var_dump($return_arr);
        return $return_arr;
    } else {
        return $arr;
    }
}

$array = array(
array('name'=>'手機','brand'=>'諾基亞','price'=>1050),
array('name'=>'筆記本電腦','brand'=>'lenovo','price'=>4300),
array('name'=>'剃鬚刀','brand'=>'飛利浦','price'=>3100),
array('name'=>'跑步機','brand'=>'三和松石','price'=>4900),
array('name'=>'手錶','brand'=>'卡西歐','price'=>960),
array('name'=>'液晶電視','brand'=>'索尼','price'=>6299),
array('name'=>'激光打印機','brand'=>'惠普','price'=>1200),
array('name'=>'手機','brand'=>'諾基亞','price'=>1050),
);

var_dump(quickSort($array,'m'));





//看對一個數組裏面元素值都爲空的怎麼排序
$row = array(
0=>null,
1=>null,
2=>null,
3=>null,
);
asort($row);
var_dump($row);//若是爲空。則根據key值倒過來?

/*返回的是array
  3 => null
  2 => null
  1 => null
 0 => null
如今終於明白了,數據庫字段中是否保持null,對於排序是有影響的。結果就會影響展現效果。
*/

複製代碼

相關文章
相關標籤/搜索