算法笔记--栈

栈是一种特殊的线性表,它只能在一个表的一个固定端进行数据结点的插入和删除操作。栈按照后进先出的原则来存储数据,也就是说,先插入的数据将被压入栈底,最后插入的数据在栈顶,读出数据时,从栈顶开始逐个读出。栈在汇编语言程序中,经常用于重要数据的现场保护。栈中没有数据时,称为空栈。入栈出栈原理图见图
算法笔记--栈

栈的实现

栈的结构就像一个集装箱,越先放进去的东西越晚才能拿出来,所以,栈常应用于实现递归功能方面的场景,例如斐波那契数列。

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

typedef struct node
{
	int nValue;
	struct node *pNext;
}MyStack;

typedef struct node2
{
	int nCount;
	MyStack *pTop;
}Stack;

void s_Init(Stack **pStack)
{
	*pStack = (Stack*)malloc(sizeof(Stack));
	(*pStack)->nCount = 0;
	(*pStack)->pTop = NULL;
}

void s_Push(Stack *pStack,int nNum)
{
	if(pStack == NULL)
	{
		printf("栈不存在!\n");
		return;
	}

	MyStack *pTemp = NULL;
	pTemp = (MyStack*)malloc(sizeof(MyStack));
	pTemp->nValue = nNum;
	
	pTemp->pNext = pStack->pTop;
	pStack->pTop = pTemp;

	pStack->nCount++;
}

int s_Pop(Stack *pStack)
{
	if(pStack == NULL || pStack->pTop == NULL)return -1;

	MyStack *pDel = NULL;
	int nNum;

	pDel = pStack->pTop;
	nNum = pDel->nValue;

	pStack->pTop = pStack->pTop->pNext;
	free(pDel);
	pDel = NULL;

	pStack->nCount--;
	return nNum;
}

void s_Clear(Stack *pStack)
{
	if(pStack == NULL)return;

	while(pStack->nCount != 0)
	{
		s_Pop(pStack);
	}
}

void s_Destroy(Stack **pStack)
{
	s_Clear(*pStack);

	free(*pStack);
	*pStack = NULL;
}

MyStack *s_GetTop(Stack *pStack)
{
	if(pStack == NULL)exit(1);

	return pStack->pTop;
}

int s_GetCount(Stack *pStack)
{
	if(pStack == NULL)exit(1);

	return pStack->nCount;
}

int s_IsEmpty(Stack *pStack)
{
	if(pStack == NULL)exit(1);

	return pStack->nCount == 0?1:0;
}


int main()
{
	Stack *pStack = NULL;
	s_Init(&pStack);

	s_Push(pStack,1);
	s_Push(pStack,2);
	s_Push(pStack,3);
	s_Push(pStack,4);

	printf("%d\n",s_Pop(pStack));
	printf("%d\n",s_Pop(pStack));
	printf("%d\n",s_Pop(pStack));
	printf("%d\n",s_Pop(pStack));

	s_Destroy(&pStack);
	s_Push(pStack,4);

	return 0;
}

有效的括号

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

有效字符串需满足:

左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
注意空字符串可被认为是有效字符串。

示例 1:

输入: “()”
输出: true
示例 2:

输入: “()[]{}”
输出: true

bool isValid(char * s){
    if (s == NULL || s[0] == '\0') return true; //差错检验
	char *stack = (char*)malloc(strlen(s)+1); //申请一个数组 模拟栈
	int top =0; //模拟栈顶指针
    for (int i = 0; s[i]; ++i) {
        if (s[i] == '(' || s[i] == '[' || s[i] == '{') 
			stack[top++] = s[i]; //如果是左边的括号  就入栈
        else {  //不是的话 就做对比
        	if ((--top) < 0)  //栈中没有元素了             
				return false;//先减减,让top指向栈顶元素
//不匹配 就返回false
            if (s[i] == ')' && stack[top] != '(') return false;
            if (s[i] == ']' && stack[top] != '[') return false;
            if (s[i] == '}' && stack[top] != '{') return false;
        }
    }//正常top最终 = 0  否则就是还有没匹配
    return (!top);//防止“【”这种类似情况
}

最长有效括号

给定一个只包含 ‘(’ 和 ‘)’ 的字符串,找出最长的包含有效括号的子串的长度。

示例 1:

