記錄乙個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語言缺陷和陷阱 中的場景是編譯器按照...