【演算法篇】棧和佇列專題之廣度優先遍歷和深度優先遍歷
前言
今天要介紹棧和佇列相關演算法,棧和佇列這種資料結構相對簡單,但是結合演算法就變化莫測了,一起來看一下吧
一、棧
1、簡介
棧這種資料結構可以用陣列、線性表和連結串列等來實現,但要保證先進後出這種性質;
可能會問棧有什麼應用呢?
應用非常廣泛,像編輯器的撤銷功能,先把“操作”入棧,然後最後入棧的,先彈出,就實現撤銷功能了;
像linux核心實現的函式呼叫,也是把函式不斷入棧,然後再彈出,還有棧和遞迴和密不可分的。
2、題目
LeetCode上第20號題,題目如下:
給定一個只包含字元“(”、“)”、“{”、“}”、“[”和“]”的字串,確定輸入字串是否有效。 如果:輸入字串有效: 開括號必須用相同型別的括號括起來。 開括號必須按正確的順序關閉。 注意,空字串也被認為是有效的。 示例1: 輸入:“()” 輸出:正確 示例2: 輸入:“()(){ }” 輸出:正確 示例3: 輸入:“(]” 輸出:假 示例4: 輸入:“([))” 輸出:假 例5: 輸入:“{[]}” 輸出:正確
進行畫圖講解吧,如下圖: PS:依舊是全網最醜圖!很努力去畫,依舊很醜,看來畫圖的天賦了!
說明:
假如,[()]要入棧,規則:遇到“左邊符號”入棧,“右邊符號”彈出棧頂元素,進行比較,“()”符合要求,就是正確的,以此類推;還有一點要注意的,最後棧不是空的,說明棧裡還有“左邊符號”,這是不正確的。
3、程式碼及演示
程式碼如下:
#include <iostream> #include <stack> #include <cassert> using namespace std;View Code// 20. Valid Parentheses // https://leetcode.com/problems/valid-parentheses/description/ // 時間複雜度: O(n) // 空間複雜度: O(n) class Solution { public: bool isValid(string s) { stack<char> stack; for( int i = 0 ; i < s.size() ; i ++ ) if( s[i] == '(' || s[i] == '{' || s[i] == '[') stack.push(s[i]); else{ if( stack.size() == 0 ) return false; char c = stack.top(); stack.pop(); char match; if( s[i] == ')' ) match = '('; else if( s[i] == ']' ) match = '['; else{ assert( s[i] == '}' ); match = '{'; } if(c != match) return false; } if( stack.size() != 0 ) return false; return true; } }; int main() { if(Solution().isValid("()")) cout << "() is valid." << endl; else cout << "() is invalid." << endl; if(Solution().isValid("()[]{}")) cout << "()[]{} is valid." << endl; else cout << "()[]{} is invalid." << endl; if(Solution().isValid("(]")) cout << "(] is valid." << endl; else cout << "(] is invalid." << endl; if(Solution().isValid("([)]")) cout << "([)] is valid." << endl; else cout << "([)] is invalid." << endl; return 0; }
我對幾組資料進行了測試:()、()[]{}等,執行結果如下:
4、總結
棧的應用非常多,主要理解棧的先進後出的特性!
二、佇列
1、簡介
佇列也是一種線性資料結構,特性是先進先出;佇列有一個重要應用:廣度優先遍歷,相對於廣度還有一種深度優先遍歷,可能對於一些人還不知道廣度、深度優先遍歷,所以來解釋一下。
對於二叉樹來說:
廣度優先遍歷叫層序遍歷更貼切,一層一層來遍歷的,下面會詳細講解這種應用;
深度優先遍歷就是先序、中序和後序遍歷;
對圖來說:就分為廣度優先遍歷和深度優先遍歷了,圖這部分之後我還會詳細講解;
2、題目
LeetCode第102題,題目如下:
給定二叉樹,返回其節點值的層次順序遍歷。(例如,從左到右,逐層排列)。 例如: 給定二叉樹[3,9,20,null,null,15,7], 3 / \ 9 20 / \ 15 7 返回其水平順序遍歷如下: [ [3], [9,20], [15,7] ]
這是二叉樹典型的層序遍歷,還有二叉樹的先序、中序和後序遍歷,統稱為深度優先遍歷!不懂的老鐵可以參考這篇部落格:https://www.cnblogs.com/liudw-0215/p/9835691.html,講的很詳細。
用圖進行講解吧,圖如下:
說明:先把根節點1入隊,然後是2、3,以此類推,然後再出隊,就可以實現層序遍歷了。
3、程式碼實現
程式碼如下:
#include <iostream> #include <vector> #include <queue> #include <cassert> using namespace std; /// 102. Binary Tree Level Order Traversal /// https://leetcode.com/problems/binary-tree-level-order-traversal/description/ /// 二叉樹的層序遍歷 /// 時間複雜度: O(n), n為樹的節點個數 /// 空間複雜度: O(n) /// Definition for a binary tree node. struct TreeNode { int val; TreeNode *left; TreeNode *right; TreeNode(int x) : val(x), left(NULL), right(NULL) {} }; class Solution { public: vector<vector<int>> levelOrder(TreeNode* root) { vector<vector<int>> res; if(root == NULL) return res; queue<pair<TreeNode*,int>> q; q.push(make_pair(root, 0)); while(!q.empty()){ TreeNode* node = q.front().first; int level = q.front().second; q.pop(); if(level == res.size()) res.push_back(vector<int>()); assert( level < res.size() ); res[level].push_back(node->val); if(node->left) q.push(make_pair(node->left, level + 1 )); if(node->right) q.push(make_pair(node->right, level + 1 )); } return res; } }; int main() { return 0; }View Code
總結
棧和佇列的應用非常之多,要不斷理解它們的特性:先進後出、先進先出!喜歡的歡迎隨時點贊,不懂的歡迎隨時留言!