数据结构-队列与栈以及相关练习

队列(顺序表实现)

#include<iostream>
#include<time.h>

using namespace std;

//采用顺序表来实现队列,因此先封装一个顺序表SqList
typedef struct SqList
{
	int* data;
	int size;
}SqList;

SqList* InitSqList(int n)
{
	SqList* q = (SqList*)malloc(sizeof(SqList));
	q->data = (int*)malloc(sizeof(int) * n);
	q->size = n;
	return q;
}

void InsertSqList(SqList*q, int pos, int val)
{
	if (pos<0 || pos>q->size)
	{
		cout << "插入位置不合法" << endl;
		return;
	}

	for (int i = q->size - 1; i >= pos; i--)
	{
		q->data[i + 1] = q->data[i];
	}
	q->data[pos] = val;

	return;
}

int GetSqList(SqList* q, int pos)
{
	if (pos<0 || pos>q->size)
	{
		cout << "查看位置不合法" << endl;
		return -1;
	}
	return q->data[pos];
}

void ClearSqList(SqList*q)
{
	if (q == NULL) return;
	free(q->data);
	free(q);
	return;
}

typedef struct Queue
{
	SqList* data;//队列的数据存储区
	int size;//队列的大小
	int head, tail;//队列的头尾指针
	int count;//队列里的元素数量
}Queue;

//队列的初始化操作,传入队列的大小
Queue* InitQueue(int n)
{
	Queue* q = (Queue*)malloc(sizeof(Queue));//申请出一个队列空间
	q->data = InitSqList(n);
	q->size = n;
	q->head = q->tail = q->count = 0;
	return q;
}

//判断队列是否为空
bool empty(Queue* q)
{
	return q->count == 0;
}

//队列的插入操作,即入队;传入待操作队列和待插入元素
bool push(Queue* q, int val)
{
	if (q->count == q->size)
	{
		cout << "队列已满,入队失败" << endl;
		return false;
	}

	InsertSqList(q->data, q->tail, val);
	q->tail += 1;//将tail指针向后移动一位

	if (q->tail == q->size) q->tail = 0;//规避假溢出情况
	q->count += 1;
	
	return true;
}

//队列的删除操作,即出队;传入待操作队列
bool pop(Queue* q)
{
	if (empty(q))
	{
		cout << "队列为空,出队失败" << endl;
		return false;
	}

	q->head += 1;
	q->count -= 1;
	
	return true;
}

//查看队首元素
int front(Queue* q)
{
	if (q->count == 0) return -1;
	return GetSqList(q->data, q->head);
}

//队列的销毁操作,传入待销毁的队列
void ClearQueue(Queue* q)
{
	if (NULL == q)
	{
		cout << "不存在该队列" << endl;
		return;
	}
	ClearSqList(q->data);
	free(q);
	return;
}

//队列的输出操作
void CoutQueue(Queue* q)
{
	for (int i = 0; i < q->count; i++)
	{
		printf("%-4d", GetSqList(q->data, (q->head+i)%q->size));
	}
	cout << endl;
	cout << endl;
	return;
}

int main()
{
	srand(time(0));
	Queue* q = InitQueue(5);
	for (int i = 0; i < 7; i++)
	{
		int op = rand() % 5;
		int val = rand() % 100;
		switch (op)
		{
		case 0:
		case 1:
		case 2:
		case 3:
			cout << "入队:" << val << endl;
			push(q, val);
			break;
		case 4:
			cout << "出队:" << front(q) << endl;
			pop(q);
			break;
		}
		CoutQueue(q);
	}
	return 0;
}

用顺序表实现的循环队列示意图:

初始化

//封装了顺序表结构
SqList* InitSqList(int n)
{
	SqList* q = (SqList*)malloc(sizeof(SqList));
	q->data = (int*)malloc(sizeof(int) * n);
	q->size = n;
	return q;
}

//队列的初始化操作,传入队列的大小
Queue* InitQueue(int n)
{
	Queue* q = (Queue*)malloc(sizeof(Queue));//申请出一个队列空间
	q->data = InitSqList(n);
	q->size = n;
	q->head = q->tail = q->count = 0;
	return q;
}

入队

//封装了顺序表结构
void InsertSqList(SqList*q, int pos, int val)
{
	if (pos<0 || pos>q->size)
	{
		cout << "插入位置不合法" << endl;
		return;
	}

	for (int i = q->size - 1; i >= pos; i--)
	{
		q->data[i + 1] = q->data[i];
	}
	q->data[pos] = val;

	return;
}

