leetcode 刷题系列(一)

230. 二叉搜索树中第K小的元素
给定一个二叉搜索树,编写一个函数 kthSmallest 来查找其中第 k 个最小的元素。
说明:
你可以假设 k 总是有效的,1 ≤ k ≤ 二叉搜索树元素个数。
示例 1:

输入: root = [3,1,4,null,2], k = 1
   3
  / \
 1   4
  \
   2
输出: 1
示例 2:
输入: root = [5,3,6,2,4,null,null,1], k = 3
       5
      / \
     3   6
    / \
   2   4
  /
 1
输出: 3

思路:根据中序遍历,bst是升序的,那么我们用一个st记录该节点的走过的第几个节点,当等于k即可第k小的答案。

class Solution {
    private int st;
    private int ans;
    private boolean flag;
    public int kthSmallest(TreeNode root, int k) {
        st=0;
        flag=false;
        ans=-1;
        dfs(root,k);
        return ans;
    }
    public void dfs(TreeNode root,int k){
        if(root==null||flag){
            return;
        }
        if(root.left!=null){
            dfs(root.left,k);
        }
        if(++st==k){
            ans=root.val;
            flag=true;
        }
        if(root.right!=null){
            dfs(root.right,k);
        }
    }
}
449. 序列化和反序列化二叉搜索树

序列化是将数据结构或对象转换为一系列位的过程,以便它可以存储在文件或内存缓冲区中,或通过网络连接链路传输,以便稍后在同一个或另一个计算机环境中重建。
设计一个算法来序列化和反序列化二叉搜索树。 对序列化/反序列化算法的工作方式没有限制。 您只需确保二叉搜索树可以序列化为字符串,并且可以将该字符串反序列化为最初的二叉搜索树。
编码的字符串应尽可能紧凑。
注意:不要使用类成员/全局/静态变量来存储状态。 你的序列化和反序列化算法应该是无状态的。

思路:利用前序遍历(前序遍历可以保持原来的树结构)遍历整棵BST.其中我们用 ’ ‘表示该节点val值的终点标志,用#表示空树,dfs一遍即可。

class Codec2 {
    public int pos;
    // Encodes a tree to a single string.
    public String serialize(TreeNode root) {
        if(root==null){
            return "#";
        }
        StringBuilder sb=new StringBuilder();
        sb.append(root.val);
        sb.append(' ');
        sb.append(serialize(root.left));
        sb.append(serialize(root.right));
        return sb.toString();
    }
    // Decodes your encoded data to tree.
    public TreeNode deserialize(String data) {
        pos=0;
        return dfs(data);
    }
    public TreeNode dfs(String data){
        if(data.charAt(pos)=='#'){
            ++pos;
            return null;
        }
        int val=0;
        while (data.charAt(pos)!=' '){
            val=val*10+(data.charAt(pos)-'0');
            ++pos;
        }
        ++pos;
        TreeNode root=new TreeNode(val);
        root.left=dfs(data);
        root.right=dfs(data);
        return root;
    }
    public static void main(String[] args) {
        TreeNode root=new TreeNode(2);
        root.left=new TreeNode(1);
        root.right=new TreeNode(3);
        String s=new Codec2().serialize(root);
        TreeNode r=new Codec2().deserialize(s);
    }
}
450. 删除二叉搜索树中的节点
给定一个二叉搜索树的根节点 root 和一个值 key,删除二叉搜索树中的 key 对应的节点,并保证二叉搜索树的性质不变。返回二叉搜索树(有可能被更新)的根节点的引用。

一般来说,删除节点可分为两个步骤:

首先找到需要删除的节点;
如果找到了,删除它。
说明: 要求算法时间复杂度为 O(h),h 为树的高度。

示例:

root = [5,3,6,2,4,null,7]
key = 3

    5
   / \
  3   6
 / \   \
2   4   7

给定需要删除的节点值是 3,所以我们首先找到 3 这个节点,然后删除它。

一个正确的答案是 [5,4,6,2,null,null,7], 如下图所示。

    5
   / \
  4   6
 /     \
2       7

另一个正确答案是 [5,2,6,null,4,null,7]。

    5
   / \
  2   6
   \   \
    4   7

