b_leetcode——栈、队列、堆(含VS建立解决方案)

VS建立解决方案

文件——>新建——>项目——>其他项目类型——>Visual Studio解决方案——>空白解决方案

之后在这个空白解决方法中 添加——>新建项目就可以了

一般C++的项目的话会建立两个文件,一个是.h文件,这个是定义相关的类的,一个是.cpp文件,这个是实现相关的类里面的方法的。

b_leetcode1——使用队列实现栈

题目:设计一个栈,支持基本的栈操作,这个栈的内部存储数据的结构为队列,队列的方法只能包括push、peek(front)、pop、size、empty等标准的队列方法。

思路:

首先需要明确的是:基本的栈操作一共包括五个方法:empty()、size()、top()、pop()、push(x)。下面的思路复杂度都是O(n)。

方法一:

在这里插入图片描述

对于我们要完成的操作,也就是栈来说,刚来的那个数据c是要压入到栈的顶端的,但是由于底层的数据结构是队列,所以

  • 先把这个将要压入的数据放入到一个临时队列中去
  • 然后将之前的数据从开头的元素一个一个压入到临时队列中的c的后面
  • 最后将这个临时的队列从开头的元素一个一个压入到真正的队列中去
  • 例如原来的数据中已经有4321,如果把5 push进入5队列中去,正常push的顺序是43215,但是这显然是队列queue的规则,不是栈的方法,我们需要的是54321,所以要先将5放入到一个临时队列中去,然后把4321从头开始一个一个push到5的后面,最后将这个54321的队列一个一个push到原始的队列中去,就完成了用队列实现栈的功能。

方法二(思路可取,但是没有相应的pop()方法):

一开始我的思路是想先把原来的数据push到临时队列中,如4321已经在原来的队列中了,但是如果正常push的话,就先push1234,从后面一个个push,最后oush 5,变成12345,最后再从后面push,54321这样。

这个办法行不通的地方在于S.back()方法可以取到最后一个,但是把最后一个弹出来在接着取出倒数第二个,就做不到,而且又不能取到队列的任意的元素,所以完不成相应的操作。

Mystach.h

#pragma once
#include <queue>

class Mystack {
public:
	Mystack();

	int size();

	bool empty();

	int top();

	void pop();

	void push(int x);

private:
	std::queue<int> _data;
};
    

Mystack.cpp

#include "Mystack.h"

Mystack::Mystack() {}

void  Mystack::push(int x) {
	std::queue<int> _temp_queue;
	_temp_queue.push(x);
	while (!_data.empty()) {
		_temp_queue.push(_data.front());
		_data.pop();
	}

	while (!_temp_queue.empty()) {
		_data.push(_temp_queue.front());
		_temp_queue.pop();
	}

}

int Mystack::size() {
	return _data.size();
}

bool Mystack::empty() {
	return _data.empty();
}

void Mystack::pop() {
	_data.pop();
}

int Mystack::top() {
	return _data.front();
}

main

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include "Mystack.h"
using namespace std;
int main()
{

	Mystack Mystack;
	Mystack.push(1);
	Mystack.push(2);
	Mystack.push(3);
	Mystack.push(4);
	cout << Mystack.top() << endl;
	Mystack.pop();
	cout << Mystack.top() << endl;

	system("pause");
	return 0;
}

b_leetcode2——使用栈实现队列

题目:设计一个队列,支持基本的队列操作,这个队列的内部存储数据结构为栈,栈的方法只能包括push、top、pop、size、empty等标准的栈方法。

思路:首先明确是我们要完成队列的六个基本方法:

队列的基本属性:

  • size()、empty()

队列的基本操作:

  • front()、back()、pop()、push()

方法(复杂度为O(n)):

最核心的仍然是实现push的操作。如在已经实现队列的基础上继续压入,如果进行正常的push操作,会把这个数据push到队列的最前面,这显然不是我们想要的方法。因此

  • 先将原来的数据从顶开始一个一个push到一个临时栈中,如原来的数据是1234,经过push,将数据push到临时栈去,这个时候会变成4321。
  • 然后将我们要push的数据push到这个临时栈去,比如会变成54321。
  • 最后将54321再push到我们的数据队列中去,最终变成了12345。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bslR88mO-1613634540271)(C:\Users\wl\AppData\Roaming\Typora\typora-user-images\image-20210216204228872.png)]

Myqueue.h

#pragma once
#include <stack>

class Myqueue {
public:
	Myqueue();

	int size();
	
	bool empty();

	void push(int x);

	void pop();

	int front();

private:
	std::stack<int> _data;

};

Myqueue.cpp

#include "Myqueue.h"

Myqueue::Myqueue(){
}


