算法分析与设计 - 作业4

问题一

有一个序列是某个有序系列围绕着下标为 K 的元素(0klength)旋转得到的序列,使数组下标变为 [k],[k+1],,[n1],[0],[1],,[k1],如 123456 围绕着下标为 3 的元素旋转得到 456123,请为此序列编写元素查找算法,并分析你的算法性能。

发现序列以位置 k 为界,前后分别为有序状态。在有序部分内可以进行二分查找,于是考虑对于要查询的位置,首先判断它位于 1k 中还是位于 k+1n 中,然后在对应的有序部分内进行二分查找即可。

空间复杂度 O(n) 级别,单次查询即为二分查找的时间复杂度,为 O(logn) 级别。

复制
//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
const int kN = 1e5 + 10;
//=============================================================
int a[kN];
//=============================================================
//=============================================================
int main() {
//freopen("1.txt", "r", stdin);
// std::ios::sync_with_stdio(0), std::cin.tie(0);
int n, k;
std::cin >> n >> k;
for (int i = 1, j = k + 1; i <= k; ++ i, ++ j) a[i] = j;
for (int i = k + 1, j = 1; i <= n; ++ i, ++ j) a[i] = j;
int q; std::cin >> q;
while (q --) {
int x, p = -1; std::cin >> x;
if (x <= k) p = std::lower_bound(a + k + 1, a + n + 1, x) - a;
else if (x <= n) p = std::lower_bound(a + 1, a + k + 1, x) - a;
else p = -1;
if ((x <= k && p == n + 1) || (k < x && x <= n && p == k + 1)) p = -1;
std::cout << p << "\n";
}
return 0;
}

问题二

我们学习的二分查找算法是针对一维有序序列的,现假设有一个矩阵,其每一行每一列分别是从左到右、从上到下有序的,请为此矩阵编写元素查找算法,并分析你的算法性能。

手玩一下可知矩阵的形态类似于:

5
1 2 3 4 5
2 3 4 5 6
3 4 5 6 7
4 5 6 7 8
5 6 7 8 9

则某种元素的出现次数可能为 O(n2) 级别,且出现位置可能并不连续,估计很难找到一个小于 O(n) 的解法。

解法一

发现每一行都是有序的,则在每一行若有待查询元素,则它们一定构成一段连续的区间。于是考虑对每一行运行二分查找算法,找到待查询元素出现的区间即可。

并不需要额外的数据结构维护,空间复杂度 O(n2) 级别,时间复杂度 O(nlogn) 级别。

//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
const int kN = 2000 + 10;
//=============================================================
int a[kN][kN];
//=============================================================
//=============================================================
int main() {
//freopen("1.txt", "r", stdin);
// std::ios::sync_with_stdio(0), std::cin.tie(0);
int n; std::cin >> n;
for (int i = 1; i <= n; ++ i) {
for (int j = 1; j <= n; ++ j) {
std::cin >> a[i][j];
}
}
int q; std::cin >> q;
while (q --) {
int x; std::cin >> x;
for (int i = 1; i <= n; ++ i) {
int l = std::lower_bound(a[i] + 1, a[i] + n + 1, x) - a[i];
int r = std::upper_bound(a[i] + 1, a[i] + n + 1, x) - a[i] - 1;
if (l <= r) std::cout << "line " << i << " : [" << l << ", " << r << "] is element " << x <<".\n";
else std::cout << "line " << i << " has no element " << x << "!\n";
}
}
return 0;
}

解法二

解法一没有使用每一列也为有序这一性质,这可不行!

通过观察可知,若每一列也为有序,则对于某元素 x,其在第 i 行与第 i+1 行中出现的位置 [li,ri],[li+1,ri+1] 一定满足:

li+1liri+1ri

即有:

(li+1li)(ri+1ri)

发现数列 l1ln 和数列 r1rn 均为非递减数列,分别代表第 i 行元素 x 最靠左和最靠右出现的位置。考虑通过求得这两个数列来确定待查询元素的位置。

以求得 l1ln 为例,考虑倒序枚举每一行 i(1in),在此过程中维护一个指针 p 指向当前行 x 出现的最靠左的位置:

  • i=n 时,初始化 p=0;否则当 1i<n 时初始化 p=li+1
  • 然后不断右移指针直至 ai,px,则若第 i 行存在元素 x,则 li=p

求数列 r1rn 同理,仅需维护指针 p 指向该行元素 x 最靠右的满足 ai,px 的元素,并不断右移即可。

按照上述算法求得上述两数列后,若某行满足 liri,说明该行存在元素 x,且所有元素 x 构成区间 [li,ri],否则不存在元素 x

空间复杂度 O(n) 级别。发现上述过程中指针 p 在倒序枚举每行时是单调不降的,则右移操作至多会进行 O(n) 次,又仅会枚举 O(n) 行,则枚举次数为 O(n) 级别。

//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
const int kN = 2000 + 10;
//=============================================================
int a[kN][kN], l[kN], r[kN];
//=============================================================
//=============================================================
int main() {
// freopen("1.txt", "r", stdin);
// std::ios::sync_with_stdio(0), std::cin.tie(0);
int n; std::cin >> n;
for (int i = 1; i <= n; ++ i) {
for (int j = 1; j <= n; ++ j) {
std::cin >> a[i][j];
}
}
int q; std::cin >> q;
while (q --) {
int x; std::cin >> x;
for (int i = n; i >= 1; -- i) {
int pl = (i == n) ? 0 : l[i + 1];
while (pl <= n && a[i][pl] < x) ++ pl;
l[i] = pl;
int pr = (i == n) ? 0 : r[i + 1];
while (pr < n && a[i][pr + 1] <= x) ++ pr;
r[i] = pr;
}
for (int i = 1; i <= n; ++ i) {
if (l[i] <= r[i]) std::cout << "line " << i << " : [" << l[i] << ", " << r[i] << "] is element " << x <<".\n";
else std::cout << "line " << i << " has no element " << x << "!\n";
}
}
return 0;
}

写在最后

问题二中解法二的时间复杂度分析是一种均摊时间复杂度分析。

posted @   Rainycolor  阅读(49)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示