解析呼叫sort導致死迴圈問題的原因

2021-10-01 10:30:47 字數 3630 閱讀 7169

記錄乙個sort問題

2020.8.7 更新

某位某里的「同學」說我這個錯是自己寫程式的問題。你的自定義比較函式直接返回true會有偏序問題,他是這麼說的,如果你傳遞進a,b和傳遞進b,a都返回true的話,那麼就無法判斷a和b誰大,就會造成問題

。如果是在visual studio下debug模式編譯器會報錯。在他的「提示」下,我改用visual studio debug模式試了下。**和下面的一致。結果是不報錯。

環境

windows 10

visual studio community 2019 版本16.1.3

編譯截圖

windows下不會報錯,那麼在linux下呢?

環境

ubuntu 14.04.6 lts

gcc 4.8.5

編譯命令如下

g++ test.cc -o test -std=c++11 -ddebug
結果正常編譯

為了完全反駁他,可以把自定義比較函式改一下

bool cmp(const int &a, const int &b)

這樣就不會產生所說的偏序的問題,那麼這麼寫會不會產生問題?讓我們來試一下,編譯命令如下

g++ test.cc -o test -std=c++11 -ddebug
執行結果如圖

可見,仍然出現錯誤。至此,問題結束。

#include #include #include using namespace std;

bool cmp(const int &a, const int &b)

int main()

這段**很簡單,首先向vector裡插入了33個1(只要是重複元素就會出現這個問題),然後用sort函式,利用自定義比較函式對其進行排序,最後將排序的結果輸出。按道理來說,自定義排序函式如果一直返回true的結果是,不對vector內元素進行排序,但是編譯執行後的結果是程式一直處於執行狀態,遲遲沒有結果輸出。經測試,只要陣列元素大於16,就會出現這種情況。百思不得其解,後來檢視stl原始碼得到了答案。

從stl-sort原始碼開始分析。原始碼版本5.1.5。

template // 帶自定義比較函式的sort函式

void sort(_randomaccessiter __first, _randomaccessiter __last, _compare __comp)

}

自省排序:是一種混合排序方式,大部分情況下與median-of-3 quick sort排序演算法完全相同,但是當分割行為有惡化為二次行為傾向時,能能夠自我偵測,轉而改用heap sort,使效率維持在o(nlogn)。注:二次行為傾向,看過**後感覺就是快排的次數超過了設定的閾值,這個閾值由__lg()函式計算出,**下面有)。

用來控制分割閾值的情況,找出2^k <= n的最大值k返回

template inline _size __lg(_size __n)
template // 自省排序主流程

void __introsort_loop(_randomaccessiter __first,

_randomaccessiter __last, _tp*,

_size __depth_limit, _compare __comp)

--__depth_limit;

// 利用快排進行排序,並返回中樞節點

_randomaccessiter __cut =

__unguarded_partition(__first, __last,

_tp(__median(*__first,

*(__first + (__last - __first)/2),

*(__last - 1), __comp)),

__comp);

// 對右半段進行sort

__introsort_loop(__cut, __last, (_tp*) 0, __depth_limit, __comp);

__last = __cut;

// 現在回到while迴圈,對左半段進行sort

}}

先簡單說一下快排演算法。分割方法不只一種,以下敘述既簡單又有良好成效的做法。令頭端迭代器first向尾部移動, 尾端迭代器last向頭部移動。當*first大於或等千樞軸時就停下來, 當*last小於或等於樞軸時也停下來,然後檢驗兩個迭代器是否交錯。如果first仍然在左而last仍然在右, 就將兩者元素互換, 然後各自調整乙個位置(向**逼近),再繼續進行相同的行為。如果發現兩個迭代器交錯了(亦即!(first// 快排演算法,也是書裡所說的分割演算法

_randomaccessiter __unguarded_partition(_randomaccessiter __first,

_randomaccessiter __last,

_tp __pivot, _compare __comp)

--__last;

// last找到 <= pivot 的元素就停下來

while (__comp(__pivot, *__last))

// 交錯,結束迴圈

if (!(__first < __last))

return __first;

// 大小值交換

iter_swap(__first, __last);

++__first;

}}接著走完整個流程。當待排序資料變為區域性有序之後,就可以執行最後一步,呼叫插入排序來完成整個排序過程。

template void __final_insertion_sort(_randomaccessiter __first,

_randomaccessiter __last, _compare __comp)

else

__insertion_sort(__first, __last, _stlp_value_type(__first,_randomaccessiter), __comp);

}

從頭說一下流程。當我們呼叫sort進行排序的時候,首先會判斷元素個數,如果大於16,則先進行快排(分割演算法),等資料呈有序小塊的時候再呼叫插入排序進行合併。

在快排那裡,它首先要找到呼叫自定義比較函式返回false的那個資料(cmp(first,pivot)),如果返回true就一直向後找,注意這裡是沒有進行邊界檢查了,也就是說如果我們讓cmp函式一直返回true,程式就會一直讓first++,死在這裡。

sort函式自定義排序函式中,當兩個數相等時返回false。避免快排時比較兩個數大小越界的問題。

HashMap之鍊錶導致死迴圈

描述 hashmap採用拉鍊法 陣列鍊錶 解決hash衝突,因為是鍊錶結構,那麼就很容易形成閉合的鏈路。在單執行緒情況下,只有乙個執行緒對hashmap的資料結構進行操作,是不可能產生閉合的迴路的。那就只有在多執行緒併發的情況下才會出現這種情況,那就是在put操作的時候,如果size nitialc...

C C 分析陣列越界訪問導致死迴圈

閱讀下面 並分析導致其結果的原因 以下分析基於vs環境的除錯 include include intmain 擁有10個元素的整型陣列 for i 0 i 12 i 迴圈13次,越界訪問 system pause return0 分析 整型陣列arr有10個元素,for迴圈13次,導致陣列越界訪問。...

C語言中陣列越界導致死迴圈的測試

include 如何把這段 變成死迴圈,就像 c語言缺陷和陷阱 裡描述的那樣 經過測試發現,變數i的位址和a crazynum 的位址一樣,當執行 a crazynum 0 相當於i 0 所以死迴圈跑起來啦。測試環境 win7 32 dev c 4.9.9.2 c語言缺陷和陷阱 中的場景是編譯器按照...