若要删除一个BST的一个结点,需要考虑如下三种情况:
需要删除的节点下并没有其他子节点
需要删除的节点下有一个子节点(左或右)
需要删除的节点下有两个子节点(既左右节点都存在)
对这三种情况分别采取的措施是:
直接删除此结点
删除此结点,将此结点父节点连接到此结点左(右)子树
找出此结点右子树中的最小结点,用以代替要删除的结点,然后删除此最小结点(或者右子树的最大节点,即第二种情况)

class Solution {
    public TreeNode deleteNode(TreeNode root, int key) {
        return dfs(root,key);
    }
    public TreeNode dfs(TreeNode root,int key){
        if(root==null){
            return null;
        }
        if(key<root.val){
            root.left=dfs(root.left,key);
        }else if(key>root.val){
            root.right=dfs(root.right,key);
        }else{
            if(root.left==null||root.right==null){ // 存在至多一个子节点
                root=(root.left==null)?root.right:root.left;
            }else{ //两个子节点都存在
                TreeNode cur=root.right;
                while (cur.left!=null){ 
                    cur=cur.left;
                }
                root.val=cur.val;
                root.right=deleteNode(root.right,cur.val);        
            }
        }
        return root;
    }
}
329. 矩阵中的最长递增路径

给定一个整数矩阵,找出最长递增路径的长度。
对于每个单元格,你可以往上,下,左,右四个方向移动。 你不能在对角线方向上移动或移动到边界外(即不允许环绕)。

记忆化搜索,我们记录下每次走过的点,由该点延伸出去的能走最大的长度。
然后枚举矩阵所有的点,找出最长的路径。之前走过的状态都保留着,
所以越到后面的dfs就可以直接得出答案。

class Solution {
    int[] dx={0,0,1,-1};
    int[] dy={1,-1,0,0};
    boolean[][] vis;
    int n,m;
    int[][] path;
    public int longestIncreasingPath(int[][] matrix) {
        if(matrix.length==0||matrix[0].length==0){
            return 0;
        }
        int ans=0;
        n=matrix.length;
        m=matrix[0].length;
        vis=new boolean[n][m];
        path=new int[n][m];
        for(int i=0;i<n;++i){
            for(int j=0;j<m;++j){
                ans=Math.max(dfs(i,j,matrix,path),ans);
            }
        }
        return ans;
    }
    boolean check(int x,int y){
        if(x<0||y<0||x>=n||y>=m){
            return false;
        }
        return true;
    }
    //保留路径的长度
    public int dfs(int x,int y,int[][] matrix,int[][] path){
        if(vis[x][y]){
            return path[x][y];
        }
        path[x][y]=1;
        for(int i=0;i<4;++i){
            int xi=x+dx[i];
            int yi=y+dy[i];
            if(check(xi,yi)&&matrix[xi][yi]<matrix[x][y]){
                path[x][y]=Math.max(path[x][y],dfs(x+dx[i],y+dy[i],matrix,path)+1);
            }
        }
        vis[x][y]=true;
        return path[x][y];
    }
}
160. 相交链表

题目描述提示帮助提交记录社区讨论阅读解答
随机一题
编写一个程序,找到两个单链表相交的起始节点。
例如,下面的两个链表:
A: a1 → a2

c1 → c2 → c3

B: b1 → b2 → b3
在节点 c1 开始相交。
注意:
如果两个链表没有交点,返回 null.
在返回结果后,两个链表仍须保持原有的结构。
可假定整个链表结构中没有循环。
程序尽量满足 O(n) 时间复杂度,且仅用 O(1) 内存。

  • 先记录a,b的长度la,lb,且记录a,b的最后一个节点,若相等,相交。
  • 让长(假设la长)的链表先走la-lb步,再同一起走,知道curA==curB,即为相交节点。
public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        if(headA==null||headB==null){
            return null;
        }
        int lenA=1;
        ListNode curA=headA;
        while (curA.next!=null){
            ++lenA;
            curA=curA.next;
        }
        int lenB=1;
        ListNode curB=headB;
        while (curB.next!=null){
            ++lenB;
            curB=curB.next;
        }
        if(curA!=curB){
            return null;
        }
        curA=headA;
        curB=headB;
        int t;
        if(lenA>lenB){
            t=lenA-lenB;
            while (--t>=0){
                curA=curA.next;
            }
        }else{
            t=lenB-lenA;
            while (--t>=0){
                curB=curB.next;
            }
        }
        while (true){
            if(curA==curB){
                return curA;
            }else{
                curA=curA.next;
                curB=curB.next;
            }
        }
    }
}
14. 最长公共前缀