输入: “(()”
输出: 2
解释: 最长有效括号子串为 “()”
“()(())” 输出:6

  int longestValidParentheses(string s) 
    {
        stack<char> vec;  //用来放左括号或者未匹配的右括号
        stack<int> num;  //存放左括号的下标
        vector<int> pos;  //存放成对括号 的两个下标
        int maxlen=0;  //最终返回值
        int len=0;  
        if(s.size()<=1) return 0;  //差错检验
        for(int i=0;i<s.size();i++)
        {
            if(!vec.empty())  //栈中有值  即有未匹配的左括号
            {
                if(vec.top()=='('&&s[i]==')')   //是匹配的()
                {
                    pos.push_back(num.top());  //讲左右括号的下标存入pos容器
                    pos.push_back(i);
                    //cout<<num.top()<<endl;
                    //cout<<i<<endl;
                    vec.pop();  //已匹配的左括号出栈
                    num.pop();  //已匹配的左括号的下标出栈
                }
                else  //和栈顶元素不匹配  
                {
                    num.push(i);  //将元素下标入栈  
                    vec.push(s[i]);  //将元素入栈
                }
            }
			else   //栈中没有元素 就将元素和元素下标入栈
            { 
                num.push(i);
                vec.push(s[i]);
            }
        }
        sort(pos.begin(),pos.end());  //将成对的括号下标排序
        for(int i=0;i<pos.size();i++)  
        {
           // cout<<pos[i]<<endl;
            if(i!=0&&pos[i]-pos[i-1]==1)   //相邻下标都有标记  
            {
                len=len+1;
                maxlen=max(len,maxlen);
            }
            else  //若有间隔  就直接将长度归1
            {
                len=1;
            }
        }
        return maxlen;
    }
};

方法2:

class Solution {
public:
    int longestValidParentheses(string s) {
        int size = s.size();
       // 一个默认为-1的用于标记的数组
        vector<int>  m(size, -1);
        // 从 1 开始 向右 遍历每个字符。
        for (int i = 1; i < size; i++) {
            // 跳过左括号
            if (s[i] == '(') { continue; }
            // 向前遍历,跳过 已经成对 括号【标记为 0 或者 2】的位置
       // 找到 标记为 -1 且 字符为 左括号的地方,将左括号标记为0,右括号标记为2
            for (int j = i - 1; j >= 0; j--) {
                if (m[j] < 0 &&  s[j] == '(') { 
                    m[j] = 0; m[i] = 2; 
					break;
                }
            }
        }
        // max为返回值, tmp记录当前匹配括号总和
        int max = 0, tmp = 0;
        // 获取连续的 0,2标记总和,并随时更新到max,如果被-1标记打断,则tmp重新开始计算新长度。
        for (auto i : m) 
        {
	        if (i >= 0) 
	        { 
	        	tmp += i;
	        } 
			else 
			{ 
				tmp = 0; 
			}
	     max = (tmp > max) ? tmp : max;
	   }
      	  return max;
    }	

二叉树的递归遍历

#include <iostream>
using namespace std;
 
typedef int TreeEntry;
 
struct TreeNode
{
	TreeEntry val;
	TreeNode* pleft;
	TreeNode* pright;
};
 
//二叉树的递归遍历
//前序
void PreOrderTraverse(TreeNode* root)
{
	if(root == NULL)
		return;
	//根
	cout<<root->val<<endl;
	//左
	PreOrderTraverse(root->pleft);
	//右
	PreOrderTraverse(root->pright);
	return;
}
 
//中序
void InOrderTraverse(TreeNode* root)
{
	if(root == NULL)
		return;
	//左
	InOrderTraverse(root->pleft);
	//根
	cout<<root->val<<endl;
	//右	
	InOrderTraverse(root->pright);
	return;
}
 
//后序
void LstOrderTraverse(TreeNode* root)
{
	if(root == NULL)
		return;
	//左
	LstOrderTraverse(root->pleft);
	//右
	LstOrderTraverse(root->pright);
	//根
	cout<<root->val<<endl;
	return;
}
 
 
int main(void)
{
	TreeNode node1;
	node1.val = 2;
	node1.pleft = NULL;
	node1.pright = NULL;
	TreeNode node2;
	node2.val = 3;
	node2.pleft = NULL;
	node2.pright = NULL;
	TreeNode root;
	root.val = 1;
	root.pleft = &node1;
	root.pright = &node2;
	cout<<"前序"<<endl;
	PreOrderTraverse(&root);
	cout<<"中序"<<endl;
	InOrderTraverse(&root);
	cout<<"后序"<<endl;
	LstOrderTraverse(&root);
	return 0;
};

非递归遍历

#include <iostream>
#include <stack>
#include <queue>
using namespace std;
 
typedef int TreeEntry;
 
struct TreeNode
{
	TreeEntry val;
	TreeNode* pleft;
	TreeNode* pright;
};
 
//二叉树的非递归遍历
 
//深度优先  前序
void DeepTravese(TreeNode * root)
{
	if(root == NULL)
		return;
	stack<TreeNode> stk;
	stk.push(*root);//根节点入栈
	while(!stk.empty())
	{
		TreeNode node = stk.top();
		stk.pop();
		cout<<node.val<<endl;
		if(node.pright != NULL) //右节点入栈
		{
			stk.push(*node.pright);
		}
		if(node.pleft != NULL) //左节点入栈
		{
			stk.push(*node.pleft);
		}
	}
}
 
int main(void)
{
	TreeNode node1;
	node1.val = 2;
	node1.pleft = NULL;
	node1.pright = NULL;
	TreeNode node2;
	node2.val = 3;
	node2.pleft = NULL;
	node2.pright = NULL;
	TreeNode root;
	root.val = 1;
	root.pleft = &node1;
	root.pright = &node2;
	DeepTravese(&root);
	LevelTravese(&root);
	return 0;
};

队列和栈

队列:先进先出 栈:先进后出

两个队列实现一个栈

queue<int> que1;   
    queue<int> que2;  // 1 2 3 4 
      