//队列的插入操作,即入队;传入待操作队列和待插入元素
bool push(Queue* q, int val)
{
	if (q->count == q->size)
	{
		cout << "队列已满,入队失败" << endl;
		return false;
	}

	InsertSqList(q->data, q->tail, val);
	q->tail += 1;//将tail指针向后移动一位

	if (q->tail == q->size) q->tail = 0;//规避假溢出情况
	q->count += 1;
	
	return true;
}

出队

//封装了顺序表的结构
int GetSqList(SqList* q, int pos)
{
	if (pos<0 || pos>q->size)
	{
		cout << "查看位置不合法" << endl;
		return -1;
	}
	return q->data[pos];
}

//队列的删除操作,即出队;传入待操作队列
bool pop(Queue* q)
{
	if (empty(q))
	{
		cout << "队列为空,出队失败" << endl;
		return false;
	}

	q->head += 1;
	q->count -= 1;
	
	return true;
}

运行结果:

出队:-1
队列为空,出队失败


入队:76
76

入队:19
76  19

入队:50
76  19  50

入队:2
76  19  50  2

入队:74
76  19  50  2   74

入队:46
队列已满,入队失败
76  19  50  2   74

队列(链表实现)

#include<iostream>
#include<time.h>

using namespace std;

//采用链表来实现队列,因此先封装一个顺序表SqList
typedef struct ListNode
{
	ListNode* next;
	int data;
}ListNode;

typedef struct LinkList
{
	ListNode* tail;//指向尾节点
	ListNode head;//定义一个头节点,头节点中不存储数据
}LinkList;

LinkList* InitList()
{
	LinkList* L = (LinkList*)malloc(sizeof(LinkList));
	L->head.next = NULL;
	L->tail = &(L->head);
	return L;
}

void ClearList(LinkList* L)
{
	ListNode* q = L->head.next;
	ListNode* p;
	while (q)
	{
		p = q->next;
		free(q);
		q = p;
	}
	free(L);
	return;
}

int GetList(LinkList*L)
{
	return L->head.next->data;
}

//尾插法
void InsertList(LinkList* L, int val)
{
	ListNode* node = (ListNode*)malloc(sizeof(ListNode));
	node->data = val;
	node->next = NULL;

	L->tail->next = node;
	L->tail = node;
	return;
}

//删除第一个节点
void EraseList(LinkList* L)
{
	ListNode* p = L->head.next;
	L->head.next = p->next;
	if (p == L->tail) L->tail = &(L->head);
	free(p);
	return;
}

typedef struct Queue
{
	LinkList* L;//将底层结构改为链表后就不会出现假溢出
	int count;
}Queue;

//初始化操作
Queue* InitQueue()
{
	Queue* q = (Queue*)malloc(sizeof(Queue));
	q->L = InitList();
	q->count = 0;
	return q;
}

//判空操作
bool empty(Queue* q)
{
	return q->count == 0;
}

//查看队首元素
int front(Queue* q)
{
	if (empty(q))
	{
		cout << "队列为空" << endl;
		return -1;
	}

	return GetList(q->L);
}

//入栈
void push(Queue*q,int val)
{
	InsertList(q->L, val);
	q->count += 1;
	return;
}

//出栈
void pop(Queue* q)
{
	if (empty(q)) return;
	EraseList(q->L);
	q->count -= 1;
	return;
}

//销毁操作
void clear(Queue* q)
{
	if (NULL == q) return;
	ClearList(q->L);
	free(q);
	return;
}

//队列的输出操作
void CoutQueue(Queue* q)
{
	ListNode* p = q->L->head.next;
	for (int i = 0; i < q->count; i++)
	{
		printf("%-4d", p->data);
		p = p->next;
	}
	cout << endl;
	cout << endl;
	return;
}

int main()
{
	srand(time(0));
	Queue* q = InitQueue();
	for (int i = 0; i < 7; i++)
	{
		int op = rand() % 5;
		int val = rand() % 100;
		switch (op)
		{
		case 0:
		case 1:
		case 2:
		case 3:
			cout << "入队:" << val << endl;
			push(q, val);
			break;
		case 4:
			cout << "出队:" << front(q) << endl;
			pop(q);
			break;
		}
		CoutQueue(q);
	}
	return 0;
}

初始化

//封装链表的结构
LinkList* InitList()
{
	LinkList* L = (LinkList*)malloc(sizeof(LinkList));
	L->head.next = NULL;
	L->tail = &(L->head);
	return L;
}

//初始化操作
Queue* InitQueue()
{
	Queue* q = (Queue*)malloc(sizeof(Queue));
	q->L = InitList();
	q->count = 0;
	return q;
}

