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);
}
}
}