表是由n个同一类型的元素a1,a2,…,an组成的有限序列。(元素不可对调,顺序表示了逻辑关系)
·支持的运算:
1.ListEmpty(L):表L是否为空
2.ListLength(L):表L的长度
3.ListLocate(x,L):元素x在表L中的位置(若x在表中重复出现多次,则返回最前面的x的位置)
4.ListRetrieve(k,L):返回表L的位置k处的元素(若表中没有位置k时,该运算无定义)
5.ListInsert(k,x,L):在表L的位置k处插入元素x,并将原位置及之后的元素往后挪
6.ListDelete(k,L):从表L中删除位置k处的元素,并返回被删除的元素(若表中没有位置k时,该运算无定义)
7.PrintList(L):将表L中所有元素按位置的先后次序打印输出
·ADT表的实现:
1.数组实现(顺序存储实现)
–等概论下插入和删除运算的平均性能为n/2、(n-1)/2 时间复杂度O(n)->用于移动元素
–优点:查找方便、随机存取
–缺点:大小固定,不方便动态添加
2.指针实现(非顺序存储实现、链式存储实现)
–增删O(n)->用于遍历链表(与数组实现时的O(n)不同)
–优点:动态添加删除、大小可变
–缺点:查询效率低、需要额外空间来存这一关系
·head结点的作用:简化插入和删除操作(不用特判情况)
·单链表逆置操作
新建:(0 0想法大概是用p1,p2两个指针从左到右扫描,记录每个结点的上一结点pre,再新建一个链表把pre赋值给next(?)
就地:(头插法)一边遍历一边把每个结点插到头结点之后http://blog.chinaunix.net/uid-27033491-id-3349370.html
·单链表合并:
(a1,a2,a2,…an)(b1,b2,b3,…bm)->(O(n+m))
举例:多项式加法
·将单链表b接到a后O(n)(a接到b后O(m))
·循环链表
·双链表
//–作业题
2.3 击鼓传花
(要删去的人数为n-1,所以每次只要找到要删去的位置pos,把其后的数都往前移,pos再移到下一位置,这样重复n-1次,最后pos位置的数既是答案。复杂度O(n^2)/O(mn))
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;
int a[550];
int i;
int main()
{
int n,m;
cin >> n >> m;
for(i = 1; i <= n; i++)
a[i] = i;
int cnt = 0;
int pos = 1;
int len = n;
while(cnt < n-1)
{
//move
for(i = pos+1; i <= len; i++)
a[i-1] = a[i];
len--;
pos += m-1;
while(pos > len) pos-=len;
// pos = (pos-1)%len + 1; 另一种常用写法
cnt++;
}
cout << a[pos] << endl;
return 0;
}
//思路2 瞎模拟 (这样当删到最后几个未被踢出的结点相当稀疏的时候寻找下一人所花时间较多)
vis[1] = 1, find = 1, cnt = 0, i = 1;
while (find != n)
{
i++;
if (i > n) i = 1;
if (!vis[i]) cnt++;
if (cnt == m)
vis[i] = 1, cnt = 0, find++;
}
//思路3 维护两个数组保存每一结点的前后结点,每当踢出一个结点时将pre[i]的nex值修改为nex[i],nex[i]的pre值修改为pre[i]
1 2 3 4 5
nex 2 3 4 5 1
pre 5 1 2 3 4
//思路4
约瑟夫环的变形
约瑟夫问题:
一圈共有N个人,从第一个开始报数,报到M的人自杀,然后重新开始报数,问最后自杀的人是谁
如N=6,M=5,被杀掉的顺序是:5,4,6,2,3,1。
把问题稍微改变一下:
N个人(0, 1, 2, … , N-1),从0开始报数,报到M-1的退出,剩下的人继续从0开始报数。求最后退出的人的编号
Round 1:
0 1 2 3 … N-2 N-1
第一个退出的人是(M-1)%N
0 … M-2 (gg) M … N-2 N-1
N-1人组成新环, (由M开始), 即:
M M+1 … N-1 0 1 … M-2
重新编号:
M M+1 … N-1 0 1 … M-2
0 1 … … … … N-2
问题转变为(N-1)个人,报到(M-1)的人自杀 —-> N-1的子问题
假设N-1个人时最后剩下的为x,则N个人时,最后剩下的也是这个人,对应编号为
x’=(x+M)%N
故有递推公式:
令F[i]表示i个人玩游戏报M退出最后胜利者的编号
则有 F[1] = 0
F[i] = (F[i-1] + M) % i (i>1)
回到本题,当n>1时,只要重新编号:
1 2 3 4 … N–1 N
0 1 2 … N-3 N-2
则为(N-1)个人报M退出的约瑟夫问题
故有 F[1] = 0
F[i] = (F[i-1] + M) % i (i>1)
最后答案+2即可
#include <iostream>
using namespace std;
int main()
{
int n, m, i, ans;
cin >> n >> m;
for (ans = 0, i = 2; i <= n - 1; ++i)
ans = (ans + m) % i;
cout << (n == 1 ? 1 : ans + 2) << endl;
return 0;
}
约瑟夫环博客参考:http://blog.youkuaiyun.com/kangroger/article/details/39254619
2.4 排队
(同理,模拟服务的人以及每隔id个人离开队伍,其后数据往前挪)
#include<cstdio>
#include<iostream>
using namespace std;
#define MAX 10000
int a[MAX + 10];
int i;
int n;
int cnt; //服务人数
int id; //编号
int pos;
int len = MAX;
void move(int p,int N)
{
for(i = p+1; i <= N; i++)
a[i-1] = a[i];
len--;
}
int main()
{
scanf("%d",&n);
for(i = 1; i <= MAX; i++)
a[i] = i+1;
cnt = 0;
id = 2;
pos = 1;
while(cnt < n-1)
{
while(pos < len)
{
move(pos,len);
pos += id-1;
}
cnt++;
id = a[1];
pos = 1;
}
printf("%d\n",a[1]);
}
————
练习题
2.1 塔防
(线性表的基本操作 增删改查什么的_(:зゝ∠))
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
int a[550];
int i;
int len;
void add(int num)
{
a[len] = num;
len++;
}
void del(int id)
{
for(i = id+1; i < len; i++)
a[i-1] = a[i];
len--;
}
void cge(int id,int num)
{
a[id] = num;
}
int qry(int id)
{
return a[id];
}
int main()
{
int n,m;
scanf("%d%d",&n,&m);
for(i = 0; i < n; i++)
scanf("%d",&a[i]);
len = n;
char s[5];
int index;
int num;
while(m--)
{
scanf("%s",&s);
if(s[0] == 'a')
{
scanf("%d",&num);
add(num);
}
else if(s[0] == 'd')
{
scanf("%d",&index);
del(index);
}
else if(s[0] == 'c')
{
scanf("%d%d",&index,&num);
cge(index,num);
}
else if(s[0] == 'q')
{
scanf("%d",&index);
printf("%d\n",qry(index));
}
}
return 0;
}
2.2 逆序数
(对于原数列1,2,3…,n,逆序数i其实就对应了这些数里面第i+1大的数。如果用数组从0开始存储,a[i]就是原数字,如a[4] = 5。找到这个数字之后把它删去,因为这是个单增数组,全部往前挪即可,然后再继续从剩下的数字中找下标为i的数输出。)
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
int a[550];
int num;
int len;
void del(int id)
{
for(int i = id+1;i < len; i++)
a[i-1] = a[i];
len--;
}
int main()
{
int n;
cin >> n;
len = n;
for(int i = 0; i < n; i++)
a[i] = i+1;
cin >> num;
cout << a[num];
del(num);
for(int i = 1; i < n; i++)
{
cin >> num;
cout <<" " << a[num];
del(num);
}
cout << endl;
return 0;
}