经典算法遍历分为两部分:

  • 树的遍历
    • 深度优先遍历
      • 前序:迭代法(栈),递归法
      • 中序:迭代法(栈),递归法
      • 后序:迭代法(栈),递归法
    • 广度优先
      • 层次遍历:迭代法(队列),递归法
  • 图的遍历
    • 深度优先遍历 DFS
    • 广度优先遍历 BFS

1 树的遍历

1.1 深度优先遍历

1.1.1 前序

迭代法:

迭代法的经典步骤:

  • 1 根节点进栈
  • 2 while(栈不为空)
  • 3 取栈顶元素,弹出栈顶元素
  • 4 存取栈顶元素
  • 5 将栈顶元素的右左孩子进栈(由于栈是先进后出,所以需要先进栈右孩子,再进栈左孩子)
class Solution {
public:
    vector preorderTraversal(TreeNode* root) {
        stack<TreeNode*> st;
        vector result;
        if (root == NULL) return result;
        st.push(root);
        while (!st.empty()) {
            TreeNode* node = st.top();                       // 中
            st.pop();
            result.push_back(node->val);
            if (node->right) st.push(node->right);           // 右(空节点不入栈)
            if (node->left) st.push(node->left);             // 左(空节点不入栈)
        }
        return result;
    }
};
递归法: 经典步骤:
  • 处理自身节点
  • 递归处理左孩子节点
  • 递归处理右孩子节点
class Solution {
public:
    void traversal(TreeNode* cur, vector& vec) {
        if (cur == NULL) return;
        vec.push_back(cur->val);    // 中
        traversal(cur->left, vec);  // 左
        traversal(cur->right, vec); // 右
    }
    vector preorderTraversal(TreeNode* root) {
        vector result;
        traversal(root, result);
        return result;
    }
};

1.1.2 中序

中序的递归法不用讲,重点介绍一下迭代法。 中序的遍历顺序为左中右,所以需要BFS进入最左下角的元素,再中间,再右边。 中序遍历的经典步骤:
  • 1 根节点进栈
  • 2 while(栈不为空, cur存在),之所以这里需要两个或的判断条件,因为会出现栈为空,但是右孩子还未遍历的情况。
  • 3 如果cur不为空,则一直往左下角走
  • 4 如果cur为空,则取出栈顶元素(回溯),存取栈顶元素,然后走向右边
class Solution {
public:
    vector inorderTraversal(TreeNode* root) {
        vector result;
        stack st;
        TreeNode* cur = root;
        while (cur != NULL || !st.empty()) {
            if (cur != NULL) { // 指针来访问节点,访问到最底层
                st.push(cur); // 将访问的节点放进栈
                cur = cur->left;                // 左
            } else {
                cur = st.top(); // 从栈里弹出的数据,就是要处理的数据(放进result数组里的数据)
                st.pop();
                result.push_back(cur->val);     // 中
                cur = cur->right;               // 右
            }
        }
        return result;
    }
};

1.1.3 后序

后序的递归法依然不需要讲,只需要关注迭代法。

后序的遍历顺序为左右中,如果我们将先序的右左孩子进栈变成左右孩子进栈,则遍历顺序变成中右左,最后将遍历顺序翻转,则变成了左右中。

1.2 广度优先遍历

1.2.1 层次遍历

迭代法:使用队列,需要注意的是,队列中每层元素需要做一波处理。
vector> &result;

void BFS(ListNode *root) {
	if (root == nullptr)
		return ;
	queue que;
	que.push(root);
	while (!que.empty()) {
		vector path;
		int size = que.size();
		for (int i = 0; i < size; i++) {
			int node = que.front();
			que.pop();
			path.push_back(node->val);
			if (root->left)
				que.push(root->left);
			if (root->right)
				que.push(root->right);
		}
	}
}
递归法:每层的第一个节点,使用depth和二阶数组的size做对比,开辟一个新的该层数组。
vector<vector> &result;

void BFS(ListNode *root, int depth) {
	if (root == nullptr)
		return;
	if (depth == result.size())
		result.append(vector());
	result[depth].append(root->val);
	BFS(root->left, depth + 1);
	BFS(root->right, dpeth + 1);
}

2 图的遍历

2.1 深度优先遍历

深度优先遍历的经典步骤:
  • 1 判断是否访问过,访问过则退出,未访问则设置为访问状态。
  • 2 上下左右四个方向(for循环),先判断是否越界,如果不越界则进入下一次递归。
int dir[4][2] = {0, 1, 0, -1, 1, 0, -1, 0};

void DFS(vector<vector>& grid, vector<vector>& visited, int x, int y){
	if (visited[x][y] || grid[x][y] == '0') return ;
	visited[x][y] = true;
	for(int i=0; i<4; i++){
		int next_x = x + dir[i][0];
		int next_y = y + dir[i][1];
		if (next_x < 0 || next_x > grid.size()) || next_y < 0 || next_y > grid[0].size()) continue;
		DFS(grid, visited, next_x, next_y);
	}
}

2.2 广度优先遍历

int dir[4][2] = {0, 1, 0, -1, 1, 0, -1, 0};

void BFS(vector>& grid, vector>& visited, int x, int y){
	queue> que;
	que.push({x, y});
	visited[x][y] = true;
	while(!que.empty()){
		for(int i=0; i<4; i++){
			pair node = que.front(); que.pop();
			int next_x = x + node.first;
			int next_y = y + node.second;
			if (next_x < 0 || next_x > grid.size() || next_y < 0 || next_y > grid[0].size()) continue;
			if (grid[next_x][next_y] != '0' && !visited[next_x][next_y]){
				que.push({next_x, next_y});
				visited[next_x][next_y];
			}
		}
	}
}