hdu 5818 Joint Stacks 2016 Multi-University 7

本文详细解析了一道涉及两个栈的编程题目,栈支持push、pop、merge三种操作。通过两种不同的实现方式,一种使用数组模拟栈,另一种采用双向链表实现,探讨了如何在合并操作时保持元素的进栈顺序。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Problemacm.hdu.edu.cn/showproblem.php?pid=5818

官方题解bestcoder.hdu.edu.cn/blog/2016-multi-university-training-contest-7-solutions-by-sysu/

题意:两个栈,支持push、pop、merge 3种操作

push x y :把 y 压入栈 x

pop x :栈 x 弹出一个元素,输出它的值

merge x y :把 y 中的元素全部移到 x 中,并且保持所有元素的进栈时的先后顺序

分析:官方题解中有种解法,是开第3个栈,当要合并的时候把两个栈里的元素全部放到这个栈里,并且清空原来两个栈

我理解:

#include <stdio.h>
#define N 100000
struct
{
	int v;
	int id; // 标明元素的进栈先后
} a[N], b[N], c[N]; // 3个栈
int main()
{
	int n,kase=1;
	while( ~scanf("%d%*c",&n) && n )
	{
		printf("Case #%d:\n",kase++);
		// 3个栈的栈顶指针
		int topa = 0, topb = 0, topc = 0;
		// “时间”标明元素进栈先后
		int time;
		// own标记c中的元素属于A还是B
		char own = '\0';
		for(time=0; time<n; time++)
		{
			char op[7],who;
			int val,i,j;
			scanf("%s %c%*c",op, &who);
			if( op[1] == 'u' ) // push
			{
				scanf("%d%*c", &val);
				if( who == 'A' )
				{
					a[topa].v = val;
					a[topa++].id = time;
				}
				else
				{
					b[topb].v = val;
					b[topb++].id = time;
				}
			}
			else if( op[1] == 'o' ) // pop
			{
				if( who == 'A' )
				{
					if( topa ) // 如果a栈中有元素,必是最新的
						printf("%d\n", a[--topa].v);
					else if( topc && own == 'A' ) // c中有元素且属于A
						printf("%d\n", c[--topc].v);
				}
				else
				{
					if( topb )
						printf("%d\n", b[--topb].v);
					else if( topc && own == 'B' )
						printf("%d\n", c[--topc].v);
				}
			}
			else // merge
			{
				// 读掉第2个参数
				scanf("%*s");
				// 把a、b栈的元素都放进c栈
				for(i=j=0; i<topa || j<topb; )
				  if( i<topa && j<topb ) //a、b栈都非空
				  {
					  if( a[i].id < b[j].id ) // 比较入栈顺序
					  {
						  c[topc].v = a[i].v;
						  c[topc++].id = a[i++].id;
					  }
					  else
					  {
						  c[topc].v = b[j].v;
						  c[topc++].id = b[j++].id;
					  }
				  }
				  else if( i<topa ) // a栈非空
				  {
					  c[topc].v = a[i].v;
					  c[topc++].id = a[i++].id;
				  }
				  else // b栈非空
				  {
				      c[topc].v = b[j].v;
				      c[topc++].id = b[j++].id;
				  }
				// 清空a、b栈
				topa = topb = 0;
				// 第1个参数表示归属
				own = who;
			}
		}
	}
	return 0;
}
可以开两个栈,记录两个栈的栈顶指针(topa,topb)和各自的一个标记指针(marka,markb)

要合并时,比如 merge A B,就把 b 栈的标记指针打到 a 栈的栈顶指针那,表示此标记开始以下的元素都属于 b 栈

同时把 a 栈在 b 栈上的标记指针打到 b 栈的栈底

那么属于 a 栈的元素就包括:a 栈中从栈顶指针 topa 到 b 的标记指针 markb 中间一段的元素,和 b 栈中从 a 栈的标记指针 marka 到 b 栈栈底一段的元素

要弹栈时,比如 pop B,就要比较 topb 所指元素(如果有)与 markb 所指元素(如果有)的进栈顺序,输出较新的一个,并且删掉那个元素

用双向链表实现栈(因为删元素的时候要把与它相邻的两个元素连起来)

注意:如果 pop B 的时候要删的是 markb 所指的元素,而 topa == markb,那么在删元素前更改 markb 的指向时,要把 topa 也一同改了(不然 topa 成野指针)

#include <stdio.h>
#include <stdlib.h>

typedef struct Node
{
	int v,id;
	struct Node *pre,*next; // 上面和下面
}node;

void push(node **s, int val, int tm)
{
	node *p = malloc( sizeof( node ) );
	p->v = val;
	p->id = tm;
	p->pre = NULL;
	p->next = *s;
	if(*s)
	  (*s)->pre = p;
	*s = p;
}
/* 删除元素 */
void erase(node **me, node **histop)
{
	node *q = *me;
	printf("%d\n", q->v);
	if( q->pre )
		q->pre->next = q->next;
	if( q->next )
		q->next->pre = q->pre;
	*me = q->next;
	if( *histop == q )
		*histop = *me;
	free(q);
}

void pop(node **mytop, node **mymark, node **histop, node *hismark)
{
	// 我栈中有属于我的元素,他栈中没有
	if( *mytop != hismark && !*mymark )
		erase(mytop,histop);
	// 我栈没有,他栈有
	else if( *mytop == hismark && *mymark )
		erase(mymark,histop);
	// 我有他有
	else if( *mytop != hismark && *mymark )
		if( (*mytop)->id < (*mymark)->id )
			erase(mymark,histop);
		else
			erase(mytop,histop);
}

void merge(node **mymark, node **hismark, node *histop)
{
	// 标记打到对方栈顶
	*mymark = histop;
	// 对方的标记打到“栈底”
	*hismark = NULL;
}
/* 销毁链表 */
void destroy(node *s)
{
	while(s)
	{
		node *q = s;
		s = s->next;
		free(q);
	}
}

int main()
{
	int n,kase=1;
	while( ~scanf("%d%*c",&n) && n )
	{
		int i;
		// 栈顶指针,标记指针
		node *topa,*topb,*marka,*markb;
		printf("Case #%d:\n", kase++);
		topa = topb = marka = markb = NULL;
		for(i=0; i<n; i++)
		{
			char op[7],who;
			int val;
			scanf("%s %c%*c", op, &who);
			if( op[1] == 'u' )
			{
				scanf("%d%*c", &val);
				if( who == 'A' )
					push(&topa, val, i);
				else
					push(&topb, val, i);
			}
			else if( op[1] == 'o' )
			{
				if( who == 'A' )
					pop(&topa, &marka, &topb, markb);
				else
					pop(&topb, &markb, &topa, marka);
			}
			else
			{
				scanf("%*s");
				if( who == 'A' )
					merge(&marka, &markb, topb);
				else
					merge(&markb, &marka, topa);
			}
		}
		// 销毁链表
		destroy(topa);
		destroy(topb);
	}
	return 0;
}
本来想用C++的 vertor 来写栈,删元素时直接用它的 erase(),然而wa了…不知道它删完一个元素后,是不是后面元素的下标都自动 -1 还是怎么变,还是因为写漏了个冒号…

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值