入队

//尾插法
void InsertList(LinkList* L, int val)
{
	ListNode* node = (ListNode*)malloc(sizeof(ListNode));
	node->data = val;
	node->next = NULL;

	L->tail->next = node;
	L->tail = node;
	return;
}

//入栈
void push(Queue*q,int val)
{
	InsertList(q->L, val);
	q->count += 1;
	return;
}

出队

//删除第一个节点
void EraseList(LinkList* L)
{
	ListNode* p = L->head.next;
	L->head.next = p->next;
	if (p == L->tail) L->tail = &(L->head);
	free(p);
	return;
}

//出栈
void pop(Queue* q)
{
	if (empty(q)) return;
	EraseList(q->L);
	q->count -= 1;
	return;
}

运行结果:

入队:22
22

出队:22


队列为空
出队:-1


入队:10
10

入队:65
10  65

入队:51
10  65  51

出队:10
65  51

栈(顺序表实现)

#include<iostream>
#include<time.h>

using namespace std;

//定义栈结构
typedef struct Stack
{
	int* data;//数据存储区
	int top;  //栈顶指针
	int size; //栈大小
}Stack;

//初始化栈的大小,n代表栈能容纳的数据个数
Stack* InitStack(int n)
{
	Stack* s = (Stack*)malloc(sizeof(Stack));
	s->data = (int*)malloc(sizeof(int) * n);
	s->size = n;
	s->top = -1;//由于top永远指向栈顶元素,初始时刻栈中没有元素,故为-1
	return s;
}

//判空操作
bool empty(Stack* s)
{
	return s->top == -1;
}

//查看栈顶元素
int GetTop(Stack* s)
{
	if (empty(s))
	{
		cout << "栈为空,无法查看栈顶元素" << endl;
		return -1;
	}

	return s->data[s->top];
}

//入栈操作
bool push(Stack* s, int val)
{
	if (s->top+1 == s->size)
	{
		cout << "栈满,入栈失败" << endl;
		return false;
	}

	s->top += 1;
	s->data[s->top] = val;
	return true;
}

//出栈操作
bool pop(Stack* s)
{
	if (empty(s))
	{
		cout << "栈为空,出栈失败" << endl;
		return false;
	}

	s->top -= 1;
	return true;
}

//销毁操作
void ClearStack(Stack* s)
{
	if (NULL == s)
	{
		return;//为空表示不存在这么一个栈,那么就直接返回
	}
	free(s->data);
	free(s);
	return;
}

//输出操作
void CoutStack(Stack* s)
{
	cout << "Satck(顶->底):";
	for (int i = s->top; i >= 0; i--)
	{
		printf("%-4d", s->data[i]);
	}
	cout << endl;
	cout << endl;
	return;
}

int main()
{
	srand(time(0));
	Stack* s = InitStack(4);
	for (int i = 0; i < 7; i++)
	{
		int op = rand() % 4;
		int val = rand() % 100;
		switch (op)
		{
		case 0:
		case 1:
		case 2:
			cout << "入栈:" << val << endl;
			push(s, val);
			break;
		case 3:
			cout << "出栈:" << GetTop(s) << endl;
			pop(s);
			break;
		}
		CoutStack(s);
	}
	ClearStack(s);

	return 0;
}

初始化

//初始化栈的大小,n代表栈能容纳的数据个数
Stack* InitStack(int n)
{
	Stack* s = (Stack*)malloc(sizeof(Stack));
	s->data = (int*)malloc(sizeof(int) * n);
	s->size = n;
	s->top = -1;//由于top永远指向栈顶元素,初始时刻栈中没有元素,故为-1
	return s;
}

入栈

//入栈操作
bool push(Stack* s, int val)
{
	if (s->top+1 == s->size)
	{
		cout << "栈满,入栈失败" << endl;
		return false;
	}

	s->top += 1;
	s->data[s->top] = val;
	return true;
}

出栈

//出栈操作
bool pop(Stack* s)
{
	if (empty(s))
	{
		cout << "栈为空,出栈失败" << endl;
		return false;
	}

	s->top -= 1;
	return true;
}

相关习题:

有效的括号(Leecode-20):

给定一个只包括 '('')''{''}''['']' 的字符串 s ,判断字符串是否有效。

有效字符串需满足:

  1. 左括号必须用相同类型的右括号闭合。
  2. 左括号必须以正确的顺序闭合。
  3. 每个右括号都有一个对应的相同类型的左括号。

示例:

输入:s = "()"     输出:true

输入:s = "()[]{}"    输出:true

输入:s = "(]"   输出:false