void Myqueue::push(int x) {
	std::stack<int> _temp_stack;
	while (!_data.empty()) {
		_temp_stack.push(_data.top());
		_data.pop();
	}

	_temp_stack.push(x);
	while (!_temp_stack.empty()) {
		_data.push(_temp_stack.top());
		_temp_stack.pop();
	}

}

int Myqueue::size() {
	return _data.size();
}

bool Myqueue::empty() {
	return _data.empty();
}

void Myqueue::pop() {
	_data.pop();
}

int Myqueue::front() {
	return _data.top();
}

main.c

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include "Myqueue.h"
using namespace std;
int main()
{
	Myqueue Myqueue;
	Myqueue.push(1);
	Myqueue.push(4);
	Myqueue.push(5);
	Myqueue.push(78);

	Myqueue.pop();

	cout << Myqueue.front() << endl;
	system("pause");
	return 0;
}

b_leetcode3——包含min函数的栈

设计一个栈,支持如下操作,这些操作的算法复杂度需要是常数级,O(1)。

  • push(x):将元素x压入栈中
  • pop():弹出(移除)栈顶元素
  • top():返回栈顶元素
  • getMin():返回栈内最小元素

思路:

首先需要明确的是,正常的栈有五个基本方法

  • 自身的属性方法
    • size()
    • empty()
  • 自身的操作方法
    • top()
    • pop()
    • push()
  • 此外这里还加了一个最小值的方法
    • getMin()

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nqYEnslL-1613634540273)(C:\Users\wl\AppData\Roaming\Typora\typora-user-images\image-20210217095709399.png)]

思路:我们建立一个新的栈来存储最小值,这个栈和数据栈保持一致,每次数据push进入数据栈的时候,这个最小值栈栈顶为数据栈的最小值,不断的更新,保持与其数据栈size相同

  • 建立两个栈,一个为数据栈data,一个为最小值的栈min
  • 当数据来的时候,如果两个栈均为空,就把这个数据均push到这两个栈中去
  • 如果栈不为空,首先push进入数据栈data中,同时还要和最小值栈min的栈顶进行比较
    • 如果这个数据比min的栈顶大,那么min栈仍然需要保持其栈顶的数据,这个时候把min的栈顶push进入就可以了
    • 如果这个数据比min的栈顶小,那么把这个数据同时push进入min栈,这个时候min栈的栈顶就会一直是最小值了
  • 同时这里还需要注意的是,当数据栈data弹出一个值的时候,最小值栈也要弹出相应的值

Minstack.h

#pragma once
#include <stack>

class Minstack {
public:
	Minstack();

	int size();

	bool empty();

	void push(int x);

	void pop();

	int top();

	int getMin();

private:
	std::stack<int> _data;
	std::stack<int> min_stack;
};

Minstack.cpp

#include "Minstack.h"

Minstack::Minstack() {
}

void Minstack::push(int x) {
	if (_data.empty()) 
	{
		_data.push(x);
		min_stack.push(x);
	}
	else
	{
		_data.push(x);
		if (x > min_stack.top()) 
		{
			x = min_stack.top();
		}
		min_stack.push(x);
	}
}

int Minstack::size() {
	return _data.size();
}

bool Minstack::empty() {
	return _data.empty();
}

void Minstack::pop() {
	_data.pop();
	min_stack.pop();
}

int Minstack::top() {
	return _data.top();
}

int Minstack::getMin() {
	return min_stack.top();
}

main.cpp

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include "Minstack.h"
using namespace std;

int main()
{
	Minstack Minstack;
	Minstack.push(3);
	Minstack.push(9);
	Minstack.push(8);
	Minstack.push(4);
	Minstack.push(7);

	cout << Minstack.getMin() << endl;

	system("pause");
	return 0;
}

b_leetcode4——合法的出栈序列

已知从1至n的数字序列,按顺序入栈,每个数字入栈后即可出栈,也可在栈中停留,等待后面的数字入栈出栈后,该数字再出栈,求该数字序列的出栈序列是否合法。

思路:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CKZIlxYL-1613634540274)(C:\Users\wl\AppData\Roaming\Typora\typora-user-images\image-20210217110103402.png)]

这个思路就是按照给定的需要测试的顺序进行模拟出栈和入栈的过程的正确性。具体操作如下:

  • 12345为正确的出入栈序列,中间可以入栈后再进行出栈,顺序不定
  • 测试32541是否为正确的出入栈顺序
  • 首先将1压入栈中,这个时候开始比较栈的顶部和这个队列的顶部的元素是否相等
    • 如果这两个数据相等
      • 同时弹出,然后将2压入栈中,压入之后比较第二个元素是否相等。如果相等,则弹出,压入3,不断的重复此过程,直到把所有的数据都压入栈
    • 如果这两个数不相等
      • 压入下一个数据,这个时候再次比较栈顶的元素和队列的顶部的元素是否相等,如果相等,同时弹出,重复上面的过程,如果不相等,则压入下一个数据,再次比较栈顶和队列首个数。不断的重复此过程
  • 需要注意的是,每次比较的都是栈顶和队列的首个字符,最终需要比较的是看栈是否为空,如果栈为空的话,就说明出栈顺序不合法。

