从一个例子来回顾链表操作,学生数据包括学号(id)和学分(v),
相应的操作有添加学生数据,删除学生数据,修改学分,
一系列的操作后,求学分top 3的数据。
我们可以一开始就构造一个有序链表,来一个数据就插入到合适的位置,删除和修改时都找到相应的节点去操作,可以定义为双向链表来方便删除操作,这个思路比较清晰。但是如果修改和删除的次数较多,查找次数较多。
有些像表结构创建索引,复杂的索引可以很好的处理这个问题。
这里考虑以下处理方案:
1. 创建普通链表,在获取top的时候再进行遍历,比如获取top3数据,获取到top1的时候,可以对数据做个标记,方便获取到top2,再设置标记,获取到top3,完成后再回复标记。
struct stu{
int id;
int v;
int tag;//
stu* nt;
stu* pre;
inline bool isGreat(stu* o)
{
if (o == 0)
{
return true;
}
if (v > o->v)
{
return true;
}
else if(v == o->v && id < o->id)
{
return true;
}
return false;
}
inline void append(stu* s)
{
s->nt = nt;
if (nt != 0)
{
nt->pre = s;
}
nt = s;
s->pre = this;
}
inline void rmed()
{
pre->nt = nt;
if (nt != 0)
{
nt->pre = pre;
}
}
};
//add del modify
//get top3
stu stus;
int tn = 0;
stu alls[1000];
void add(stu* &hd, stu* s)
{
s->nt = hd;
if (hd != 0)
{
hd->pre = s;
}
hd = s;
}
void rm(stu* &hd, stu* s)
{
if (hd == s)
{
hd = s->nt;
s->nt->pre = hd;
}
else
{
stu* p = s->pre;
p->nt = s->nt;
if (s->nt != 0)
{
s->nt->pre = p;
}
}
}
void rmstu(int id)
{
rm(stus.nt, &alls[id]);
}
void modify(int id, int v)
{
alls[id].v = v;
}
int gettop()
{
stu* top[3] = {0};
int tn = 0;//record the number, maybe there is less than 3
for (int i = 0; i < 3; i++)
{
stu* p = stus.nt;
while (p != 0)
{
if (p->isGreat(top[i]) && p->tag == 0)
{
top[i] = p;
}
//
p = p->nt;
}
if (top[i] != 0)
{
tn++;
top[i]->tag = 1;//not selected in the next time
}
}
printf("\n\ntop value is: ");
//reset tag
for (int i = 0; i < 3; i++)
{
if (top[i] != 0)
{
printf("%d, ", top[i]->v);
top[i]->tag = 0;//
}
}
return tn;
}
int data[][2] = {
{1,3},{3,5},{2,2},{8,11},{7,5},{6,11},{5,3},{4,4},
{10,5},{9,4},{11,6},{12,29},{13,3},{14,3},{15,22},{17,7},{16,5}
};
int _tmain(int argc, _TCHAR* argv[])
{
for (int i = 0; i < 15; i++)
{
int id = data[i][0];
int v = data[i][1];
alls[id].id = id;
alls[id].v = v;
add(stus.nt, &alls[id]);
}
gettop();
modify(4,44);
modify(9,33);
gettop();
rmstu(9);
gettop();
}
打印结果
top value is: 29, 22, 11,
top value is: 44, 33, 29,
top value is: 44, 29, 22,
2. 考虑建立部分排序top序列,当新的数据大于top序列的最小值时,替换进top序列,
需要考虑几种情况,修改的数据是否已经在top序列,删除的数据是否在top序列,每次操作都更新top序列,
在取top的时候,直接获取top序列即可。
这个方式需要考虑的情况较多。
构造一个链表来保存数据,链表的前面几个数据是top序列,用tmin标记最后一个top数据,其他的数据在tmin节点后,新来的数据如果大于tmin数据,就插入到top序列。如果删除的数据在top序列里,从tmin后面的序列中找一个最大的,补到top序列中。有数据修改时,可以理解为先删除再插入数据,这样来复用删除的代码。
#define TOPN 3
struct stu{
int id;
int v;
int tag;//
stu* nt;
stu* pre;
inline bool isGreat(stu* o)
{
if (o == 0)
{
return true;
}
if (v > o->v)
{
return true;
}
else if(v == o->v && id < o->id)
{
return true;
}
return false;
}
inline void append(stu* s)
{
s->nt = nt;
if (nt != 0)
{
nt->pre = s;
}
nt = s;
s->pre = this;
}
inline void rmed()
{
pre->nt = nt;
if (nt != 0)
{
nt->pre = pre;
}
}
};
int data[][2] = {
{1,3},{3,5},{2,2},{8,11},{7,5},{6,11},{5,3},{4,4},
{10,5},{9,4},{11,6},{12,29},{13,3},{14,3},{15,22},{17,7},{16,5}
};
struct ts{
int tn;
stu hd;
stu* tmin;
void pt()
{
stu* p = hd.nt;
while (p != 0)
{
printf("%d(%d), ", p->id, p->v);
p = p->nt;
}
}
stu* getmax()
{
stu* m = 0;
stu* p = tmin->nt;
while (p != 0)
{
if (p->isGreat(m))
{
m = p;
}
p = p->nt;
}
return m;
}
bool isin(stu* s)
{
stu* p = hd.nt;
for (int i = 0; i < tn; i++)
{
if (p == s)
{
return true;
}
p = p->nt;
}
return false;
}
void update(stu* s)
{
if (isin(s))
{
rm(s);
add(s);
}
else if (s->isGreat(tmin))
{
s->rmed();
add(s);
}
}
void rm(stu* s)
{
if (isin(s))
{
stu* m = getmax();
s->rmed();
tn--;
if (m != 0)
{
m->rmed();
add(m);
}
getmin();
}
else
{
s->rmed();
}
}
stu* getmin()
{
stu* p = hd.nt;
for (int i = 0; i < tn-1; i++)
{
p = p->nt;
}
tmin = p;
return tmin;
}
void add(stu* s)
{
if (tn < TOPN)
{
addsort(s);
tn++;
getmin();
}
else if (s->isGreat(tmin))
{
addsort(s);
getmin();
}
else
{
tmin->append(s);
}
}
void addsort(stu* s)
{
stu* p = &hd;
while (p->nt != 0)
{
if (s->isGreat(p->nt))
{
break;
}
p = p->nt;
}
p->append(s);
}
};
ts tlist;
stu sdat[1000];
int _tmain(int argc, _TCHAR* argv[])
{
for (int i = 0; i < 15; i++)
{
int id = data[i][0];
int v = data[i][1];
sdat[id].id = id;
sdat[id].v = v;
tlist.add(&sdat[id]);
}
for (int i = 0; i < 15; i++)
{
int id = data[i][0];
int v = data[i][1];
// sdat[id].id = id;
// sdat[id].v = v;
if (id == 15)
{
sdat[id].v = 9;
printf("\nupdate:");
tlist.update(&sdat[id]);
}
else if (id == 6)
{
sdat[id].v = 39;
printf("\nupdate:");
tlist.update(&sdat[id]);
}
else if (id == 4)
{
sdat[id].v = 29;
printf("\nupdate:");
tlist.update(&sdat[id]);
}
else
{
tlist.rm(&sdat[id]);
}
printf("\n\n id=%d :", id);
tlist.pt();
}
return 0;
}
再修改下
struct ts{
int tn;
stu hd;
stu* tmin;
inline void init()
{
tn = 0;
hd.nt = 0;
tmin = 0;
}
void pt()
{
stu* p = hd.nt;
while (p != 0)
{
printf("%d(%d), ", p->id, p->v);
p = p->nt;
}
}
inline private stu* getmax()
{
stu* m = 0;
stu* p = tmin->nt;
while (p != 0)
{
if (p->isGreat(m))
{
m = p;
}
p = p->nt;
}
return m;
}
bool isin(stu* s)
{
stu* p = hd.nt;
for (int i = 0; i < tn; i++)
{
if (p == s)
{
return true;
}
p = p->nt;
}
return false;
}
inline void update(stu* s)
{
//the data may be lower, so need to check if data in Top list changed
if (isin(s))
{
rmIn(s);
add(s);
}
else if (s->isGreat(tmin))
{
s->rmed();
add(s);
}
}
//remove stu in Top list, so it need to find a max value
//in the rest l
inline private void rmIn(stu* s)
{
stu* m = getmax();
s->rmed();
tn--;
if (m != 0)
{
m->rmed();
add(m);
}
getmin();
}
void rm(stu* s)
{
if (s->isGreat(tmin))
{
rmIn(s);
}
else
{
s->rmed();
}
/*
if (s->isGreat(tmin))
{
s->rmed();
tn--;
stu* m = getmax();
if (m != 0)
{
m->rmed();
add(m);
}
}
else if (s == tmin)
{
stu* m = getmax();
s->rmed();
tn--;
if (m != 0)
{
m->rmed();
add(m);
}
getmin();
}
else
{
s->rmed();
}
*/
}
inline private stu* getmin()
{
stu* p = hd.nt;
for (int i = 0; i < tn - 1; i++)
{
p = p->nt;
}
tmin = p;
return tmin;
}
inline void add(stu* s)
{
if (tn < TOPN)
{
addsort(s);
tn++;
getmin();
}
else if (s->isGreat(tmin))
{
addsort(s);
getmin();
}
else
{
tmin->append(s);
}
}
inline private void addsort(stu* s)
{
stu* p = &hd;
while (p->nt != 0)
{
if (s->isGreat(p->nt))
{
break;
}
p = p->nt;
}
p->append(s);
}
};