/*解题思路:遇到左括号就进栈,
            遇到右括号就和栈顶的左括号做匹配,
            如果成功,栈顶元素出栈;
            如果失败,则说明括号序列不合法。
*/

/*解题难点:①如何设计这个栈?
              用数组进行模拟
            ②如何标记左括号、右括号?
			  左括号就是(  [   {右括号就是:) } ]无需标记
			③什么情况返回true,什么情况返回false
			  1.待比较括号与栈顶括号不匹配返回false;
			  2.遍历完待比较括号但是栈内还存在括号,返回false;
			  3.其余情况返回true。
*/

class Solution 
{
public:
    bool isValid(string s) 
    {
		char str[10000] = { 0 };
		int top = 1, flag = 0;//将top设为1而不是0,是为了防止top-1越界
		for (int i = 0; i < s.length(); i++)
		{
			if (s[i] == '(' || s[i] == '{' || s[i] == '[')
			{
				str[top] = s[i];
				top += 1;
			}
			else
			{
				switch (s[i])
				{
				case ')':
					if (str[top - 1] == '(')
					{
						top -= 1;
						str[top] = 0;
					}
					else
					{
						flag = 1;
					}
					break;
				case '}':
					if (str[top - 1] == '{')
					{
						top -= 1;
						str[top] = 0;
					}
					else
					{
						flag = 1;
					}
					break;
				case ']':
					if (str[top - 1] == '[')
					{
						top -= 1;
						str[top] = 0;
					}
					else
					{
						flag = 1;
					}
					break;
				}
			}

			if (flag)
			{
				return false;
				break;
			}
		}

		for (int i = 0; i < 10000; i++)
		{
			if (str[i] != 0)
			{
				return false;
				break;
			}
		}

		return true;
    }
};

对栈的深入理解

如果将上个问题中的三种括号变为一种括号:那么问题的解决方式如下


 1.在最后一个位置上,左括号数量==右括号数量,true
 2.程序中只需要记录左括号数量和右括号数量即可

解题代码如下:

class Solution 
{
public:
    bool isValid(string s) 
    {
        int top = 0;//栈顶指针
        for (int i = 0; i < s.length(); i++)
        {
            switch (s[i])
            {
            case '(': 
                ++top;
                break;
            case ')':
                --top;
                break;
            }
            if (top >= 0)
                continue;
            return false;
        }

        return top == 0;
    }
};

总结:

①+1可以等价为【进】or【发生】,-1可以等价为【出】or【解决】

②一对()可以等价为一个完整的事件

③(())可以看作事件与事件之间的完全包含关系

再深入一点:当我们遇见这种主从结构(从整体到部分的结构)的时候,大概率就会用到栈来解决,也就是说:栈适合用来解决具有完全包含关系的问题。

程序调用关系(HZOJ-595):

 
#include<iostream>
#include<string>
#include<vector>

using namespace std;

int main()
{
	int n, flag = 0;
	cin >> n;
	vector<string>v(5), s;//v是存储函数名的数组,s是模拟栈的数组
	string goal;//待查找函数

	for (int i = 0; i < n; i++)
	{
		cin >> v[i];
	}

	cin >> goal;
	for (int i = 0; i < n; i++)
	{
		if (goal == v[i])
		{
			s.push_back(v[i]);
			flag = 1;
			break;
		}

		if (v[i] == "return")
		{
			s.pop_back();
		}
		else
		{
			s.push_back(v[i]);
		}
	}

	if (flag)
	{
		for (int i = 0; i < s.size(); i++)
		{
			if (i)  cout << "->";
			cout << s[i];
		}
		cout << endl;
	}
	else
	{
		cout << "NOT REFERENCED" << endl;
	}

	return 0;
}

2020年数据结构41题(HZOJ-838):

定义三元组(a,b, c)(a,b,c 均为正数)的距离 D=|a-b|+|b-c|+|c-a|。给定 3 个非空整数集合 S1, S2 ,S3, 按升序分别存储在 3 个数组中。请设计一个尽可能高效的算法,计算并输出所有可能的三元组(a, b, c)(a∈S1,b∈S2,c∈S3)中的最小距离。例如 S1={-1, 0, 9}, S2={-25,-10,10,11},S3={2,9,17,30,41},则最小距离为 2,相应的三元组为(9,10,9)。

输入:输入三个参数,分别为存储三个非空集合的队列

输出:返回一个整形值,表示所有可能的三元组(a, b, c)(a∈S1,b∈S2,c∈S3)中的最小距离

#include <iostream>
#include <cstdlib>
#include <queue>
using namespace std;

