[数据结构习题]线性表——未出现的最小正整数
👉知识点导航💎:【数据结构】线性表——顺序存储
👉[王道数据结构]习题导航💎: p a g e 19.13 page19.13 page19.13
本节为线性表的习题 |
题目描述:
🎇思路:补空法
🔱思路分析:
我们先对题目进行分析,题目要求得到一个在时间上尽可能高效的算法找到数组中未出现的最小正整数,于是,我们采取空间换时间的策略,之前我们做过的题都是尽可能将空间复杂度压缩为常数级,而这里选择让出空间,我们则可以构造一个辅助数组b[n]
✨我们手算的想法是:从1开始不断增大,一一检查,最先发现没在数组中的元素就是最小正整数
所以,我们可以得到:
对于小于等于0的数可以直接排除,对于大于0的数,我们一一检查从1开始的正整数,判断是否在数组中出现
✨算法思路:这就是为什么要构造辅助数组的原因,我们换一种思路,把一一检查数组中是否存在该正整数换成遍历数组,每遇到一个正整数就将它放入辅助数组对应的空中,最后遍历辅助数组,最先遇到的空即为最小正整数
😍 我们再来看一个重要性质:
最小正整数一定在 [ 1 , n + 1 ] [1,n+1] [1,n+1]之间
🍰def:
- 最特殊的情况就是
[
1
,
n
]
[1,n]
[1,n]之间没有空缺,原数组的元素含有
[
1
,
n
]
[1,n]
[1,n]的所有数,这时候遍历辅助数组时,发现
[
1
,
n
]
[1,n]
[1,n]之间的数 都被填满了,无法插入
[
1
,
n
]
[1,n]
[1,n]之间的空,因此,最后落到
1
+
n
1+n
1+n上
最小正整数 x = n + 1 x=n+1 x=n+1
- 一般的情况就是
[
1
,
n
]
[1,n]
[1,n]之间的数存在空缺
最小正整数 x ∈ [ 1 , n ] x∈[1,n] x∈[1,n]
🎁综上所述,最小正整数一定是落在 [ 1 , n ] [1,n] [1,n]上的,因此,我们构造辅助数组长度为n,也就是存放正整数 [ 1 , n ] [1,n] [1,n],所以<=0 / >n 的数都可以舍去
🍰algorithm step:
- 分配一个内存空间存放 b [ n + 1 ] b[n+1] b[n+1](让下标与值对齐),用来记录a中是否出现了1~n的正整数
- 第一次遍历:
遍历数组a,若 1 < = a [ i ] < = n 1<=a[i]<=n 1<=a[i]<=n,将辅助数组该下标处标记为1,表示被占有;
否则直接跳过,此时必然导致 [ 1 , n ] [1,n] [1,n]之间存在空位 - 第二次遍历:
遍历数组b(从1开始),第一个出现值为0的位置即为所求的最小正整数若遍历结束都没有出现0,则表明结果为 n + 1 n+1 n+1
功能函数实现:
🎁这里补充一下memset的用法:
memset的作用就是将某一块内存中的内容全部设置为指定的值,用来初始化新申请的内存,返回对象是一个指向存储区s的指针,从地址s开始,字节长度为n的内存全部赋值为ch
memset(void *s, int ch, size_t n);
参数列表:
- s:指针或者数组,指向要填充的内存块赋值,从地址s开始
- ch:初始化所赋的值
- n:字节长度
//主函数
int FindMissmin(int a[], int n)
{
//1.开辟一个新数组
int *b; //定义一个指向数组的指针
b = (int*)malloc(sizeof(int) * (n+1)); //定义一个长度为n的数组,记录元素1-n
if (b == NULL)
{
cout << "内存分配失败" << endl;
return 0;
}
memset(b, 0, sizeof(int) * (n + 1)); //作用是将某一块内存中的内容全部设置为指定的值, 这个函数通常为新申请的内存做初始化工作
//简单来说,就是将某一块内存的全部内容设置为指定的值
//2.填空
for (int i = 0; i < n; i++) {
if (a[i] > 0 && a[i]<=n) //将范围锁死在1到n之间
*(b+a[i])=1; //表示该位置被占用了
//这里用vs写的时候 若为b[a[i]]=1 会报错,写入"b"时缓冲区溢出,还不知道为什么...
}
//3.插空
int res;
for (res=1; res <= n; res++) {
if (b[res] == 0) //有空就插
break;
}
return res;
}
完整代码实现:
#include<iostream>
#include <stdlib.h>
#define Maxsize 50
using namespace std;
//主函数
int FindMissmin(int a[], int n)
{
//1.开辟一个新数组
int *b; //定义一个指向数组的指针
b = (int*)malloc(sizeof(int) * (n+1)); //定义一个长度为n的数组,记录元素1-n
if (b == NULL)
{
cout << "内存分配失败" << endl;
return 0;
}
memset(b, 0, sizeof(int) * (n + 1)); //作用是将某一块内存中的内容全部设置为指定的值, 这个函数通常为新申请的内存做初始化工作
//简单来说,就是将某一块内存的全部内容设置为指定的值
//2.填空
for (int i = 0; i < n; i++) {
if (a[i] > 0 && a[i]<=n) //将范围锁死在1到n之间
*(b+a[i])=1; //表示该位置被占用了
//这里用vs写的时候 若为b[a[i]]=1 会报错,写入"b"时缓冲区溢出,还不知道为什么...
}
//3.插空
int res;
for (res=1; res <= n; res++) {
if (b[res] == 0) //有空就插
break;
}
return res;
}
void InitList(int* a)
{
int x,i=0;
while (cin >> x) {
a[i++] = x;
if (cin.get() == '\n')
break;
}
}
int main() {
//int a[Maxsize];
int a[Maxsize];
int len;
cout << "请输入数组长度:" << endl;
cin >> len;
cout << "请输入数组:" << endl;
InitList(a);
cout << "未出现的最小正整数为:" << FindMissmin(a, len) << endl;
system("pause");
return 0;
}
输出结果: