3.16力扣刷题记录

1. I. 数组中数字出现的次数(位运算)

题目描述
一个整型数组 nums 里除两个数字之外,其他数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)。
难度:中等
示例

输入:nums = [1,2,10,4,1,4,3,3]
输出:[2,10] 或 [10,2]

题目地址
题解

class Solution {
public:
    vector<int> singleNumbers(vector<int>& nums) {
        int xOr=0;
        for(auto num:nums)
            xOr^=num;
        int div=1;
        while((xOr&div)==0)
            div<<=1;
        int a=xOr,b=xOr;
        for(auto num:nums){
            if((num&div)==0)
                a^=num;
            else
                b^=num;
        }
        return {a,b};
    }
};

异或的性质:两个相同的数异或的结果为0,任何一个数跟0异或的结果都是它本身。
通过性质,我们可以很快知道,如果在数组中,如果只有一个数出现一次,其余数均出现两次,那么对数组中的数做一遍异或,就可以得到这个数。
如果有两个数都出现了一次,考虑将数组分组,把这两个数分到不同的组,并且组中其余数仍然成对出现,对这两个新的数组做异或,就可以得到这两个数。
回到本题,首先,对原数组做一遍异或,就可以得到这两个单独出现的数异或的结果。因为两个数不相同,所以他们异或的二进制数的结果中,至少有一位为1,这意味着,这两个数其中一个该位为1,另一个为0。我们就可以按该数位是否为1,将原来的数组分成两组。再对这两组数分别做异或,就可以得到单独的两个数。
<题解参考力扣官方题解>

2.地图分析(多源dfs)

题目描述
你现在手里有一份大小为 n x n 的 网格 grid,上面的每个 单元格 都用 0 和 1 标记好了。其中 0 代表海洋,1 代表陆地。
请你找出一个海洋单元格,这个海洋单元格到离它最近的陆地单元格的距离是最大的,并返回该距离。如果网格上只有陆地或者海洋,请返回 -1。
我们这里说的距离是「曼哈顿距离」( Manhattan Distance):(x0, y0) 和 (x1, y1) 这两个单元格之间的距离是 |x0 - x1| + |y0 - y1| 。
题目难度:中等
示例
在这里插入图片描述
题目地址

题解

class Solution {
    int dirs[4][2]={{1,0},{-1,0},{0,1},{0,-1}};
public:
    int maxDistance(vector<vector<int>>& grid) {
        queue<pair<int,int>> q;
        int n=grid.size();
        vector<vector<bool>> _grid(n,vector<bool>(n,false));
        for(int i=0;i<n;i++)
            for(int j=0;j<n;j++)
                if(grid[i][j]==1){
                    q.push(make_pair(i,j));
                    _grid[i][j]=true;
                }
        int m=q.size();
        if(m==n*n||m==0)
            return -1;
        while(!q.empty()){
            int size=q.size();
            for(int k=0;k<size;k++){
                int x=q.front().first,y=q.front().second;
                q.pop();
                int i,j;
                for(auto dir:dirs){
                    i=x+dir[0],j=y+dir[1];
                    if(i>=0&&i<n&&j>=0&&j<n&&_grid[i][j]==false){
                        grid[i][j]=grid[x][y]+1;
                        q.push(make_pair(i,j));
                        _grid[i][j]=true;
                    }
                }
            }
        }
        int ans=0;
        for(int i=0;i<n;i++)
            for(int j=0;j<n;j++)
                ans=max(ans,grid[i][j]);
        return ans-1;
    }
    
};

其余多源bfs题:01矩阵地图中的最高点

3.另一个树的子树(bfs,kmp)

题目描述
给你两棵二叉树 root 和 subRoot 。检验 root 中是否包含和 subRoot 具有相同结构和节点值的子树。如果存在,返回 true ;否则,返回 false 。
二叉树 tree 的一棵子树包括 tree 的某个节点和这个节点的所有后代节点。tree 也可以看做它自身的一棵子树。
难度:简单??
示例
在这里插入图片描述
题目地址
题解
1.bfs

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    bool isSubtree(TreeNode* root, TreeNode* subRoot) {
        if(root==NULL)
            return false;
        if(dfs(root,subRoot))
            return true;
        else
            return isSubtree(root->left,subRoot)||isSubtree(root->right,subRoot);
    }
    bool dfs(TreeNode* tree1,TreeNode* tree2){
        if(tree1==NULL&&tree2==NULL)
            return true;
        else if(tree1==NULL||tree2==NULL)
            return false;
        else if(tree1->val!=tree2->val)
            return false;
        return dfs(tree1->left,tree2->left)&&dfs(tree1->right,tree2->right);
    }
};

2.kmp

class Solution {
public:
    vector <int> sOrder, tOrder;
    int maxElement, lNull, rNull;

    void getMaxElement(TreeNode *o) {
        if (!o) {
            return;
        }
        maxElement = max(maxElement, o->val);
        getMaxElement(o->left);
        getMaxElement(o->right);
    }

    void getDfsOrder(TreeNode *o, vector <int> &tar) {
        if (!o) {
            return;
        }
        tar.push_back(o->val);
        if (o->left) {
            getDfsOrder(o->left, tar);
        } else {
            tar.push_back(lNull);
        }
        if (o->right) {
            getDfsOrder(o->right, tar);
        } else {
            tar.push_back(rNull);
        }
    }

    bool kmp() {
        int sLen = sOrder.size(), tLen = tOrder.size();
        vector <int> fail(tOrder.size(), -1);
        for (int i = 1, j = -1; i < tLen; ++i) {
            while (j != -1 && tOrder[i] != tOrder[j + 1]) {
                j = fail[j];
            }
            if (tOrder[i] == tOrder[j + 1]) {
                ++j;
            }
            fail[i] = j;
        }
        for (int i = 0, j = -1; i < sLen; ++i) {
            while (j != -1 && sOrder[i] != tOrder[j + 1]) {
                j = fail[j];
            }
            if (sOrder[i] == tOrder[j + 1]) {
                ++j;
            }
            if (j == tLen - 1) {
                return true;
            }
        }
        return false;
    }

    bool isSubtree(TreeNode* s, TreeNode* t) {
        maxElement = INT_MIN;
        getMaxElement(s);
        getMaxElement(t);
        lNull = maxElement + 1;
        rNull = maxElement + 2;

        getDfsOrder(s, sOrder);
        getDfsOrder(t, tOrder);

        return kmp();
    }
};

作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/subtree-of-another-tree/solution/ling-yi-ge-shu-de-zi-shu-by-leetcode-solution/
来源:力扣(LeetCode)

kmp题解来自官方,将两个树的先序遍历转化为字符串,再比较第二个是否为第一个的子串。暴力比较时间复杂度为O(m*n),kmp算法优化到O(m+n)。
计算next数组:

void getNext(string s, int* next, int length) {
	int j = 0, k = -1;
	next[0] = -1;
	while (j < length - 1) {
		if (k == -1 || s[j] == s[k]) {
			k++;
			j++;
			next[j] = k;
		}
		else
			k = next[k];
	}
}

匹配:

int pos = -1;
for (int i = 0; i < length; i++) {
	while (pos != -1 && s[pos + 1] != s0[i]) {
		pos = next[pos];
	}
	if (s[pos + 1] == s0[i])
		pos++;
}

代码来自数据结构课程作业。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值