legal_order.h

#pragma once
#include <stack>
#include <queue>

bool check_is_valid_order(std::queue<int> &legal_order);

legal_order.cpp

#include "legal_order.h"

bool check_is_valid_order(std::queue<int> &legal_order) {
	std::stack<int> temp_stack;
	int n = legal_order.size();
	for (int i = 1; i <= n; i++) {
		temp_stack.push(i);
		while (!temp_stack.empty() && legal_order.front() == temp_stack.top()) {
			temp_stack.pop();
			legal_order.pop();
		}
	}
	if (!temp_stack.empty()) {
		return false;
	}
	return true;
}

legal_order_test.cpp

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
#include <sstream>
#include "legal_order.h"
using namespace std;
int main() {
	int value[10];
	for (int i = 1; i <= 5; i++) {
		cout << "请输入第" << i << "个整数:" << endl;
		cin >> value[i];
	}

	queue<int> legal_order;
	legal_order.push(value[1]);
	legal_order.push(value[2]);
	legal_order.push(value[3]);
	legal_order.push(value[4]);
	legal_order.push(value[5]);
	bool flag = check_is_valid_order(legal_order);
	cout<<flag << endl;

	system("pause");
	return 0;
}

b_leetcode5——数组中第K大的数

题目:已知一个未排序的数组,求这个数组中第K大的数字。

如array = [3,2,1,5,6,4],K = 2,return 5

思路

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RgxTrh2V-1613703123654)(C:\Users\wl\AppData\Roaming\Typora\typora-user-images\image-20210218190622033.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8Vb4JIR1-1613703123656)(C:\Users\wl\AppData\Roaming\Typora\typora-user-images\image-20210218191135547.png)]

其实这个思路很简单:就是建立一个K大小的最小堆,vector中来一个数字,如果来一个数字,如果比这个堆顶大,那么就放入这个堆里面,比堆顶小,那就不放进去。总之一个K大小的最小堆,比堆顶大,扔进去,比堆顶小,就不做任何处理。

比如一个array = [3,2,1,5,6,4],找其中第2大的数字

  • 首先建立一个长度为2的最小堆,把最开始的两个元素push进入这个最小堆中去。如现在是2——>3
  • 然后开始第三个元素的一个个的push进去,如果比堆顶的元素小,则不做任何的处理,如1比2小,则不会进入堆中。
  • 如果元素比堆顶的元素大,比如5比2大,则进入这个最小堆中,并把堆顶给pop()掉,此时堆中的数据是3——>5。
  • 这样遍历完全所有vector中的数据以后,这个堆中就是vector中最大的两个数,堆顶就是第二大的数字。

findkthlargest.h

#pragma once
#include <vector>
#include <queue>
#include <functional>

int findkthlargest(std::vector<int> &nums,int k);

findkthlargest.cpp

#include "findkthlargest.h"


int findkthlargest(std::vector<int> &nums, int k) {
	std::priority_queue<int, std::vector<int>, std::greater<int>> min_heap;
	for (int i = 0; i < nums.size(); i++) {
		if (min_heap.size() < 2) {
			min_heap.push(nums[i]);
		}
		else if (nums[i] > min_heap.top()) {
			min_heap.pop();
			min_heap.push(nums[i]);
		}
	}
	return min_heap.top();
}

findkthlargest_test.cpp

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include "findkthlargest.h"
using namespace std;
int main()
{
	std::vector<int> nums;
	nums.push_back(3);
	nums.push_back(2);
	nums.push_back(1);
	nums.push_back(5);
	nums.push_back(6);
	nums.push_back(4);
	int kthlargest = findkthlargest(nums, 2);
	cout << kthlargest << endl;
	system("pause");
	return 0;
}

b_leetcode6——寻找中位数

题目:设计一个数据结构,该数据结构动态维护一维数组,且支持如下操作:

1、添加元素:void addNum(int num),将整型num添加至数据结构中。

2、返回数据的中位数:double findMedian(),返回其维护的数据的中位数。

中位数定义:

1、若数据个数为奇数,中位数是该组数排序后中间的数。【1,2,3】 ——>2

2、若数据为偶数,中位数是该组数排序后中间两个数字的平均数【1,2,3,4】——>2.5