编写一个函数来查找字符串数组中的最长公共前缀。
如果不存在公共前缀,返回空字符串 “”。

以第一个串为外循环,记录匹配的最后一个位置,暴力即可。

class Solution {
    public String longestCommonPrefix(String[] strs) {
        if(strs.length==0){
            return "";
        }
        int i,j;
        int lsindx=0;
        for(i=0;i<strs[0].length();++i){
            for(j=1;j<strs.length;++j){
                if((strs[j].length()-1)<i||strs[j].charAt(i)!=strs[0].charAt(i)){
                    break;
                }
            }
            if(j!=strs.length){
                return strs[0].substring(0,i);
            }else{
                lsindx=i+1;
            }
        }
        return strs[0].substring(0,lsindx);
    }
}
22. 括号生成

给出 n 代表生成括号的对数,请你写出一个函数,使其能够生成所有可能的并且有效的括号组合。
例如,给出 n = 3,生成结果为:
[
“((()))”,
“(()())”,
“(())()”,
“()(())”,
“()()()”
]

思路:dfs即可。l代表可以添加多少个’(‘,r代表可以添加多少个’)’.
注意一下添加”)”的条件即可,添加“)”必须是前面已经有“(”才可以添加,判断条件就是r!=0&&r>l.

class Solution {
    public List<String> generateParenthesis(int n) {
        List<String> ans=new ArrayList<String>();
        dfs(n,n,ans,"");
        return ans;
    }
    public void dfs(int l,int r,List<String> ans,String s){
        if(l==0&&r==0){
            ans.add(s);
            return;
        }
        if(l!=0){
            dfs(l-1,r,ans,s+"(");
        }
        if(r!=0&&r>l){
            dfs(l,r-1,ans,s+")");
        }
    }
}
67. 二进制求和

模拟即可。

class Solution {
    public String addBinary(String a, String b) {
        if(a==null||b==null){
            return a==null?b:a;
        }
        StringBuilder sb=new StringBuilder();
        int m=a.length();
        int n=b.length();
        if(m<n){
            String tmp=b;
            b=a;
            a=tmp;
        }
        int t=Math.abs(m-n);
        int ex=0;
        int ad;
        for(int i=a.length()-1;i>=0;--i){
            if(i-t>=0){
                ad=a.charAt(i)-'0'+b.charAt(i-t)-'0'+ex;
            }else{
                ad=a.charAt(i)-'0'+ex;
            }
            if(ad>=2){
                sb.append(ad%2);
                ex=1;
            }else{
                sb.append(ad);
                ex=0;
            }
        }
        if(ex==1){
            sb.append(1);
        }
        return sb.reverse().toString();
    }
}   
841. 钥匙和房间

有 N 个房间,开始时你位于 0 号房间。每个房间有不同的号码:0,1,2,…,N-1,并且房间里可能有一些钥匙能使你进入下一个房间。
在形式上,对于每个房间 i 都有一个钥匙列表 rooms[i],每个钥匙 rooms[i][j] 由 [0,1,…,N-1] 中的一个整数表示,其中 N = rooms.length。 钥匙 rooms[i][j] = v 可以打开编号为 v 的房间。
最初,除 0 号房间外的其余所有房间都被锁住。
你可以自由地在房间之间来回走动。
如果能进入每个房间返回 true,否则返回 false。
示例 1:
输入: [[1],[2],[3],[]]
输出: true
解释:
我们从 0 号房间开始,拿到钥匙 1。
之后我们去 1 号房间,拿到钥匙 2。
然后我们去 2 号房间,拿到钥匙 3。
最后我们去了 3 号房间。
由于我们能够进入每个房间,我们返回 true。
示例 2:
输入:[[1,3],[3,0,1],[2],[0]]
输出:false
解释:我们不能进入 2 号房间。
提示:
1 <= rooms.length <= 1000
0 <= rooms[i].length <= 1000
所有房间中的钥匙数量总计不超过 3000。

dfs,对每个房间遍历一遍记录找到的钥匙,对找到的钥匙继续遍历即可。

class Solution {
    boolean[] vis;
    int[] key;
    public boolean canVisitAllRooms(List<List<Integer>> rooms) {
        key=new int[rooms.size()];
        vis=new boolean[rooms.size()];
        dfs(0,rooms);
        for(int i=0;i<vis.length;++i){ //如果有一个没参观过即为false
            if(!vis[i]){
                return false;
            }
        }
        return true;
    }
    public void dfs(int pos,List<List<Integer>> rooms){
        if(vis[pos]){
            return;
        }
        vis[pos]=true;
        List<Integer> re=rooms.get(pos);
        for(int i=0;i<re.size();++i){ //找钥匙
            key[re.get(i)]=1;
        }
        for(int i=0;i<key.length;++i){ //遍历可以走的门
            if(key[i]==1){
                dfs(i,rooms);
            }
        }
    }
}
200. 岛屿的个数

给定一个由 ‘1’(陆地)和 ‘0’(水)组成的的二维网格,计算岛屿的数量。一个岛被水包围,并且它是通过水平方向或垂直方向上相邻的陆地连接而成的。你可以假设网格的四个边均被水包围。
示例 1:
输入:
11110
11010
11000
00000
输出: 1
示例 2:
输入:
11000
11000
00100
00011
输出: 3

dfs,当该点为1而且没有访问过,岛屿数+1,接着就遍历该岛屿,上下左右搜索,把该岛屿连接的所有1都
标记为访问过。(同一个岛屿)

class Solution {
    boolean[][] vis;
    int n,m;
    int[] dx={0,0,1,-1};
    int[] dy={1,-1,0,0};
    public int numIslands(char[][] grid) {
        if(grid.length==0||grid[0].length==0){
            return 0;
        }
        n=grid.length;
        m=grid[0].length;
        vis=new boolean[n][m];
        int ans=0;
        for(int i=0;i<n;++i){
            for(int j=0;j<m;++j){
                if(!vis[i][j]&&grid[i][j]=='1'){
                    ++ans;
                    dfs(i,j,grid);
                }
            }
        }
        return ans;
    }
    public void dfs(int x,int y,char[][] grid){
        if(x<0||y<0||x>=n||y>=m||vis[x][y]||grid[x][y]=='0'){
            return;
        }
        vis[x][y]=true;
        for(int i=0;i<4;++i){
            dfs(x-dx[i],y-dy[i],grid);
        }
    }
}
130. 被围绕的区域

给定一个二维的矩阵,包含 ‘X’ 和 ‘O’(字母 O)。
找到所有被 ‘X’ 围绕的区域,并将这些区域里所有的 ‘O’ 用 ‘X’ 填充。
示例:
X X X X
X O O X
X X O X
X O X X
运行你的函数后,矩阵变为:
X X X X
X X X X
X X X X
X O X X
解释:
被围绕的区间不会存在于边界上,换句话说,任何边界上的 ‘O’ 都不会被填充为 ‘X’。 任何不在边界上,或不与边界上的 ‘O’ 相连的 ‘O’ 最终都会被填充为 ‘X’。如果两个元素在水平或垂直方向相邻,则称它们是“相连”的。

这道题与上一题类似。
不过我们这次不找被包围的的路径,我们找不被包围的路径。
所以我们可以从四个边界开始找,把经过的O用*替换。
这样我们把所有不被包围的O都标记处理了。
最后将O变成X,将*变成O,注意顺序。

class Solution {
    int[] dx={0,0,1,-1};
    int[] dy={1,-1,0,0};
    int n,m;
    public void solve(char[][] board) {
        if(board.length==0||board[0].length==0){
            return;
        }
        int ls=0;
        int le=board.length-1;
        int rs=0;
        int re=board[0].length-1;
        n=le;
        m=re;
        for(int i=ls;i<=le;++i){
            if(board[i][rs]=='O'){
                dfs(i,rs,board);
            }
            if(board[i][re]=='O'){
                dfs(i,re,board);
            }
        }
        for(int i=rs;i<=re;++i){
            if(board[ls][i]=='O'){
                dfs(ls,i,board);
            }
            if(board[le][i]=='O'){
                dfs(le,i,board);
            }
        }
        for(int i=0;i<=n;++i){
            for(int j=0;j<=m;++j){
                if(board[i][j]=='O'){
                    board[i][j]='X';
                }
                if(board[i][j]=='#'){
                    board[i][j]='O';
                }
            }
        }
    }
    public void dfs(int x,int y,char[][] board){
        if(x<0||y<0||x>n||y>m||board[x][y]=='X'||board[x][y]=='#'){
            return;
        }
        board[x][y]='#';
        for(int i=0;i<4;++i){
            dfs(x+dx[i],y+dy[i],board);
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值