堆排序

概念

堆是按照一定規(guī)則順序存儲的完全二叉樹(二叉樹是每個節(jié)點最多有兩個子樹的樹結(jié)構(gòu))箕戳,其中可以分為大根堆和小根堆耗溜。

  • 大根堆: 每個父結(jié)點的關(guān)鍵字都大于其子節(jié)點的關(guān)鍵字(如果有子結(jié)點的話)
  • 小根堆: 每個父結(jié)點的關(guān)鍵字都小于其子節(jié)點的關(guān)鍵字(如果有子結(jié)點的話)

舉例來說奋刽,有個數(shù)組[A0, A1, A2, A3...An]假設(shè)父結(jié)點為Ai(其中i=1,2,…,n/2向下取整), 由二叉樹的排列規(guī)律可知其左子結(jié)點為A(2 * i + 1), 右子結(jié)點為A(2 * i + 2)因此大根堆必須滿足:Ai >= A(2 * i + 1)且Ai >= A(2 * i + 2), 小根堆必須滿足Ai <= A(2 * i + 1)且Ai <= A(2 * i + 2)

假如有個數(shù)列[3, 8, 15, 31, 25]砌溺,以二叉樹的形式表示如下圖:


318837-20160422104522335-1248911478.png

首先需要按照堆的規(guī)則去構(gòu)造初始堆(即構(gòu)建一個完全二叉樹阁将,保證所有的父結(jié)點都比它的孩子結(jié)點數(shù)值大),構(gòu)造規(guī)則為不同行從下往上侮措、相同行從右往左分別比較每一個子二叉樹的結(jié)點懈叹,如果父結(jié)點的關(guān)鍵字不是最大值,讓子節(jié)點中最大的值與父節(jié)點進行交換分扎。如此遍歷一遍后可以得到一個初始堆澄成。
注意: 當(dāng)?shù)谝患壎鏄渥咏Y(jié)點值大于父結(jié)點的值進行交換后,交換后新的子結(jié)點作為父結(jié)點的第二級子二叉樹中如果依然不是最大值則需要再次與其最大子結(jié)點進行交換畏吓。例:有個數(shù)組[5,3,1,8,7]在調(diào)整后變?yōu)閇5,8,1,3,7]->[8,5,1,3,7]墨状,然而此時由于在(5,3,7)這個子二叉樹中父結(jié)點5不是最大值,因此需要再次向下比較一次進行交換

在初始堆的基礎(chǔ)上交換數(shù)組中被調(diào)整的首尾元素菲饼,并讓數(shù)組重新調(diào)整為大根堆(這次調(diào)整忽略移到末尾的元素),采用遞歸的方式就可以依次把所有元素按升序排列到數(shù)組中了肾砂。

OC語言實例

給可變數(shù)組創(chuàng)建一個分類來存放該堆排序方法。

