数组下标的巧用(约瑟夫环等)
最近在牛客网看到华为的笔试题,如下一题,不难,但觉得遇到好多可以巧用数组下标来存储数据的案例,觉得可以列在一起分享一下。
题1:
明明想在学校中请一些同学一起做一项问卷调查,为了实验的客观性,他先用计算机生成了N个1到1000之间的随机整数(N≤1000),对于其中重复的数字,只保留一个,把其余相同的数去掉,不同的数对应着不同的学生的学号。然后再把这些数从小到大排序,按照排好的顺序去找同学做调查。请你协助明明完成“去重”与“排序”的工作。
Input Param
n 输入随机数的个数
inputArray n个随机整数组成的数组
Return Value
OutputArray 输出处理后的随机整数
因为输入的数的个数不限,然后需要去重,再排序。所以大家的下意识常规做法:先排序,这样重复的在一起,然后遍历去重就好了。
低效常规做法:
#include<stdio.h>
int main()
{
int n,a[10000],i,j,k,temp;
while(scanf("%d",&n)!=EOF)
{
for(i=0;i<n;i++)
scanf("%d",&a[i]);
for(i=0;i<n;i++)
{
for(j=i+1;j<n;j++)
{
if(a[i]==a[j])
{
for(k=j+1;k<n;k++)
a[k-1]=a[k];
n--;
j--;
}
}
}
for(i=0;i<n-1;i++)
{
for(j=0;j<n-1-i;j++)
{
if(a[j]>a[j+1])
{
temp=a[j];
a[j]=a[j+1];
a[j+1]=temp;
}
}
}
for(i=0;i<n;i++)
printf("%d\n",a[i]);
}
}
但海量数据排序,怎么也逃不了两层嵌套循环,效率很低。于是可以这么想,因为输入的数不超过1000,所以可以定义一个1000长度的一维数组,下标可以作为输入的数,如果输入,对应下标的数据置1,标记为已存在,这样下标默认排序了,同时也去重了。下标的妙用如下:
//1-1000随机数,去重,排序(这里只要输入数的范围不是特别大,都可以这么用,输入的个数无所谓)
#include <iostream>
using namespace std;
int main() {
int N, n;
while (cin >> N) {
int a[1001] = { 0 };
while (N--) {
cin>> n;
a[n]= 1;
}
for (int i = 0; i < 1001;i++)
if (a[i])
cout<< i << endl;
}
return 0;
}
同样的用法,我想起之前写约瑟夫环的问题:
已知n个人(以编号1,2,3...n分别表示)围坐在一张圆桌周围。从编号为k的人开始报数,数到m的那个人出列;他的下一个人又从1开始报数,数到m的那个人又出列;依此规律重复下去,直到圆桌周围的人全部出列。
之前,自己用的循环链表来写,每次不停的去数数,然后遍历,删除节点,再数数,直至所有结点全部剔完。后来用这种方法,数组下标标记法。踢出去置0或者置1标记下就好。效率高了很多。因为数组的查找效率是O(1
class Solution {
public:
int LastRemaining_Solution(unsigned int n, unsigned int m)
{
if(m==0||n==0)
return -1;
struct Lnode{
int data;
Lnode* next;
};
int len = sizeof(struct Lnode);
unsigned int i=1;
struct Lnode* head = (struct Lnode*)malloc(len);
head->data=0; //因为需要一个圈,这里是头结点(也就是带数据的),不是头指针。
head->next=NULL;
struct Lnode* cur = head;
while(i<n){
struct Lnode* p = (struct Lnode*)malloc(len);
p->data=i;
p->next=NULL;
cur->next = p;
cur = p ;
i++;
}
cur->next = head; //形成循环链表,首尾相连了
Lnode* q=head;
Lnode* pre; //为下面删除结点定义一个前驱
while(n>1){ //删除数到的结点
i=0;
while(i++<m-1){ //遍历到第m-1个结点
pre=q;
q=q->next;
}
pre->next=q->next;
free(q);
q=pre->next;
--n;
}
return (q->data);
}
};
后来用数组改进:
#include<iostream>
#include<cstdlib>
#include<cstring>
#define MAX 101
using namespace std;
int main()
{
boola[MAX];
intn,m;
cin>>n>>m;
memset(a,0,sizeof(int)*n);
//memset(a,false,sizeof(int)*n);
// for(int i=1;i<=n;++i)a[i]=false;
intout=0,t=0,s=0;
do
{
++t;
if(t==n+1) t=1;
if(false==a[t]) ++s;
if(s==m)
{
a[t]=true;
++out;
s=0;
cout<<t<<" ";
}
}while(out!=n);
system("pause");
return 0;
}
数组下标用来存储数据,而数组元素存其标志,可以有很多妙用。。