int min_num(int a, int b, int c) 
{
    if (a > b) swap(a, b);
    if (a > c) swap(a, c);
    return a;
}

int func(queue<int> que1, queue<int> que2, queue<int> que3) 
{
    int ans = 0x3f3f3f3f;
    while (!que1.empty() && !que2.empty() && !que3.empty())
    {
        int a = que1.front(), b = que2.front(), c = que3.front();
        int temp_ans = abs(a - b) + abs(a - c) + abs(b - c);
        if (temp_ans < ans) ans = temp_ans;
        int d = min_num(a, b, c);
        if (a == d)  que1.pop();
        if (b == d)que2.pop();
        if (c == d)que3.pop();
    }

    return ans;
}

int main() {
    int m, n, k, x;
    queue<int> que1, que2, que3;
    cin >> m >> n >> k;
    for (int i = 0; i < m; i++) 
    {
        cin >> x;
        que1.push(x);
    }
    for (int i = 0; i < n; i++)
    {
        cin >> x;
        que2.push(x);
    }
    for (int i = 0; i < k; i++) 
    {
        cin >> x;
        que3.push(x);
    }
    cout << func(que1, que2, que3) << endl;
    return 0;
}

比较含退格的字符串(Leetcode-844):

给定 s 和 t 两个字符串,当它们分别被输入到空白的文本编辑器后,如果两者相等,返回 true 。# 代表退格字符。

 chuo

#include <iostream>
#include <cstdlib>
#include <stack>
using namespace std;

class Solution
{
public:
    void pushStack(string& s, stack<char>& s1)
    {
        for (int i = 0; s[i]; i++)
        {
            if (s[i] == '#')
            {
                if (!s1.empty())
                    s1.pop();
            }
            else
                s1.push(s[i]);
        }
        return;
    }

    bool backspaceCompare(string s, string t)
    {
        stack<char>s1, s2;
        pushStack(s, s1);
        pushStack(t, s2);

        if (s1.size() != s2.size())
        {
            return false;
        }

        while (!s1.empty())
        {
            if (s1.top() != s2.top())
                return false;

            s1.pop(), s2.pop();
        }
        return true;
    }
};

火车进栈 (HZOJ-263):

有n列火车按1到n的顺序从东方左转进站,这个车站是南北方向的,它虽然无限长,只可惜是一个死胡同,而且站台只有一条股道,火车只能倒着从西方出去,而且每列火车必须进站,先进后出。

进站的火车编号顺序为 1∼n,现在请你按火车编号从小到大的顺序,输出前20种可能的出站方案。

 

#include<iostream>
#include<string>
#include<algorithm>
#include<stack>
#include<vector>

using namespace std;

bool isValid(int a[], int n)
{
	stack<int>s;
	int x = 1;//代表当前可以入栈的最小值
	for (int i = 0; i < n; i++)
	{
		//当栈顶元素小于当前元素时,当前元素可以入栈
		if (s.empty()||s.top() < a[i])
		{
			while (x <= a[i])
			{
				s.push(x);
				x += 1;
			}
		}

		if (s.empty() || s.top() != a[i])
		{
			return false;
		}

		s.pop();
	}

	return true;
}

int main()
{
	int n, a[25], count = 20;
	cin >> n;
	for (int i = 0; i < n; i++)
	{
		a[i] = i + 1;
	}

	do
	{
		//判断输出序列是否合法
		if (isValid(a, n))
		{
			for (int i = 0; i < n; i++)
			{
				cout << a[i];
			}
			cout << endl;
			count -= 1;
		}
	} while (next_permutation(a, a + n)&&count);

	return 0;
}

验证栈序列(Leetcode-946):

给定 pushed 和 popped 两个序列,每个序列中的 值都不重复,只有当它们可能是在最初空栈上进行的推入 push 和弹出 pop 操作序列的结果时,返回 true;否则,返回 false 。

class Solution 
{
public:
    bool validateStackSequences(vector<int>& pushed, vector<int>& popped) 
    {
        int x = 0;//代表入栈指针的下标
        int n = pushed.size();
        stack<int>s;
        for (int i = 0; i < n; i++)
        {
            //判断入栈的情况
            if (s.empty() || s.top() != popped[i])
            {
                while (x < pushed.size() && pushed[x] != popped[i])
                {
                    s.push(pushed[x]);
                    x += 1;
                }

                if (x == pushed.size())//说明把所有元素都压进去了
                {
                    return false;
                }

                s.push(pushed[x]);
                x += 1;
            }

            s.pop();
        }
        
        return true;
    }
};

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值