- (NSMutableArray *)heapSort
{
    // 判斷是不是NSNumber的數(shù)組
    BOOL isFullNSNumber = YES;
    for (id value in self) {
        if (![value isKindOfClass:[NSNumber class]]) {
            isFullNSNumber = NO;
        }
    }
    
    NSAssert(isFullNSNumber, @"數(shù)組中應(yīng)當(dāng)全是NSNumber對象");
    
    // 初始化一個終止計算結(jié)點索引位置(一次排序后把最大值移動到數(shù)組計算結(jié)點范圍的末尾)
    int endIndex = (int)(self.count - 1);
    while (endIndex > 0) {
        // 遍歷一次數(shù)組按照堆的規(guī)則排序一次
        [self binaryTreeSortWithArray:self maxIndex:endIndex];
        // 將建立堆得到的最大根與數(shù)組中最后一個數(shù)組互換
        [self exchangeObjectAtIndex:0 withObjectAtIndex:endIndex--];
    }
    
    return self;
}
/// 二叉樹規(guī)則(大根堆)排序 -- (排序范圍為索引為0的位置到待排序結(jié)點最大索引位置)
- (void)binaryTreeSortWithArray:(NSMutableArray *)array maxIndex:(int)maxIndex
{
    // 從可排序結(jié)點的最大索引處開始進行排序
    for (int i = maxIndex; i >= 0; i--) {
        // 獲取移動后的子結(jié)點的索引
        int newIndex = [self compareNodesSizeWithMaxIndex:maxIndex fatherNodeIndex:i];
        while (newIndex > 0) {
            newIndex = [self compareNodesSizeWithMaxIndex:maxIndex fatherNodeIndex:newIndex];
        }
    }
}
/// 遞歸比較指定索引位置的結(jié)點與其左右子節(jié)點的大小
- (int)compareNodesSizeWithMaxIndex:(int)maxIndex fatherNodeIndex:(int)fatherIndex
{
    // 獲取當(dāng)前索引位置的左右子節(jié)點索引
    int leftIndex = fatherIndex * 2 + 1;
    int rightIndex = fatherIndex * 2 + 2;
    
    // 該索引和左右子節(jié)點值
    NSNumber *currentValue = [self objectAtIndex:fatherIndex];
    NSNumber *leftValue = nil;
    NSNumber *rightValue = nil;
    if (leftIndex <= maxIndex) {
        leftValue = [self objectAtIndex:leftIndex];
    }
    if (rightIndex <= maxIndex) {
        rightValue = [self objectAtIndex:rightIndex];
    }
    
    if (rightValue != nil) // 右子節(jié)點存在, 則左子節(jié)點必定存在, 判斷三者的值并把最大值換到父節(jié)點上
    {
        if (currentValue.doubleValue > leftValue.doubleValue && currentValue.doubleValue > rightValue.doubleValue) {
            return -1;
        }else {
            // 與父節(jié)點進行交換的結(jié)點的索引
            int index = (leftValue.doubleValue > rightValue.doubleValue ? leftIndex : rightIndex);
            [self exchangeObjectAtIndex:fatherIndex withObjectAtIndex: index];
            // 移動完畢之后同時還要進行(被移動到子節(jié)點的值)與(該被移動節(jié)點的左右子節(jié)點值)進行比較 -- 遞歸
            return index;
        }
    }
    else if (leftValue != nil) // 右結(jié)點不存在, 左結(jié)點存在, 判斷左結(jié)點是否大于父結(jié)點的值
    {
        if (currentValue.doubleValue < leftValue.doubleValue) {
            [self exchangeObjectAtIndex:fatherIndex withObjectAtIndex:leftIndex];
            return leftIndex;
        }else {
            return -1;
        }
    }
    
    return -1;
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末宏悦,一起剝皮案震驚了整個濱河市镐确,隨后出現(xiàn)的幾起案子包吝,更是在濱河造成了極大的恐慌,老刑警劉巖源葫,帶你破解...
    沈念sama閱讀 218,546評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件漏策,死亡現(xiàn)場離奇詭異,居然都是意外死亡臼氨,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,224評論 3 395
  • 文/潘曉璐 我一進店門芭届,熙熙樓的掌柜王于貴愁眉苦臉地迎上來储矩,“玉大人,你說我怎么就攤上這事褂乍〕炙恚” “怎么了?”我有些...
    開封第一講書人閱讀 164,911評論 0 354
  • 文/不壞的土叔 我叫張陵逃片,是天一觀的道長屡拨。 經(jīng)常有香客問我,道長褥实,這世上最難降的妖魔是什么呀狼? 我笑而不...
    開封第一講書人閱讀 58,737評論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮损离,結(jié)果婚禮上哥艇,老公的妹妹穿的比我還像新娘。我一直安慰自己僻澎,他們只是感情好貌踏,可當(dāng)我...
    茶點故事閱讀 67,753評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著窟勃,像睡著了一般祖乳。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上秉氧,一...
    開封第一講書人閱讀 51,598評論 1 305
  • 那天眷昆,我揣著相機與錄音,去河邊找鬼汁咏。 笑死隙赁,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的梆暖。 我是一名探鬼主播伞访,決...
    沈念sama閱讀 40,338評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼轰驳!你這毒婦竟也來了厚掷?” 一聲冷哼從身側(cè)響起弟灼,我...
    開封第一講書人閱讀 39,249評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎冒黑,沒想到半個月后田绑,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,696評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡抡爹,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,888評論 3 336
  • 正文 我和宋清朗相戀三年掩驱,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片冬竟。...
    茶點故事閱讀 40,013評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡欧穴,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出泵殴,到底是詐尸還是另有隱情涮帘,我是刑警寧澤,帶...
    沈念sama閱讀 35,731評論 5 346
  • 正文 年R本政府宣布笑诅,位于F島的核電站调缨,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏吆你。R本人自食惡果不足惜弦叶,卻給世界環(huán)境...
    茶點故事閱讀 41,348評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望妇多。 院中可真熱鬧湾蔓,春花似錦、人聲如沸砌梆。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,929評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽咸包。三九已至桃序,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間烂瘫,已是汗流浹背媒熊。 一陣腳步聲響...
    開封第一講書人閱讀 33,048評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留坟比,地道東北人芦鳍。 一個月前我還...
    沈念sama閱讀 48,203評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像葛账,于是被迫代替她去往敵國和親柠衅。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,960評論 2 355

推薦閱讀更多精彩內(nèi)容