【聊聊K路归并】

目录

先来看看二路合并(O(max{N, M}))

那么就可以利用二路归并实现K路归并了(O(N * K * logK))

优先队列优化

特殊地,我们有多路链表的归并

算法题案例,最经典的莫过于这一题(Kth smallest sum)


先来看看二路合并(O(max{N, M}))

#include <algorithm>
#include <vector>
#include <utility>
#include <ranges>
std::vector<int> merge(std::vector<std::pair<int, int>>& pairs){
//两路向量,用vector<pair>表示
	auto length { pairs.size() };
	std::vector<int> result {};
	result.reserve(length << 1 | 1);
	std::ranges::sort(pairs);
	auto i {0U};
	auto j {0U};
//这里压行了,就是普通的有序数组合并
	while(i < length || j < length){
		if(j >= length || (i < length
		&& pairs[i].first < pairs[j].second))
			result.emplace_back(pairs[i].first);
		else
			result.emplace_back(pairs[j].second);
	}
	return result;
}

那么就可以利用二路归并实现K路归并了(O(N * K * logK))

#include <algorithm>
#include <vector>
#include <utility>
#include <ranges>
//一样的二路归并
std::vector<int> merge(std::vector<int>& one, std::vector<int>& two){
	auto N { one.size() };
	auto M { two.size() };
	std::ranges::sort(one);
	std::ranges::sort(two);
	std::vector<int> result;
	result.reserve((N + M) << 1 | 1);
	auto i {0U};
	auto j {0U};
	while(i < N || j < M){
		if(j >= M || (i < N
		&& one[i] < two[j]))
			result.emplace_back(one[i]);
		else
			result.emplace_back(one[j]);
	}
	return result;
}
//利用二路归并实现K路归并
std::vector<int> KMerge(std::vector<std::vector<int>>& matrix){
	auto K { matrix.size() };
	auto i {0U};
	auto j {K - 1};
//DC
	while(!(K == 1)){
		while(i < j)
			matrix[i] = merge(matrix[i++], matrix[j--]);
//特判,K为奇数
		if(i == j)
			matrix[i-1] = matrix[i];
		K = (K + 1) >> 1U;
	}
	return matrix[0];
}

优先队列优化

#include <array>
#include <queue>
#include <algorithm>
#include <functional>
#include <cstdint>
#include <ranges>

constexpr std::size_t N { 64U };
constexpr std::size_t K { 32U };

auto merge(std::array<std::array<int32_t, N>, K>& matrix,
           std::array<int32_t, K> counter) {
	for (auto & row : matrix) std::ranges::sort(row, std::ranges::greater());
	std::priority_queue<std::pair<std::size_t, int32_t>> Q;
	for (auto i {0U}; i < K; ++i) Q.emplace(i, matrix[i][--counter[i]]);
	std::size_t cnt {};
	std::array<int32_t, N * K> result;
	while (!Q.empty()) {
		auto [row, val] = Q.top();
		Q.pop();
		result[cnt++] = val;
		if (counter[row]) Q.emplace(row, matrix[row][--counter[row]]);
	}
	return result;
}

特殊地,我们有多路链表的归并

#include <vector>
#include <limits>
#include <initializer_list>
#include <ranges>
#include <queue>
class integer_list{
	struct Node{
	 struct Node *__next__ {nullptr};
	 int         __value__ {0};
	} *dummy {new Node {__next__ : nullptr,
					   __value__ : std::numeric_limits<int>::min()}  };
public:
	void insert(int value){
		Node *U { new Node{ __next__ : dummy->__next__,
					       __value__ : value} };
		dummy->__next__ = U;
	}
	
	constexpr integer_list(void) noexcept = default;
	integer_list(std::initializer_list<int> li){
		for(auto integer : li | std::views::reverse) insert(integer);
	}
	static integer_list
	merge(std::vector<integer_list>& lists){
		std::priority_queue<std::pair<int, Node *>> Q;
		for(auto head : lists | std::views::transform([](auto list){ return list.dummy->__next__; } ) )
			if(head) Q.emplace(head->__value__, head);
		integer_list result;
		while(!Q.empty()){
			auto [value, pointer] = Q.top();
			Q.pop();
			result.insert(value);
			if(pointer->__next__)
				Q.emplace(pointer->__next__->__value__,
						  pointer->__next__);
		}
		return result;
	}
};

算法题案例,最经典的莫过于这一题(Kth smallest sum)

        给你一个 m * n 的矩阵 ,以及一个整数 k

你可以从每一行中选出 1 个元素形成一个数组

返回所有可能数组中的第 k 个 最小 数组和。

#include <array>
#include <queue>
#include <algorithm>
#include <functional>
#include <cstdint>
#include <ranges>
#include <cstdio>

constexpr int32_t N { 64U };
constexpr int32_t K { 32U };

std::array<std::array<int32_t, N>, K> matrix {};
std::array<int32_t, K> counter {};

void merge(int32_t source, int32_t mediate, int32_t destine){
	std::priority_queue<std::pair<int32_t, int32_t>> Q;
	for(int i {}; i < counter[mediate]; ++i)
		Q.emplace(i, matrix[source][0] + matrix[mediate][i]);
	for(int i {}; i < counter[destine]; ++i){
		auto [column, value] = Q.top();
		Q.pop();
		matrix[destine][i] = value;
		if(column)
			Q.emplace(--column, value - matrix[source][column] + matrix[mediate][column + 1]);
	}
}

int main(void){
	int32_t kk {};
	while(~std::scanf("%d", &kk) && kk){
		for(int i {}; i < kk; ++i){
			std::scanf("%d", &counter[i]);
			for(auto j {0}; j < counter[i]; ++j)
				std::scanf("%d", &matrix[i][j]);
			std::ranges::sort(matrix[i], std::ranges::greater());
		}
		for(int i {}; i < kk; ++i) merge(0, i, 0);
		for(int i {}; i < counter[0]; ++i)
			std::printf("%d%c", matrix[0][i], " \n"[i == counter[0] - 1]);
	}
	return 0;
}

  • 9
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

XNB's Not a Beginner

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值