线性表练习之ZUMA

祖玛是一款曾经风靡全球的游戏,其玩法是:在一条轨道上初始排列着若干个彩色珠子,其中任意三个相邻的珠子不会完全同色。此后,你可以发射珠子到轨
道上并加入原有序列中。一旦有三个或更多同色的珠子变成相邻,它们就会立即消失。这类消除现象可能会连锁式发生,其间你将暂时不能发射珠子。

在这里插入图片描述
开发商最近准备为玩家写一个游戏过程的回放工具。他们已经在游戏内完成了过程记录的功能,而回放功能的实现则委托你来完成。
游戏过程的记录中,首先是轨道上初始的珠子序列,然后是玩家接下来所做的一系列操作。你的任务是,在各次操作之后及时计算出新的珠子序列。

输入描述:
第一行是一个由大写字母’A’~'Z’组成的字符串,表示轨道上初始的珠子序列,不同的字母表示不同的颜色。

第二行是一个数字n,表示整个回放过程共有n次操作。

接下来的n行依次对应于各次操作。每次操作由一个数字k和一个大写字母Σ描述,以空格分隔。其中,Σ为新珠子的颜色。若插入前共有m颗珠子,则k ∈ [0, m]表示新珠子嵌入之后(尚未发生消除之前)在轨道上的位序。

输出描述:
输出共n行,依次给出各次操作(及可能随即发生的消除现象)之后轨道上的珠子序列。
如果轨道上已没有珠子,则以“-”表示。

样例输入:
ACCBA
5
1 B
0 A
2 B
4 C
0 A

样例输出:
ABCCBA
AABCCBA
AABBCCBA
——
A

全代码:


# include<iostream>
# include<stdio.h>
using namespace std;

const int N = 10;
char str[100];

struct Node
{
 char data;
 Node  *next;
 Node  *prior;
};

class LinkList
{
public:
 LinkList(char a[], int n); //有参构造函数,使用含有n个元素的数组a初始化单链表
 ~LinkList();             //析构函数 
 void Insert(int n, char a);//插入
 Node * Get(int i);   //获取线性表第i个位置上的元素结点地址
 void Delete(Node * l,Node* r);  //删除两节点间的元素 
 void PrintList(bool boo);//输出结果
 void GoThrough();//遍历整个链表删除重复项
private:
 Node * front;  //头指针
 Node * tail;
};

LinkList::LinkList(char a[], int n)//构造链表
{
 front = new  Node;
 tail = new Node;
 tail->next = NULL;
 Node *r = front;//初始化头结点
 for (int i = 0; i < n; i++)
 {
  Node *s = new Node;
  s->data = a[i];
  s->next = tail;// 生成新结点
  r->next = s;// 链接在尾结点后面
  s->prior = r;
  tail->prior = s;
  r = s;// 尾指针后移
 }
}

LinkList::~LinkList()    //析构函数
{
 Node *p = NULL;  //初始化工作指针p 
 while (front->next != NULL)  //要释放的结点存在
 {
  p = front->next;   //暂存要释放的结点
  p->prior = front;
  delete front;    //释放结点
  front = p;       //移动工作指针
 }
}

void LinkList::Insert(int i, char x)
{
 Node * p = front;  //初始化工作指针
 //若不是在第1个位置插入,得到第i个元素的地址。
 if (i != 1) p = Get(i);
 if (p != NULL)
 {
  Node  * s = new Node ;//建立新结点
  s->data= x;
  s->next = p->next;
  s->prior=p;
  p->next = s;  //将新结点插入到p所指结点的后面
  s->next->prior = s;
 }
 else throw "插入位置错误";
}

Node  *LinkList::Get(int i)
{
 Node *p = front;  //初始化工作指针
 int j = 1;
 if (i != 1)
  while (j!=i && p != NULL)
  {
   p = p->next;
   j++;
  };
 if (p == NULL) throw "位置非法";
 return p;
}

void LinkList::PrintList(bool boo)
{
 static int count;
 Node *p = front->next;
 if (p==tail)
 {
  str[count++] = '-';
 }
 else
 {
  while (p->next)
  {
   str[count++] = p->data;
   p = p->next;
  }
 }
 str[count++] = '\n';
 if (boo)
 {
  cout << "Outcome:" << endl;
  str[count] = '\0';
  cout << str;
  count = 0;
 }
};

void LinkList::Delete(Node * l, Node *r)
{
 Node *s = l;
 Node *t =NULL;
 Node *R1 = r->next;
 //l->prior->next = r->next;
 while (s != R1)
 {
  t = s->next;
  s->prior->next = t;
  t->prior = s->prior;
  delete s;
  s = t;
 }
}

void LinkList::GoThrough()
{
 Node * first = front->next;
 Node * second = first->next;
 while (first->next!= tail)
 {
  if (first->data != second->data)
  {
   if (first->next->next!=second&&first->next!=second)
   {
    Delete(first, second->prior);
    first = front;
    second = first->next;
   }
   else
   {
    first = first->next;
    second = first->next;
   }
  }
  else
  {
   if(second->next!=NULL)
    second = second->next;
  }
 }
}

int main()
{
 int lens;
 int turn;
 char a[N];
 cout << "Start Zuma:";
 cin >> a;
 lens = strlen(a);
 LinkList zuma(a, lens);
 cout << "Turns:";
 cin >> turn;
 for (int i = 0; i < turn; i++)
 {
  int ts;
  char cs;
  do
  {
   cin >> ts >> cs;
  } while (cs <'A'&&cs> 'Z');
  zuma.Insert(ts + 1, cs);
  zuma.GoThrough();
  zuma.PrintList(i==turn-1?true:false);
 }
}

构造函数

LinkList::LinkList(char a[], int n)//构造链表
{
 front = new  Node;
 tail = new Node;
 tail->next = NULL;
 Node *r = front;//初始化头结点
 for (int i = 0; i < n; i++)
 {
  Node *s = new Node;
  s->data = a[i];
  s->next = tail;// 生成新结点
  r->next = s;// 链接在尾结点后面
  s->prior = r;
  tail->prior = s;
  r = s;// 尾指针后移
 }
}

浅析:
(1)双链表的构造方式,便于前后查找;
(2)首、尾结点的建立,有首有尾,任何时刻都能将整个链表拎起,便于遍历全链表的循环删除操作;

析构函数

LinkList::~LinkList()    //析构函数
{
 Node *p = NULL;  //初始化工作指针p 
 while (front->next != NULL)  //要释放的结点存在
 {
  p = front->next;   //暂存要释放的结点
  p->prior = front;
  delete front;    //释放结点
  front = p;       //移动工作指针
 }
}

浅析:
(1)由于尾结点的存在,所以while判断语句的条件换为“front->next != NULL”,其余操作任与传统双链表相同。

插入

void LinkList::Insert(int i, char x)
{
 Node * p = front;  //初始化工作指针
 //若不是在第1个位置插入,得到第i个元素的地址。
 if (i != 1) p = Get(i);
 if (p != NULL)
 {
  Node  * s = new Node ;//建立新结点
  s->data= x;
  s->next = p->next;
  s->prior=p;
  p->next = s;  //将新结点插入到p所指结点的后面
  s->next->prior = s;
 }
 else throw "插入位置错误";
}

(没有什么特殊的,常规操作)

获取元素位置

Node  *LinkList::Get(int i)
{
 Node *p = front;  //初始化工作指针
 int j = 1;
 if (i != 1)
  while (j!=i && p != NULL)
  {
   p = p->next;
   j++;
  };
 if (p == NULL) throw "位置非法";
 return p;
}

(没有什么特殊的,只要记住用的时候注意一下i,j的初始值设定)

输出

(这是一个挺新颖的函数,我以前没有用过这种方法)

void LinkList::PrintList(bool boo)
{
 static int count;
 Node *p = front->next;
 if (p==tail)
 {
  str[count++] = '-';
 }
 else
 {
  while (p->next)
  {
   str[count++] = p->data;
   p = p->next;
  }
 }
 str[count++] = '\n';
 if (boo)
 {
  cout << "Outcome:" << endl;
  str[count] = '\0';
  cout << str;
  count = 0;
 }
};

浅析:
(1)接口使用bool类型,便于判断是否循环到了最后一组,使结果可以在一起输入过后一起输出。
(2)用数组记录下来所有字符
(3)用static计数
(4)在相应位置录入’\n’即可满足回车的需求
PS:也可以试试freopen(),存到文件里

遍历

(前方压轴出场的是核心代码!)

void LinkList::GoThrough()
{
 Node * first = front->next;
 Node * second = first->next;
 while (first->next!= tail)
 {
  if (first->data != second->data)
  {
   if (first->next->next!=second&&first->next!=second)
   {
    Delete(first, second->prior);
    first = front;
    second = first->next;
   }
   else
   {
    first = first->next;
    second = first->next;
   }
  }
  else
  {
   if(second->next!=NULL)
    second = second->next;
  }
 }
}

浅析:
在这里插入图片描述
开始循环判断,直到F->next=tail:
1.如果F的值与S的值相等且S->next不为空
则S向右移动
在这里插入图片描述
2.如果F的值不等于S的值:
判断是否有三个或三个以上的重复(F的下一个不是S,F的下一个的下一个也不是S,这样就可以确定F与S之间有三个或三个以上重复元素了):
(1)若有,则删除(函数)F和S->prior中的结点,每完成一次操作删除后就将指针复原指回头。
(2)若无,则F移向下一位,S移向F的下一位

删除

void LinkList::Delete(Node * l, Node *r)
{
 Node *s = l;
 Node *t =NULL;
 Node *R1 = r->next;
 //l->prior->next = r->next;
 while (s != R1)
 {
  t = s->next;
  s->prior->next = t;
  t->prior = s->prior;
  delete s;
  s = t;
 }
}

浅析:
(1)单独成立一个函数,便于代码重用
(2)用结点作为参数,删除结点(包括结点本身)之间的结点


总算完成了这zuma…感动到哭泣

发布了14 篇原创文章 · 获赞 15 · 访问量 1124

猜你喜欢

转载自blog.csdn.net/FirstOfSky/article/details/105274714