     void push(int x) {  //向非空队列后面放
       
        if(que1.empty())
        {
            que2.push(x);
        }
        else{
            que1.push(x);
        }
    }
    
    int pop() {  //非空的队列把前n-1元素顺序放到空队列   再将最后一个元素出队返回
        if(que1.empty())  
        {  
            while(que2.size() > 1){
                que1.push(que2.front());
                que2.pop();
            }
            int n = que2.front();
            que2.pop();
            return n;
        }
        else
        {
            while(que1.size() > 1){
                que2.push(que1.front());
                que1.pop();
            }
            int n = que1.front();
            que1.pop();
            return n;
        }
    }

两个栈实现一个队列

stack <int> stk1;   //1 2 3 4
    stack <int> stk2;   //存倒序的
    
    void push(int x) { //stk1中存的是正序的  所以在stk1入栈
        while(!stk2.empty()) //如果stk2中有元素(都是倒叙的),都倒在stk1中,再push
        {
            stk1.push(stk2.top());
            stk2.pop();
        }
        stk1.push(x);
    }
    
    int pop() { //返回的是第一个入栈的   stk1是1 2 3 4 所以要倒叙stk2
        while(!stk1.empty())
        {
            stk2.push(stk1.top());
            stk1.pop();
        }
        int n = stk2.top();
        stk2.pop();
        return n;
    }

删除字符串中所有相邻重复项

案例如下:
输入:“abbaca”
输出:“ca”

string removeDuplicates(string S) {
        
        if(S.empty())
            return NULL;
        stack<char> stk;
        for(int i=0;i<S.length();i++){
            if(stk.empty() || S[i]!=stk.top())  //空栈或者不匹配  入栈
            {
                stk.push(S[i]);
            }
            else  //匹配就直接出栈  说明是相邻重复   重复的都出栈了   栈中都是不相邻重复的
                stk.pop();
        }
        string s;
        while(!stk.empty())  
        {
            s+=stk.top();
            stk.pop();
        }
        reverse(s.begin(),s.end()); //ac->ca
        return s;
            
    }

自定义栈

设计包含min 函数的栈。定义栈的数据结构,要求添加一个min 函数,
能够得到栈的最小元素。要求函数min、push 以及pop 的时间复杂度都是O(1)。

struct StackItem
{
	int m_val;
	int m_min; //存到某节点为止的最小值
};
 
class MyStack
{
public:
	MyStack(int size);
	~MyStack();
public:
	StackItem* stack;
	int m_top;          //一直指向栈顶元素
public:
	void push(int value);
	int pop();
	int min();
};
 
MyStack::MyStack(int size)
{
	stack = new StackItem[size];
	for(int i=0;i<size;i++)
	{
		stack[i].m_min = 2147483647;
	}
	m_top = 0;
}
MyStack::~MyStack()
{
	delete[] stack;
	stack = NULL;
}
 
void MyStack::push(int value)
{
	m_top++;
	stack[m_top].m_val = value;
	if(m_top > 0)
	{
		if(value < stack[m_top-1].m_min)
			stack[m_top].m_min = value;
		else
		{
			stack[m_top].m_min = stack[m_top-1].m_min;	
		}
	}
	else
		stack[m_top].m_min = value;
	
}
 
int MyStack::pop()
{
	if(m_top == 0)
		return 0;
	return stack[m_top--].m_val;
}
 
int MyStack::min()
{
	if(m_top == 0)
		return 0;
	return stack[m_top].m_min;
}
上一篇:DBA如何利用strace/pstack/gdb来定位问题


下一篇:数据结构代码练习