1、問題分析
具體思路是從邊界出發標記連通塊,標記完成後,如果中心節點有值爲1,但是仍然未被標記的說明是無法訪問的節點,即題目的答案。
2、問題解決
筆者以C++方式解決。
#include "iostream"
using namespace std;
#include "algorithm"
#include "vector"
#include "math.h"
#include "queue"
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {
}
};
class Solution {
public:
// 設置上下左右訪問點
int X[4] = { 0, 0, 1, -1 };
int Y[4] = { 1, -1, 0, 0 };
// 設置數組大小
static const int maxn = 501;
// 標記某個節點是否訪問過
int visited[maxn][maxn] = { false };
// 全局二維數組節點
vector<vector<int>> value;
// 二維數組長和寬
int m, n;
//節點結構體
struct node {
int x, y;
} Node;
// 打印訪問節點,調試使用
void print() {
cout << "--------------------> chen--------------------------" << endl;
for (int i = 0; i < value.size(); ++i) {
for (int j = 0; j < value[0].size(); ++j) {
cout << visited[i][j] << " ";
}
cout << endl;
}
}
/**
* 判斷某個節點(x,y)是否需要訪問
* @param x
* @param y
* @return
*/
bool judge(int x, int y) {
// 越界則不訪問
if (x < 0 || x >= m || y < 0 || y >= n) {
return false;
}
// 訪問過或是0節點不訪問
return !(visited[x][y] != 0 || value[x][y] == 0);
}
/**
* 其實這是深搜,將連通塊節點的 visited 標記成 true
* @param x
* @param y
*/
void bfs(int x, int y) {
queue<node> queueNode;
Node.x = x;
Node.y = y;
queueNode.push(Node);
visited[x][y] = true;
while (!queueNode.empty()) {
// 取出頭結點
node nodeFront = queueNode.front();
queueNode.pop();
// 在取出的頭結點上下左右移動
for (int i = 0; i < 4; ++i) {
int newX = nodeFront.x + X[i];
int newY = nodeFront.y + Y[i];
if (judge(newX, newY)) {
bfs(newX, newY);
}
}
}
}
/**
* 具體思路是從邊界出發標記連通塊,標記完成後,如果中心節點有值爲1,但是仍然未被標記的
* 說明是無法訪問的節點,即題目的答案
* @param A
* @return
*/
int numEnclaves(vector<vector<int>> &A) {
int ans = 0;
value = A;
m = A.size();
n = A[0].size();
// 此代碼塊的作用是從邊界出發標記連通塊
{
// 矩陣上下兩層開始標記
for (int i = 0; i < n; ++i) {
if (A[0][i] == 1 && visited[0][i] == 0) {
bfs(0, i);
}
if (A[m - 1][i] == 1 && visited[m - 1][i] == 0) {
bfs(m - 1, i);
}
}
// 矩陣左右兩層開始標記
for (int i = 0; i < m; ++i) {
if (A[i][0] == 1 && visited[i][0] == 0) {
bfs(i, 0);
}
if (A[i][n - 1] == 1 && visited[i][n - 1] == 0) {
bfs(i, n - 1);
}
}
}
for (int i = 0; i < m; ++i) {
for (int j = 0; j < n; ++j) {
if (A[i][j] == 1 && visited[i][j] == 0) {
ans++;
}
}
}
return ans;
}
};
/**
* 先序遍歷
* @param root
*/
void preorder(TreeNode *root) {
if (root == NULL || root->val == -1) {
return;
}
// 先訪問根節點
cout << root->val << endl;
// 再訪問左子節點
preorder(root->left);
// 最後訪問右子節點
preorder(root->right);
}
/**
* 根據數組創建二叉樹
* @param a
* @param n
* @return
*/
TreeNode *create(int a[], int n) {
// 根據數組大小分配內存空間
TreeNode *ptree = (TreeNode *)malloc(sizeof(TreeNode) * n);
int i;
// 根據數組的值,依次給二叉樹節點賦值
for (i = 0; i < n; i++) {
// if (a[i] == -1) {
// ptree[i] = NULL;
// continue;
// }
ptree[i].val = a[i];//數組a只起到一個賦值的作用
ptree[i].left = NULL;
ptree[i].right = NULL;
}
// 將數組中節點按照二叉樹規則連接起來
for (i = 0; i <= n / 2 - 1; i++)//原來的父親節點範圍爲1~n/2,現在0~n/2-1,所以注意n/2要取到等
{
if (2 * i + 1 <= n - 1)
ptree[i].left = &ptree[2 * i + 1];//把第2*i+1個結點的地址賦給左孩子
if (2 * i + 2 <= n - 1)
ptree[i].right = &ptree[2 * i + 2];
}
return ptree;
}
template<class T>
/**
* 獲取數組長度
* @tparam T
* @param array
* @return
*/
int getsize(T &array) {
return sizeof(array) / sizeof(array[0]);
}
int main() {
// int a[] = {5, 1, 4, -1, -1, 3, 6};
// int a[] = {2,1,3};
// int a[] = {10, 5, 15, -1, -1, 6, 20};
// int a[] = {5, 3, 6, 2, 4, -1, -1, 1};
// vector<vector<int>> A = {{0, 0, 0, 0},
// {1, 0, 1, 0},
// {0, 1, 1, 0},
// {0, 0, 0, 0}};
vector<vector<int>> A = { { 0, 1, 1, 0 },
{ 0, 0, 1, 0 },
{ 0, 0, 1, 0 },
{ 0, 0, 0, 0 } };
// 創建二叉樹
// TreeNode *pNode = create(a, getsize(a));
// preorder(pNode);
Solution *pSolution = new Solution;
int i = pSolution->numEnclaves(A);
cout << i << endl;
system("pause");
return 0;
}
運行結果
有點菜,有時間再優化一下。
3、總結
書上的代碼直接運行絕大部分是對的,但是總有一些軟件的更新使得作者無能爲力。之前的API是對的,但是之後就廢棄了或修改了是常有的事。所以我們需要跟蹤源代碼。這只是一個小小的問題,如果沒有前輩的無私奉獻,很難想象我們自己一天能學到多少內容。感謝各位前輩的辛勤付出,讓我們少走了很多的彎路!
點個贊再走唄!歡迎留言哦!