思路:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cQYM0wF2-1613703123658)(C:\Users\wl\AppData\Roaming\Typora\typora-user-images\image-20210218215413197.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eEyf3lH3-1613703123661)(C:\Users\wl\AppData\Roaming\Typora\typora-user-images\image-20210218220105327.png)]

第一步:下面首先说添加元素的方法的情况void addNum(int num)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-O9Ocbzkd-1613703123663)(C:\Users\wl\AppData\Roaming\Typora\typora-user-images\image-20210218220850377.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Vmjbewt4-1613703123665)(C:\Users\wl\AppData\Roaming\Typora\typora-user-images\image-20210218221014160.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zFlPWSJH-1613703123665)(C:\Users\wl\AppData\Roaming\Typora\typora-user-images\image-20210218221114541.png)]

第二步是获取中位数的方法:double findMedian()

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gP5EHSvk-1613703123666)(C:\Users\wl\AppData\Roaming\Typora\typora-user-images\image-20210218221513602.png)]

总的来说就是:

对于添加数据:

维护两个堆,一个最大堆,里面存放数据中最小的那一半,一个最小堆,里面存放的是最大的一半数据,保证添加数据两个堆的长度之差不超过1.

第一种情况:当两个堆的数据长度一样的时候,进来一个数据

  • 如果小于最大堆的top,直接进入最大堆即可
  • 如果大于最大堆的top,直接进入最小堆

第二种情况:当最大堆的长度比最小堆多一个的时候,这个时候要调整他们两个长度一致

  • 如果进来的数据大于最大堆的top,直接进入最小堆即可
  • 如果新元素小于最大堆的top,那么将最大堆的top那个数据push进入最小堆,然后把这个数据pop()掉。最后将新元素添加至最大堆
  • 最终这连个堆的长度就保持了一致

第三种情况:当最大堆的长度比最小堆少一个的时候,还是要调整数据保持一致。

  • 如果新元素大小小于最大堆的top,直接进入最大堆即可
  • 如果新元素大小大于最大堆的top,首先讲最小堆的top这个数据push进入最大堆,然后pop()掉,将新元素push进入最小堆。
  • 最终这两个堆的长度就保持了一致

对于寻找中位数

当这两个最大堆和最小堆建立完成以后,寻找中位数就分为以下几种情况。

  • 第一种情况:当两个堆长度一致的时候,中位数就是两个堆的top相加的平均数
  • 第二种情况:当最大堆的长度小于最小堆,中位数就是最小堆的top()
  • 第三种情况:当最大堆的长度大于最小堆,中位数就是最大堆的top()

MedianFinder.h

#pragma once
#include <iostream>
#include <queue>
#include <functional>
class MedianFinder {
public:
	MedianFinder();
	void addNum(double number);
	double medianFinder();

private:
	std::priority_queue<double, std::vector<double>, std::less<double>> big_heap;
	std::priority_queue<double, std::vector<double>, std::greater<double>> small_heap;
};

MedianFinder.cpp

#include "MedianFinder.h"


MedianFinder::MedianFinder() {
}

void MedianFinder::addNum(double number) {
	if (big_heap.empty()) {
		big_heap.push(number);
	}
	else if ( big_heap.size() == small_heap.size() )
	{
		if (number < big_heap.top()) {
			big_heap.push(number);
		}
		else
		{
			small_heap.push(number);
		}
	}
	else if (big_heap.size()>small_heap.size())
	{
		if (number > big_heap.top()) {
			small_heap.push(number);
		}
		else
		{
			small_heap.push(big_heap.top());
			big_heap.pop();
			big_heap.push(number);
		}
	}
	else if( big_heap.size()<small_heap.size())
	{
		if (number < big_heap.top()) {
			big_heap.push(number);
		}
		else
		{
			big_heap.push(small_heap.top());
			small_heap.pop();
			small_heap.push(number);
		}
	}

}


double MedianFinder::medianFinder() {
	if (big_heap.empty()) {
		std::cout << "还没有添加任何数据" << std::endl;
		return 0;
	}
	else if(big_heap.size()==small_heap.size())
	{
		return (big_heap.top() + small_heap.top()) / 2;
	}
	else if (big_heap.size()>small_heap.size())
	{
		return big_heap.top();
	}
	else if (big_heap.size()<small_heap.size())
	{
		return small_heap.top();
	}
}

MedianFinder_test.cpp

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include "MedianFinder.h"
using namespace std;
int main()
{
	MedianFinder M;
	int test[] = { 6,10,1,7,99,4,33 };
	for (int i = 0; i < 7; i++) {
		M.addNum(test[i]);
		cout << M.medianFinder() << endl;
	}
	system("pause");
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_32651847/article/details/113848001