今天写一个归并排序的模板,返回值为该序列的逆序对数
基本思路
归并排序就是利用二分的思想,将区间无限递归二分,直到当前划分区间只包含一个元素或没有元素的时候(我们认为这个序列是自动有序的),我们回溯到上一层,然后将当前层的左右两个区间合并为一个有序序列,然后继续回溯,回溯之后,当前层的左右两个区间都应该分别是已经经过合并的有序子区间,我们将这两个有序子区间再进行有序合并,再返回上一层,直到返回最大区间,则合并最大区间的左右有序子区间,得到有序序列。
流程演示
比如:22 3 1 5 4 7 9 1 8 0
红色区间划分:22 3 1 5 4
左边:22 3 右边 1 5 4
左边:合并有序:3 22
右边:5 4区间递归返回时候变为:4 5
右边合并:1 4 5
左右区间合并为一个区间:1 3 4 5 22
至此左侧区间处理完毕
右侧区间同理,得到有序序列:0 1 7 8 9
最后合并整个区间
0 1 1 3 4 5 7 8 9 22
逆序对数
我们利用归并的思想,它会将每个区间细分到最小,返回整合的时候,会进行左右区间合并,合并的时候就要比较左右区间当前值那个大,然后取小的那个,我们可以在比较的时候做记录,如果左侧的值小于右侧,我们就做记录,这样一路回溯,就会找到所有的逆序对数。
我们利用归并排序的返回值来将此记录值输出到外部
泛型代码:
template<typename value_type, typename value_Ptr> int merge_sort(const value_Ptr& begin, const value_Ptr& end) { static std::vector<value_type> to(end - begin); static int cnt{ 0 }; //记录逆序对数 if (end - begin <= 1)return 0; value_Ptr mid = begin + (end - begin) / 2; merge_sort<value_type>(begin, mid); merge_sort<value_type>(mid, end); //将上述两段区间顺序排列 value_Ptr l = begin, r = mid; int k{ 0 }, index{ 0 }; while (l < mid && r < end) if (*l < *r)to[k++] = *l++; else //如果左侧值小于右侧 { cnt += mid - l; //逆序对数记录 to[k++] = *r++; } while (l < mid)to[k++] = *l++; while (r < end)to[k++] = *r++; for (index = 0; begin + index < end; ++index)*(begin + index) = to[index]; return cnt; }
测试与使用
#include <iostream> #include <vector> using namespace std; int main() { ios::sync_with_stdio(false); int list[10]{ 22,3,1,5,4,7,9,1,8,0 }; vector<int> v{ list,list + 10 }; int cnt = merge_sort<int>(list + 0, list + 10); merge_sort<int>(v.begin(), v.end()); for (auto it : v)cout << it << " "; cout << endl; for (auto it : list)cout << it << " "; cout << endl; cout << "逆序对数:" << cnt << endl << endl; char list_[10]{ 'r','c','2','A','z','b','8','0','r', '3' }; vector<char>v_{ list_,list_ + 10 }; cnt = merge_sort<char>(list_ + 0, list_ + 10); merge_sort<char>(v_.begin(), v_.end()); for (auto it : v_)cout << it << " "; cout << endl; for (auto it : list_)cout << it << " "; cout << endl; cout << "逆序对数:" << cnt << endl << endl; }
时间复杂度为:O(N * log N)
感谢您的阅读,生活愉快~