文章目录
知识点总结
set判断元素是否存在 set.contains();
返回空数组 return null return new int[0]; return new int[]{};
返回新创建的非空数组 return new int[]{i,map.get(target-nums[i])};
数组元素放入map
getOrDefault:当Map集合中有这个key时,就使用这个key对应的value值,如果没有这个key就使用默认值defaultValue
for (int i = 0; i < s.length(); ++i) {
char ch = s.charAt(i);
frequency.put(ch, frequency.getOrDefault(ch, 0) + 1);
}
取栈顶元素,也是需要判定栈是否为空的,否则会爆出空栈异常
判断栈是否为空:stack.isEmpty();
if(!stack.isEmpty()&&stack.peek()=='[') stack.pop();
总结:注意实例化队列不能用 Queuequeue=new Queue();
也不能用Queuequeue=new Deque();因为Queue()和Deque()都是抽象类,不能实例化,而是用的Queuequeue=new LinkedList();
队列添加和移除元素 add remove
用递归做二叉树的题的时候,可以先想想如果只有一颗二叉树,一个根节点和它的左右节点,你会怎么处理
2021.07.28(第1天)数组
1 217 存在重复的元素
class Solution {
public boolean containsDuplicate(int[] nums) {
Set<Integer>set=new HashSet();
for(int i=0;i<nums.length;i++)
{
if(set.contains(nums[i]))
{
return true;
}
set.add(nums[i]);
}
return false;
}
}
class Solution {
public boolean containsDuplicate(int[] nums) {
Arrays.sort(nums);
int n = nums.length;
for (int i = 0; i < n - 1; i++) {
if (nums[i] == nums[i + 1]) {
return true;
}
}
return false;
}
}
作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/contains-duplicate/solution/cun-zai-zhong-fu-yuan-su-by-leetcode-sol-iedd/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
2 53. 最大子序和
class Solution {
public int maxSubArray(int[] nums) {
int dp[]=new int [nums.length];
dp[0]=nums[0];
int max=dp[0];
for(int i=1;i<nums.length;i++)
{
dp[i]=Math.max(nums[i],dp[i-1]+nums[i]);
max=Math.max( dp[i],max);
}
return max;
}
}
2021.07.29 (第2天)数组
3 1. 两数之和
class Solution {
public int[] twoSum(int[] nums, int target) {
Map<Integer,Integer>map=new HashMap();
for(int i=0;i<nums.length;i++)
{
if(map.containsKey(target-nums[i]))
{
return new int[]{i,map.get(target-nums[i])};
}
map.put(nums[i],i);
}
return new int[]{};
}
}
4 88. 合并两个有序数组
最开始想的是从前往后插入,一次比较,但是num1前面的元素不好处理啊
还有一个方法就是重新创建一个大小为m+n的数组,但是这样太low了,看了帖子发现可以从后往前插入。。。。。
class Solution {
public void merge(int[] nums1, int m, int[] nums2, int n) {
int p1=m-1;int p2=n-1;
int curr=m+n-1;
while(p2>=0&&p1>=0)
{
if(nums1[p1]>=nums2[p2])//p1指向的数大于等于p2执行的数
{
nums1[curr--]=nums1[p1];
p1--;
}
else
{
nums1[curr--]=nums2[p2];
p2--;
}
}
//看看是谁先走完了
if(p1<0)
{
while(p2>=0)
{
nums1[curr--]=nums2[p2];
p2--;
}
}
else{
while(p1>=0)
{
nums1[curr--]=nums1[p1];
p1--;
}
}
}
}
直接合并后排序?????
class Solution {
public void merge(int[] nums1, int m, int[] nums2, int n) {
for (int i = 0; i != n; ++i) {
nums1[m + i] = nums2[i];
}
Arrays.sort(nums1);
}
}
作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/merge-sorted-array/solution/he-bing-liang-ge-you-xu-shu-zu-by-leetco-rrb0/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
这个比我的简洁
class Solution {
public void merge(int[] nums1, int m, int[] nums2, int n) {
int p1 = m - 1, p2 = n - 1;
int tail = m + n - 1;
int cur;
while (p1 >= 0 || p2 >= 0) {
if (p1 == -1) {
cur = nums2[p2--];
} else if (p2 == -1) {
cur = nums1[p1--];
} else if (nums1[p1] > nums2[p2]) {
cur = nums1[p1--];
} else {
cur = nums2[p2--];
}
nums1[tail--] = cur;
}
}
}
作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/merge-sorted-array/solution/he-bing-liang-ge-you-xu-shu-zu-by-leetco-rrb0/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
2021.07.30(第3天) 数组
5 121. 买卖股票的最佳时机
思路:找到当前天数之前的最小值,用今天的价格减去之前的最小值(就是假设在最小值处买入),这样,直到遍历到最后一天,求得利润最大值
class Solution {
public int maxProfit(int[] prices) {
int min=prices[0];
int max=0;
for(int i=1;i<prices.length;i++)
{
min=Math.min(min,prices[i]);
max=Math.max(prices[i]-min,max);
}
return max;
}
}
6 350. 两个数组的交集 II
思路:把一个数组的元素全部存进map,map的key为数字,value为该数字出现的次数,重复出现的数字就次数++
然后用数组2来匹配map的key,如果匹配,就把这个匹配的值加入 list,同时,这个map中被匹配的数的次数减1,下次再匹配到,但是map中对应的value为0的话,就不加入list了。
class Solution {
public int[] intersect(int[] nums1, int[] nums2) {
Map<Integer,Integer>map=new HashMap();
List<Integer>list=new ArrayList();
for(int i=0;i<nums1.length;i++)
{
if(map.containsKey(nums1[i]))
{
map.put(nums1[i],map.get(nums1[i])+1);
}
else
{
map.put(nums1[i],1);
}
}
for(int i=0;i<nums2.length;i++)
{
if(map.containsKey(nums2[i])&&map.get(nums2[i])!=0)
{
list.add(nums2[i]);
map.put(nums2[i],map.get(nums2[i])-1);//相当于取出来一个,把map中元素对应的个数减一
}
}
int[] arr = new int [list.size()];
// list.toArray(arr);
for(int j=0;j<list.size();j++) arr[j]=list.get(j);
return arr;
}
}
官方还是官方啊,思路差不多,但是代码简洁
class Solution {
public int[] intersect(int[] nums1, int[] nums2) {
if (nums1.length > nums2.length) {
return intersect(nums2, nums1);
}
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
for (int num : nums1) {
int count = map.getOrDefault(num, 0) + 1;
map.put(num, count);
}
int[] intersection = new int[nums1.length];
int index = 0;
for (int num : nums2) {
int count = map.getOrDefault(num, 0);
if (count > 0) {
intersection[index++] = num;
count--;
if (count > 0) {
map.put(num, count);
} else {
map.remove(num);
}
}
}
return Arrays.copyOfRange(intersection, 0, index);
}
}
作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/intersection-of-two-arrays-ii/solution/liang-ge-shu-zu-de-jiao-ji-ii-by-leetcode-solution/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
思路2:排序+双指针
如果两个数组是有序的,则可以使用双指针的方法得到两个数组的交集。
首先对两个数组进行排序,然后使用两个指针遍历两个数组。
初始时,两个指针分别指向两个数组的头部。每次比较两个指针指向的两个数组中的数字,如果两个数字不相等,则将指向较小数字的指针右移一位,如果两个数字相等,将该数字添加到答案,并将两个指针都右移一位。当至少有一个指针超出数组范围时,遍历结束。
class Solution {
public int[] intersect(int[] nums1, int[] nums2) {
Arrays.sort(nums1);
Arrays.sort(nums2);
int length1 = nums1.length, length2 = nums2.length;
int[] intersection = new int[Math.min(length1, length2)];
int index1 = 0, index2 = 0, index = 0;
while (index1 < length1 && index2 < length2) {
if (nums1[index1] < nums2[index2]) {
index1++;
} else if (nums1[index1] > nums2[index2]) {
index2++;
} else {
intersection[index] = nums1[index1];
index1++;
index2++;
index++;
}
}
return Arrays.copyOfRange(intersection, 0, index);
}
}
作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/intersection-of-two-arrays-ii/solution/liang-ge-shu-zu-de-jiao-ji-ii-by-leetcode-solution/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
2021.07.31(第4天) 数组
7 566. 重塑矩阵
思路:直接把原矩阵拉成一维的list,再依次放入新矩阵
class Solution {
public int[][] matrixReshape(int[][] mat, int r, int c) {
int m=mat.length; int n=mat[0].length;
if(m*n!=r*c) return mat;//没有办法转化,输出原矩阵
int convert[][]=new int[r][c];
List<Integer>list=new ArrayList();
for(int i=0;i<m;i++)
{
for(int j=0;j<n;j++)
{
list.add(mat[i][j]);
}
}
int count=0;
for(int i=0;i<r;i++)
{
for(int j=0;j<c;j++)
{
convert[i][j]=list.get(count++);
}
}
return convert;
}
}
思路2:映射
class Solution {
public int[][] matrixReshape(int[][] nums, int r, int c) {
int m = nums.length;
int n = nums[0].length;
if (m * n != r * c) {
return nums;
}
int[][] ans = new int[r][c];
for (int x = 0; x < m * n; ++x) {
ans[x / c][x % c] = nums[x / n][x % n];
}
return ans;
}
}
8 118. 杨辉三角
思路,找规律,变声是1,中间的元素的值为list.get(i-1).get(j-1)+list.get(i-1).get(j)
class Solution {
public List<List<Integer>> generate(int numRows) {
List<List<Integer>> list=new ArrayList();
for(int i=0;i<numRows;i++)
{
list.add(new ArrayList());//这里不add的话,下边get就会报错哦
for(int j=0;j<=i;j++)
{
if(j==0||j==i) list.get(i).add(1);
else list.get(i).add(list.get(i-1).get(j-1)+list.get(i-1).get(j));
}
}
return list;
}
}
2021.08.01(第5天) 数组
9 36. 有效的数独
10 73. 矩阵置零
解题思路:两个标记数组分别记录每一行和每一列是否有零出现。
具体地,我们首先遍历该数组一次,如果某个元素为 000,那么就将该元素所在的行和列所对应标记数组的位置置为 true\text{true}true。最后我们再次遍历该数组,用标记数组更新原数组即可
class Solution {
public void setZeroes(int[][] matrix) {
int m = matrix.length, n = matrix[0].length;
boolean[] row = new boolean[m];
boolean[] col = new boolean[n];
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (matrix[i][j] == 0) {
row[i] = col[j] = true;
}
}
}
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (row[i] || col[j]) {
matrix[i][j] = 0;
}
}
}
}
}
常量空间的思路,就是用原来数组的第一行和第一列代替我们用来标记的数组空间,
先用两个常量标记第一行和第一列是否有0,
然后遍历数组除了第一行和第一列的部分,如果出现0,就把对应的i,j映射到第一行和第一列去置零
然后再遍历一次除了第一行和第一列的部分把第一行和第一列置为0的对应的i,j的地方都置为0
然后判断原来的标志位,加入原来第一行有0,像现在就把第一行全部置0,加入原来第一行没有0,就不用改了,应为应该改成0的地方现在已经是0了
class Solution {
public void setZeroes(int[][] matrix) {
int row = matrix.length;
int col = matrix[0].length;
boolean row0_flag = false;
boolean col0_flag = false;
// 第一行是否有零
for (int j = 0; j < col; j++) {
if (matrix[0][j] == 0) {
row0_flag = true;
break;
}
}
// 第一列是否有零
for (int i = 0; i < row; i++) {
if (matrix[i][0] == 0) {
col0_flag = true;
break;
}
}
// 把第一行第一列作为标志位
for (int i = 1; i < row; i++) {
for (int j = 1; j < col; j++) {
if (matrix[i][j] == 0) {
matrix[i][0] = matrix[0][j] = 0;
}
}
}
// 置0
for (int i = 1; i < row; i++) {
for (int j = 1; j < col; j++) {
if (matrix[i][0] == 0 || matrix[0][j] == 0) {
matrix[i][j] = 0;
}
}
}
if (row0_flag) {
for (int j = 0; j < col; j++) {
matrix[0][j] = 0;
}
}
if (col0_flag) {
for (int i = 0; i < row; i++) {
matrix[i][0] = 0;
}
}
}
}
2021.08.02(第6天) 字符串
11 387. 字符串中的第一个唯一字符
思路一:遍历字符串,存储每一个字符出现的次数,之后再遍历一次字符串,找到第一个频次为1的数
思路二:Map存储字符和索引,如果多次出现,就把索引设置为1,最后遍历哈希表,找到索引是1的字符
思路3 :用队列
class Solution {
public int firstUniqChar(String s) {
Map<Character,Integer>map=new HashMap();
for(int i=0;i<s.length();i++)
{
if(map.containsKey(s.charAt(i)))
map.put(s.charAt(i),map.get(s.charAt(i))+1);
else
map.put(s.charAt(i),1);
}
for(int i=0;i<s.length();i++)
{
if(map.get(s.charAt(i))==1)
return i;
}
return -1;
}
}
用数组会快很多
class Solution {
public int firstUniqChar(String s) {
int[] arr = new int[26];
int n = s.length();
for (int i = 0; i < n; i++) {
arr[s.charAt(i)-'a']++ ;
}
for (int i = 0; i < n; i++) {
if (arr[s.charAt(i)-'a'] == 1) {
return i;
}
}
return -1;
}
}
12 383. 赎金信
思路:用两个Map分别统计两个字符串总每个字符出现的频次
然后遍历第一个字符串,比较它在两个字符串中出现频次的大小关系
需要注意的是,map的get方法,如果key不存在的话,会报空指针异常,所以需要先用containsKey判断是否存在,存在再使用get方法取出value
class Solution {
public boolean canConstruct(String ransomNote, String magazine) {
Map<Character,Integer>map1=new HashMap();
Map<Character,Integer>map2=new HashMap();
for(int i=0;i<ransomNote.length();i++)
{
char ch=ransomNote.charAt(i);
map1.put(ch,map1.getOrDefault(ch,0)+1);
}
for(int i=0;i<magazine.length();i++)
{
char ch=magazine.charAt(i);
map2.put(ch,map2.getOrDefault(ch,0)+1);
}
for(int i=0;i<ransomNote.length();i++) {
char ch=ransomNote.charAt(i);
if(!map2.containsKey(ch))
return false;
if(map1.get(ch)>map2.get(ch))
return false;
}
return true;
}
}
这个用数组的思路就快很多
class Solution {
public boolean canConstruct(String ransomNote, String magazine) {
//记录杂志字符串出现的次数
int[] arr = new int[26];
int temp;
for (int i = 0; i < magazine.length(); i++) {
temp = magazine.charAt(i) - 'a';
arr[temp]++;
}
for (int i = 0; i < ransomNote.length(); i++) {
temp = ransomNote.charAt(i) - 'a';
//对于金信中的每一个字符都在数组中查找
//找到相应位减一,否则找不到返回false
if (arr[temp] > 0) {
arr[temp]--;
} else {
return false;
}
}
return true;
}
}
13 242. 有效的字母异位词
解题思路:用一个数组存放26个小写字母的出现次数,对于第一个字符串,对每一个出现的字母做次数加法
对于第二个字符串,对每一个出现的字母做次数减法
最后,如果他们是字母异位词,那么,数组的每一个数都应该是0
class Solution {
public boolean isAnagram(String s, String t) {
int arr[]=new int[26];
for(int i=0;i<s.length();i++)
{
char ch=s.charAt(i);
int num=ch-'a';
arr[num]++;
}
for(int i=0;i<t.length();i++)
{
char ch=t.charAt(i);
int num=ch-'a';
arr[num]--;
}
for(int i=0;i<26;i++)
{
if(arr[i]!=0) return false;
}
return true;
}
}
思路2:排序之后比较,确实把我秀到了
class Solution {
public boolean isAnagram(String s, String t) {
if (s.length() != t.length()) {
return false;
}
char[] str1 = s.toCharArray();
char[] str2 = t.toCharArray();
Arrays.sort(str1);
Arrays.sort(str2);
return Arrays.equals(str1, str2);
}
}
2021.08.03 (第7天) 链表
14 141. 环形链表
思路:定义快慢指针,同时从头结点出发,快指针比慢指针每次快一步,慢指针每次走一步,开指针每次走2步,
如果链表没有环,则快指针走到终点,程序结束
如果链表有环,则慢指针会追上快指针
思考,快指针为什么走2步,而不是其他步数?
参考:为什么快指针速度一般为慢指针的两倍?
如果你是高倍的话,那么快指针将会飞快地到达第一个环,而慢指针则会很慢才能到达第一个环。中间这段过程中快指针在第一个环内将会无意义高速移动。
请问在查找单链表环时那个快的指针的步长为什么是2?3倍4倍?
为什么用快慢指针找链表的环,快指针和慢指针一定会相遇?
详解为什么用一步两步快慢指针?三步四步可以吗
public class Solution {
public boolean hasCycle(ListNode head) {
ListNode fast=head;
ListNode slow=head;
while(fast!=null&&fast.next!=null)
{
fast=fast.next.next;
slow=slow.next;
if(fast==slow)
return true;
}
return false;
}
}
思路2:使用hash表存储访问过的结点,如果结点被重复访问,说明有环
public class Solution {
public boolean hasCycle(ListNode head) {
Set<ListNode>set=new HashSet();
while(head!=null){
if(!set.add(head))
return true;
head=head.next;
}
return false;
}
}
扩展
求环形链表的入环点
public class Solution {
public ListNode detectCycle(ListNode head) {
ListNode fast=head;
ListNode slow=head;
while(fast!=null&&fast.next!=null)
{
fast=fast.next.next;
slow=slow.next;
if(slow==fast){
fast=head;
while(slow!=fast){//找到相遇结点之后,让fast指向头结点,然后fast和slow同时移动,相遇的结点即为环的入口
slow=slow.next;
fast=fast.next;
}
return fast;
}
}
return null;
}
}
思路2:用hash表,遍历结点,hash表中第一个重复的结点即为入环点
public class Solution {
public ListNode detectCycle(ListNode head) {
Set<ListNode>set=new HashSet();
while(head!=null)
{
if(!set.add(head))
return head;
head=head.next;
}
return null;
}
}
求环的长度?
从相遇点开始,假设慢指针有走了半圈,那么快指针走了一圈又回到最开始的相遇点
如果慢指针再走半圈,慢指针回到了最开始的相遇点,而快指针则又走了一圈回到相遇点
此时两指针再次相遇
所以从相遇点开始,两指针再次相遇时,慢指针走过的距离即为环的长度
public class Solution {
public ListNode detectCycle(ListNode head) {
ListNode fast=head;
ListNode slow=head;
while(fast!=null&&fast.next!=null)
{
fast=fast.next.next;
slow=slow.next;
if(slow==fast){
int length=1;
fast=fast.next.next;
slow=slow.next;
while(slow!=fast){//找到相遇结点之后,让fast指向头结点,然后fast和slow同时移动,相遇的结点即为环的入口
length++;
slow=slow.next;
fast=fast.next.next;
}
System.out.println(length);
// return fast;
}
}
return null;
}
}
///获取链表环长
55 int getRingLength(LinkNode *ringMeetNode){
56 int RingLength=0;
57 LinkNode *fast = ringMeetNode;
58 LinkNode *slow = ringMeetNode;
59 for(;;){
60 fast = fast->next->next;
61 slow = slow->next;
62 RingLength++;
63 if(fast == slow)
64 break;
65 }
66 return RingLength;
67 }
15 21. 合并两个有序链表
class Solution {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
if(l1==null) return l2;
if(l2==null) return l1;
ListNode head1=l1;
ListNode head2=l2;
ListNode head;
head=l1.val<=l2.val?l1:l2;
if(head==l1) l1=l1.next;
else l2=l2.next;
ListNode temp=head;
while(l1!=null||l2!=null){
if(l1==null) {
temp.next=l2;
break;
}
if(l2==null){
temp.next=l1;
break;
}
if(l1.val<=l2.val)
{
temp.next=l1;
l1=l1.next;
}
else
{
temp.next=l2;
l2=l2.next;
}
temp=temp.next;
}
return head;
}
}
官方简洁到我要崩溃
class Solution {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
ListNode prehead = new ListNode(-1);
ListNode prev = prehead;
while (l1 != null && l2 != null) {
if (l1.val <= l2.val) {
prev.next = l1;
l1 = l1.next;
} else {
prev.next = l2;
l2 = l2.next;
}
prev = prev.next;
}
// 合并后 l1 和 l2 最多只有一个还未被合并完,我们直接将链表末尾指向未合并完的链表即可
prev.next = l1 == null ? l2 : l1;
return prehead.next;
}
}
这个递归我服气
class Solution {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
if (l1 == null) {
return l2;
} else if (l2 == null) {
return l1;
} else if (l1.val < l2.val) {
l1.next = mergeTwoLists(l1.next, l2);
return l1;
} else {
l2.next = mergeTwoLists(l1, l2.next);
return l2;
}
}
}
作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/merge-two-sorted-lists/solution/he-bing-liang-ge-you-xu-lian-biao-by-leetcode-solu/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
16 203. 移除链表元素
class Solution {
public ListNode removeElements(ListNode head, int val) {
ListNode head2=new ListNode(-1);
ListNode temp=head2;
while(head!=null){
if(head.val!=val) {
temp.next=head;
temp=temp.next;
}
else{
temp.next=null;//为了避免最后一个结点无法处理,因为我们上面是遇到val相同的直接跳过
//但是对于[1,2,6,3,4,5,6]这种例子,6还是会接在5的后面,[1,2,6,3,4,5,6,6,6,6]这种后面的6都没办法处理
}
head=head.next;
}
return head2.next;
}
}
当我把while循环去掉,按理说只是添加了1这个结点到temp后面,但是我们不要忘记了,1的后面也是有结点的
当我把else去掉,为什么只返回了1?
这是因为不加else的话,每一次temp这个引用把当前head指针指向的结点的下一个结点给置空了,,,,
思路2 递归
class Solution {
public ListNode removeElements(ListNode head, int val) {
if (head == null) {
return head;
}
head.next = removeElements(head.next, val);
return head.val == val ? head.next : head;
}
}
作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/remove-linked-list-elements/solution/yi-chu-lian-biao-yuan-su-by-leetcode-sol-654m/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
class Solution {
public ListNode removeElements(ListNode head, int val) {
ListNode dummy = new ListNode(-1);
dummy.next = head;
dfs(dummy, dummy.next, val);
return dummy.next;
}
void dfs(ListNode prev, ListNode root, int val) {
if (root == null) return ;
if (root.val == val) {
prev.next = root.next;
} else {
prev = root;
}
dfs(prev, prev.next, val);
}
}
作者:AC_OIer
链接:https://leetcode-cn.com/problems/remove-linked-list-elements/solution/gong-shui-san-xie-yi-chu-lian-biao-yuan-ca6fu/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
迭代思路2
class Solution {
public ListNode removeElements(ListNode head, int val) {
ListNode dummyHead = new ListNode(0);
dummyHead.next = head;
ListNode temp = dummyHead;
while (temp.next != null) {
if (temp.next.val == val) {
temp.next = temp.next.next;
} else {
temp = temp.next;
}
}
return dummyHead.next;
}
}
作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/remove-linked-list-elements/solution/yi-chu-lian-biao-yuan-su-by-leetcode-sol-654m/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
2021.08.04(第8天) 链表
17 83. 删除排序链表中的重复元素
遍历链表,如果该节点的val值与新链表的尾节点的val值不相等,就把它加入到新的链表中
class Solution {
public ListNode deleteDuplicates(ListNode head) {
ListNode temp=new ListNode(101);
ListNode temp2=temp;
while(head!=null){
if(head.val!=temp2.val){
temp2.next=head;
temp2=temp2.next;
}
else{
temp2.next=null;
}
head=head.next;
}
return temp.next;
}
}
18 206. 反转链表
思路1:非原地反转,利用了o(n)的额外空间
class Solution {
public ListNode reverseList(ListNode head) {
ListNode temp=new ListNode(0);
// ListNode temp2=temp;
ListNode head2=head;
while(head!=null){
ListNode next=temp.next;//暂存新链表的第二个节点
temp.next=new ListNode(head.val);
temp.next.next=next;
head=head.next;
}
return temp.next;
}
}
递归思路:
提示,把长链表切分成头结点和子链表酱紫递归
当只有一个节点的时候,直接返回。
当有两个节点的时候
head.next.next=head;
head.next=null;
推荐 leetcode206/剑指 Offer 24. 反转链表
2021.08.05(第9天) 栈 / 队列
19 20. 有效的括号
总结:取栈顶元素,也是需要判定栈是否为空的,否则会爆出空栈异常
判断栈是否为空:stack.isEmpty();
if(!stack.isEmpty()&&stack.peek()=='[') stack.pop();
class Solution {
public boolean isValid(String s) {
Stack<Character>stack=new Stack();
for(int i=0;i<s.length();i++){
if(s.charAt(i)==')'){
if(!stack.isEmpty()&&stack.peek()=='(') stack.pop();
else stack.push(s.charAt(i));
}
else if(s.charAt(i)=='}'){
if(!stack.isEmpty()&&stack.peek()=='{') stack.pop();
else stack.push(s.charAt(i));
}
else if(s.charAt(i)==']'){
if(!stack.isEmpty()&&stack.peek()=='[') stack.pop();
else stack.push(s.charAt(i));
}
else
stack.push(s.charAt(i));
}
return stack.isEmpty();
}
}
修改了一下,遇到不匹配直接返回false
class Solution {
public boolean isValid(String s) {
Stack<Character>stack=new Stack();
for(int i=0;i<s.length();i++){
if(s.charAt(i)==')'){
if(!stack.isEmpty()&&stack.peek()=='(') stack.pop();
else return false;
}
else if(s.charAt(i)=='}'){
if(!stack.isEmpty()&&stack.peek()=='{') stack.pop();
else return false;
}
else if(s.charAt(i)==']'){
if(!stack.isEmpty()&&stack.peek()=='[') stack.pop();
else return false;
}
else
stack.push(s.charAt(i));
}
return stack.isEmpty();
}
}
20 232. 用栈实现队列
解题思路,用两个辅助栈实现队列的先入先出功能
假设现在有1 2 3 4 5
我们把他们都压入stack,弹出顺序就是5 4 3 2 1 ,弹出的时候我们把弹出的元素压入stack2
那么stack2的入栈殊顺序是5 4 3 2 1 ,弹出顺序就是1 2 3 4 5 实现了队列的功能
即封装成一个队列的话,压入元素时,压入stack,弹出元素时,先从stack弹出到stack2,在从stack2再从stack2弹出元素
但是实际情况要求可以随时压入元素和弹出元素
第一步:实现压入元素,直接压入stack
第二步:弹出元素,当stack2不为空的时候,直接弹出stack2的栈顶元素,当stack2为空,就把stack里面的元素弹出到stack2,在弹出栈顶元素
第三步:实现peek,就是跟第二步思路差不多,只不过不用弹出栈顶元素,而是返回栈顶元素
第四步:判空,当两个栈都为空的时候,说明队列为空
class MyQueue {
/** Initialize your data structure here. */
Stack<Integer>stack=new Stack();
Stack<Integer>stack2=new Stack();
public MyQueue() {
}
/** Push element x to the back of queue. */
public void push(int x) {
stack.push(x);
}
/** Removes the element from in front of queue and returns that element. */
public int pop() {
if(!stack2.isEmpty())
return stack2.pop();
else{
while(!stack.isEmpty())
stack2.push(stack.pop());
return stack2.pop();
}
}
/** Get the front element. */
public int peek() {
if(!stack2.isEmpty())
return stack2.peek();
else{
while(!stack.isEmpty())
stack2.push(stack.pop());
return stack2.peek();
}
}
/** Returns whether the queue is empty. */
public boolean empty() {
if(stack.isEmpty()&&stack2.isEmpty())
return true;
return false;
}
}
/**
* Your MyQueue object will be instantiated and called as such:
* MyQueue obj = new MyQueue();
* obj.push(x);
* int param_2 = obj.pop();
* int param_3 = obj.peek();
* boolean param_4 = obj.empty();
*/
注意代码可以简化
比如peek()里面两个return 语句是一样的,可以改一下,比如判空,直接 return inStack.isEmpty() && outStack.isEmpty();它不香吗
class MyQueue {
Deque<Integer> inStack;
Deque<Integer> outStack;
public MyQueue() {
inStack = new LinkedList<Integer>();
outStack = new LinkedList<Integer>();
}
public void push(int x) {
inStack.push(x);
}
public int pop() {
if (outStack.isEmpty()) {
in2out();
}
return outStack.pop();
}
public int peek() {
if (outStack.isEmpty()) {
in2out();
}
return outStack.peek();
}
public boolean empty() {
return inStack.isEmpty() && outStack.isEmpty();
}
private void in2out() {
while (!inStack.isEmpty()) {
outStack.push(inStack.pop());
}
}
}
作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/implement-queue-using-stacks/solution/yong-zhan-shi-xian-dui-lie-by-leetcode-s-xnb6/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
我们也可以用一个变量记住stack的栈底元素,这样在访问peek的时候如果stack2为空,就可以直接返回front
public void push(int x) {
if (s1.empty())
front = x;
s1.push(x);
}
2021.08.06(第10天) 树
21 144. 二叉树的前序遍历
思路一:递归
前序遍历就是先遍历中间节点,然后遍历左节点,右节点
我们找到第一个根节点,然后又会找到他的左节点,把这个左节点作为根节点,继续遍历,左子树遍历完之后,遍历右子树
class Solution {
List<Integer>list=new ArrayList();
public List<Integer> preorderTraversal(TreeNode root) {
if(root==null)
return list;
list.add(root.val);
preorderTraversal(root.left);
preorderTraversal(root.right);
return list;
}
}
思路二:迭代
把根节点入栈,然后栈非空为循环条件,每次弹出栈顶元素,压入该元素的右节点和左节点
class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
Stack<TreeNode>stack=new Stack();
List<Integer> list =new ArrayList();
if(root==null)
return list;
stack.push(root);
while(!stack.isEmpty()){
TreeNode temp=stack.pop();
list.add(temp.val);
if(temp.right!=null)
stack.push(temp.right);
if(temp.left!=null)
stack.push(temp.left);
}
return list;
}
}
其他:参考
leetcode 144. 二叉树的前序遍历
22 94. 二叉树的中序遍历
思路一:递归,先一直遍历到最下面的左节点,将该节点加入list,然后将该节点的父节点加入list,然后是
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List <Integer>list=new ArrayList();
dfs( root,list);
return list;
}
public void dfs(TreeNode root,List <Integer>list)
{
if(root==null) return ;
dfs(root.left,list);
list.add(root.val);
dfs(root.right,list);
return ;
}
}
思路二:迭代
先把根节点以及所有左节点压入栈,然后从最后一个左节点开始,如果有右节点,先把右节点及右节点的所有左节点压入栈,之后依次取出
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List <Integer>list=new ArrayList();
Stack<TreeNode>stack=new Stack();
if(root==null)
return list;
TreeNode temp=root;
while(temp!=null){
stack.push(temp);
temp=temp.left;
}
while(!stack.isEmpty()){
TreeNode temp1=stack.pop();
list.add(temp1.val);
if(temp1.right!=null){
TreeNode temp2=temp1.right;
while(temp2!=null){
stack.push(temp2);
temp2=temp2.left;
}
}
}
return list;
}
}
官方的迭代,很简洁
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<Integer>();
Deque<TreeNode> stk = new LinkedList<TreeNode>();
while (root != null || !stk.isEmpty()) {
while (root != null) {
stk.push(root);
root = root.left;
}
root = stk.pop();
res.add(root.val);
root = root.right;
}
return res;
}
}
23 145. 二叉树的后序遍历
思路一:递归
每个节点只有把它的左节点和右节点遍历完之后才把它自己加入list
class Solution {
List<Integer>list=new ArrayList();
public List<Integer> postorderTraversal(TreeNode root) {
if(root==null) return list;
dfs(root);
return list;
}
public void dfs(TreeNode root){
if(root==null) return;
dfs(root.left);
dfs(root.right);
list.add(root.val);
}
}
思路2 :迭代1
用双向链表,往前加元素,正好跟前序遍历相反
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
Deque<TreeNode> stack = new LinkedList<>();
LinkedList<Integer> ans = new LinkedList<>();
if (null == root) return ans;
stack.addFirst(root);
while(!stack.isEmpty()) {
TreeNode node = stack.removeFirst();
ans.addFirst(node.val);
if (null != node.left) {
stack.addFirst(node.left);
}
if (null != node.right) {
stack.addFirst(node.right);
}
}
return ans;
}
}
思路3:迭代2
就是遍历到的左节点如果还有右节点的话,怎么处理这个问题,要把这个节点重新放入stack…
class Solution {
List<Integer>list=new ArrayList();
public List<Integer> postorderTraversal(TreeNode root) {
if(root==null) return list;
Stack<TreeNode>stack=new Stack();
TreeNode prev = null;
// Deque<TreeNode> stack = new LinkedList<TreeNode>();
while(root!=null||!stack.isEmpty()){
while(root!=null){
stack.push(root);
root=root.left;
}
root=stack.pop();
if (root.right == null || root.right == prev) {
list.add(root.val);
prev = root;
root = null;
} else {
stack.push(root);
root = root.right;
}
}
return list;
}
}
2021.08.07 (第11天)树
24 102. 二叉树的层序遍历
道理咱们都懂,利用队列,先入队根节点,出队根节点,然后入队根节点的左节点和右节点,接着出队,入队,把这些节点放到一个list里面很简单,但是怎么实现分层?
可不可以用两个队列?
比如我要取出第一个队列的元素,我就把这个元素的左节点和右节点都放到第二个队列
自己写的双栈,虽然不怎么优雅,但是是自己写的
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
Queue<TreeNode>queue=new LinkedList<TreeNode>();
Queue<TreeNode>queue2=new LinkedList<TreeNode>();
List<List<Integer>>list=new ArrayList();
if(root==null)
return list;
queue.add(root);
while(!queue.isEmpty()||!queue2.isEmpty()){
if(queue2.isEmpty()&&!queue.isEmpty()){
List <Integer>tempList=new ArrayList();
while(!queue.isEmpty()){
TreeNode temp=queue.remove();
tempList.add(temp.val);
if(temp.left!=null) queue2.add(temp.left);
if(temp.right!=null) queue2.add(temp.right);
}
list.add(tempList);
}
if(queue.isEmpty()&&!queue2.isEmpty()){
List <Integer>tempList=new ArrayList();
while(!queue2.isEmpty()){
TreeNode temp=queue2.remove();
tempList.add(temp.val);
if(temp.left!=null) queue.add(temp.left);
if(temp.right!=null) queue.add(temp.right);
}
list.add(tempList);
}
}
return list;
}
}
封装一下,它不就优雅了吗
class Solution {
List<List<Integer>>list=new ArrayList();
public List<List<Integer>> levelOrder(TreeNode root) {
Queue<TreeNode>queue=new LinkedList<TreeNode>();
Queue<TreeNode>queue2=new LinkedList<TreeNode>();
if(root==null)
return list;
queue.add(root);
while(!queue.isEmpty()||!queue2.isEmpty()){
if(queue2.isEmpty()&&!queue.isEmpty())
helper(queue,queue2); //取出 queue中的元素,取出节点的左右节点放入queue2
if(queue.isEmpty()&&!queue2.isEmpty())
helper(queue2,queue); //取出 queue2中的元素,取出节点的左右节点放入queue
}
return list;
}
public void helper(Queue<TreeNode>queue,Queue<TreeNode>queue2){
List <Integer>tempList=new ArrayList();
while(!queue.isEmpty()){
TreeNode temp=queue.remove();
tempList.add(temp.val);
if(temp.left!=null) queue2.add(temp.left);
if(temp.right!=null) queue2.add(temp.right);
}
list.add(tempList);
}
}
思路二:单队列实现,就是在每次添加左右节点的时候,先记录上一层的节点数量
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> ret = new ArrayList<List<Integer>>();
if (root == null) {
return ret;
}
Queue<TreeNode> queue = new LinkedList<TreeNode>();
queue.offer(root);
while (!queue.isEmpty()) {
List<Integer> level = new ArrayList<Integer>();
int currentLevelSize = queue.size();
for (int i = 1; i <= currentLevelSize; ++i) {
TreeNode node = queue.poll();
level.add(node.val);
if (node.left != null) {
queue.offer(node.left);
}
if (node.right != null) {
queue.offer(node.right);
}
}
ret.add(level);
}
return ret;
}
}
参考:
总结:注意实例化队列不能用 Queuequeue=new Queue();
也不能用Queuequeue=new Deque();因为Queue()和Deque()都是抽象类,不能实例化,而是用的Queuequeue=new LinkedList();
25 104. 二叉树的最大深度
给定一个二叉树,找出其最大深度。
二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。
说明: 叶子节点是指没有子节点的节点。
思路一:广度优先搜索,在上一题的基础上,在遍历每一层时,把记录层数的变量加1
class Solution {
public int maxDepth(TreeNode root) {
Queue<TreeNode>queue=new LinkedList();
if(root==null) return 0;
queue.add(root);
int depth=0;
while(!queue.isEmpty()){
depth++;
int size=queue.size();
for(int i=0;i<size;i++){
// for(int i=0;i<queue.size();i++){//这个queue.size()可把我坑惨了,因为queue.size()在弹出,插入的过程中是一直在变的,,,
TreeNode temp=queue.remove();
if(temp.left!=null) queue.add(temp.left);
if(temp.right!=null) queue.add(temp.right);
}
}
return depth;
}
}
思路2:递归,深度优先
把整树转化成每一个小树,每个数的高度都是1加上它的左右子树中高度较大的那个
class Solution {
public int maxDepth(TreeNode root) {
if(root==null)
return 0;
return Math.max(maxDepth(root.left),maxDepth(root.right))+1;
}
}
再来过分一点的
class Solution {
public int maxDepth(TreeNode root) {
return root==null?0: Math.max(maxDepth(root.left),maxDepth(root.right))+1;
}
}
26 101. 对称二叉树
思路1:用队列,做层序遍历,把空节点用一个无效节点代替,然后检查每一层是不是对称的
参考:【宫水三叶】从「局部」和「整体」两个角度看待「对称二叉树」问题
class Solution {
int INF = 0x3f3f3f3f;
TreeNode emptyNode = new TreeNode(INF);
boolean isSymmetric(TreeNode root) {
if (root == null) return true;
Deque<TreeNode> d = new ArrayDeque<>();
d.add(root);
while (!d.isEmpty()) {
// 每次循环都将下一层拓展完并存到「队列」中
// 同时将该层节点值依次存入到「临时列表」中
int size = d.size();
List<Integer> list = new ArrayList<>();
while (size-- > 0) {
TreeNode poll = d.pollFirst();
if (!poll.equals(emptyNode)) {
d.addLast(poll.left != null ? poll.left : emptyNode);
d.addLast(poll.right != null ? poll.right : emptyNode);
}
list.add(poll.val);
}
// 每一层拓展完后,检查一下存放当前层的该层是否符合「对称」要求
if (!check(list)) return false;
}
return true;
}
// 使用「双指针」检查某层是否符合「对称」要求
boolean check(List<Integer> list) {
int l = 0, r = list.size() - 1;
while (l < r) {
if (!list.get(l).equals(list.get(r))) return false;
l++;
r--;
}
return true;
}
}
思路二:递归
class Solution {
public boolean isSymmetric(TreeNode root) {
return check(root, root);
}
boolean check(TreeNode a, TreeNode b) {
if (a == null && b == null) return true;
if (a == null || b == null) return false;
if (a.val != b.val) return false;
return check(a.left, b.right) && check(a.right, b.left);
}
}
评论区有人说递归每个节点会遍历两次,我也看了半天,觉得确实是这样,所以,感觉改成下面这样会更好
class Solution {
public boolean isSymmetric(TreeNode root) {
if (root==null)
return true;
return check(root.left,root.right);
}
boolean check(TreeNode a, TreeNode b) {
if (a == null && b == null) return true;
if (a == null || b == null) return false;
if (a.val != b.val) return false;
return check(a.left, b.right) && check(a.right, b.left);
}
}
2021.08.08(第12天)树
27 226 翻转一棵二叉树
思路:运用递归,从下到上
,依次翻转每一个节点的左节点和右节点
class Solution {
public TreeNode invertTree(TreeNode root) {
helper(root);
return root;
}
public void helper(TreeNode root)
{
if(root==null)
return ;
helper(root.left);
helper(root.right);
TreeNode temp=root.left;
root.left=root.right;
root.right=temp;
}
}
也可以从上到下
,依次翻转每一个节点的左节点和右节点
class Solution {
public TreeNode invertTree(TreeNode root) {
helper(root);
return root;
}
public void helper(TreeNode root)
{
if(root==null)
return ;
TreeNode temp=root.left;
root.left=root.right;
root.right=temp;
helper(root.left);
helper(root.right);
}
}
class Solution {
public TreeNode invertTree(TreeNode root) {
if (root == null) {
return null;
}
TreeNode left = invertTree(root.left);
TreeNode right = invertTree(root.right);
root.left = right;
root.right = left;
return root;
}
}
思路二:迭代,类似层序遍历,利用队列,将弹出的节点的左右节点交换
class Solution {
public TreeNode invertTree(TreeNode root) {
Queue<TreeNode>queue=new LinkedList();
if(root==null)
return null;
queue.add(root);
while(!queue.isEmpty()){
int size=queue.size();
for(int i=0;i<size;i++){
TreeNode temp=queue.remove();
TreeNode tmp=temp.left;
temp.left=temp.right;
temp.right=tmp;
if(temp.left!=null) queue.add(temp.left);
if(temp.right!=null) queue.add(temp.right);
}
}
return root;
}
}
左右节点先交换,然后再存进队列也是可以的
class Solution {
public TreeNode invertTree(TreeNode root) {
Queue<TreeNode>queue=new LinkedList();
if(root==null)
return null;
queue.add(root);
while(!queue.isEmpty()){
int size=queue.size();
for(int i=0;i<size;i++){
TreeNode temp=queue.remove();
if(temp.left!=null) queue.add(temp.left);
if(temp.right!=null) queue.add(temp.right);
TreeNode tmp=temp.left;
temp.left=temp.right;
temp.right=tmp;
}
}
return root;
}
}
28 112. 路径总和
给你二叉树的根节点 root 和一个表示目标和的整数 targetSum ,判断该树中是否存在 根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和 targetSum 。
叶子节点 是指没有子节点的节点。
思路:就是判断根节点到叶子结点的路径和
可以拆解一下,拆解成根节点的左节点到叶子结点的和是否等于 targetSum-root.val
以及根节点的右节点到叶子结点的和是否等于 targetSum-root.val
每一层都做这样的拆分
当根节点为空的时候,root.val都不存在,直接返回false
最关键的点是
if(root.left==null&&root.right==null)
return root.val==targetSum;
左右节点为空。说明是叶子节点,叶子结点的val值是否等于它的父节点传给他的targetSum,如果为fasle,则会继续判断其他路径,否则,一直返回true,程序结束。
class Solution {
public boolean hasPathSum(TreeNode root, int targetSum) {
if(root==null)
return false;
if(root.left==null&&root.right==null)//说明是叶子结点
return root.val==targetSum;
if(hasPathSum(root.left, targetSum-root.val))
return true;
if(hasPathSum(root.right, targetSum-root.val))
return true;
return false;
}
}
官方的写法
class Solution {
public boolean hasPathSum(TreeNode root, int sum) {
if (root == null) {
return false;
}
if (root.left == null && root.right == null) {
return sum == root.val;
}
return hasPathSum(root.left, sum - root.val) || hasPathSum(root.right, sum - root.val);
}
}
作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/path-sum/solution/lu-jing-zong-he-by-leetcode-solution/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
思路二:迭代
用两个队列,一个队列存储节点,另一个队列存储该节点之前的和,当遍历到叶子节点的时候进行判断
class Solution {
public boolean hasPathSum(TreeNode root, int targetSum) {
Queue<TreeNode>queue=new LinkedList();
Queue<Integer>value=new LinkedList();
if(root==null) return false;
queue.add(root);
value.add(root.val);
while(!queue.isEmpty()){
TreeNode temp=queue.remove();
int sum= value.remove();
if(temp.left==null&&temp.right==null&&sum==targetSum)
return true;
if(temp.left!=null){
queue.add(temp.left);
value.add(temp.left.val+sum);
}
if(temp.right!=null){
queue.add(temp.right);
value.add(temp.right.val+sum);
}
}
return false;
}
}
2021.08.09(第13天)树
29 700. 二叉搜索树中的搜索
给定二叉搜索树(BST)的根节点和一个值。 你需要在BST中找到节点值等于给定值的节点。 返回以该节点为根的子树。 如果节点不存在,则返回 NULL。
思路1:递归,从上到下搜索指定节点
没有用到二叉搜索树的根节点大于座机诶单,小于右节点的特性,但是理解到了,用递归做二叉树的题的时候,可以先想想如果只有一颗二叉树,一个根节点和它的左右节点,你会怎么处理
class Solution {
public TreeNode searchBST(TreeNode root, int val) {
if(root==null)
return null;
if(root.val==val)
return root;
TreeNode temp1= searchBST(root.left,val);
TreeNode temp2= searchBST(root.right,val);
if(temp1!=null)
return temp1;
if(temp2!=null)
return temp2;
return null;
}
}
class Solution {
public TreeNode searchBST(TreeNode root, int val) {
if(root==null)
return null;
if(root.val==val)
return root;
if(root.val>val)
return searchBST(root.left,val);
if(root.val<val)
return searchBST(root.right,val);
return null;
}
}
我的天啊,这个写法,爱了爱了
class Solution {
public TreeNode searchBST(TreeNode root, int val) {
if (root == null || val == root.val) return root;
return val < root.val ? searchBST(root.left, val) : searchBST(root.right, val);
}
}
思路2:利用二叉树的前中后序遍历,层序遍历,依次匹配节点也是可以的
思路3:迭代
从根节点开始,依次比较val值,大于给定值就往左搜索,小于给定值,就往右搜索
class Solution {
public TreeNode searchBST(TreeNode root, int val) {
while(root!=null){
if(root.val==val)
return root;
else if(root.val>val)
root=root.left;
else
root=root.right;
}
return null;
}
}
我的妈呀,官方题解杀我
class Solution {
public TreeNode searchBST(TreeNode root, int val) {
while (root != null && val != root.val)
root = val < root.val ? root.left : root.right;
return root;
}
}
作者:LeetCode
链接:https://leetcode-cn.com/problems/search-in-a-binary-search-tree/solution/er-cha-sou-suo-shu-zhong-de-sou-suo-by-leetcode/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
30 701. 二叉搜索树中的插入操作
自己写的第一版递归,自己写的!!!
思路:先想象只有一棵树,比较和根节点的值的情况,根节点为空,则直接把新的节点当作根节点
插入节点的值大于根节点,则将这个插入节点交给右节点处理
插入节点的值小于根节点,则将这个插入节点交给左节点处理
就相当于是要把新的节点插入到原来的树中作为某一个节点(这个节点不能是左右节点已经满了的节点)的左节点或者右节点.
总结:插入的节点必定是一个叶子节点
class Solution {
public TreeNode insertIntoBST(TreeNode root, int val) {
TreeNode addNode=new TreeNode(val);
return root=help(root,addNode,val);
}
public TreeNode help(TreeNode root,TreeNode addNode,int val){
if(root==null)
return root=addNode;
if(root.val>val)
root.left= help( root.left,addNode,val);
if(root.val<=val)
root.right =help(root.right,addNode,val);
return root;
}
}
第二版递归
class Solution {
public TreeNode insertIntoBST(TreeNode root, int val) {
if(root==null)
return root=new TreeNode(val);
if(root.val>val)
root.left= insertIntoBST( root.left,val);
if(root.val<=val)
root.right =insertIntoBST(root.right, val);
return root;
}
}
思路2:迭代
找到某个节点的值大于val值且这个节点的左节点为空,我们就把新节点当作这个节点的左节点
或者某个节点的值小于val值且这个节点的右节点为空,我们就把新节点当作这个节点的右节点
冷知识
,while(true)不相信眼泪 ,while(true)里面有return子句之后,while(true)外面居然不用返回的。。。。。
class Solution {
public TreeNode insertIntoBST(TreeNode root, int val) {
if(root==null)
return new TreeNode(val);
TreeNode temp=root;
while(true) {
if(temp.val<val){
if(temp.right==null)
{
temp.right=new TreeNode(val);
return root;
}
else
temp=temp.right;
}
else{
if(temp.left==null)
{
temp.left=new TreeNode(val);
return root;
}
else
temp=temp.left;
}
}
}
}
我也是脑c了,死循环里面break就好了呀,最后return嘛
看看人家
class Solution {
public TreeNode insertIntoBST(TreeNode root, int val) {
if (root == null) {
return new TreeNode(val);
}
TreeNode pos = root;
while (pos != null) {
if (val < pos.val) {
if (pos.left == null) {
pos.left = new TreeNode(val);
break;
} else {
pos = pos.left;
}
} else {
if (pos.right == null) {
pos.right = new TreeNode(val);
break;
} else {
pos = pos.right;
}
}
}
return root;
}
}
作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/insert-into-a-binary-search-tree/solution/er-cha-sou-suo-shu-zhong-de-cha-ru-cao-zuo-by-le-3/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
2021.08.10 (第14天)树
31 98. 验证二叉搜索树
我的思路是分别验证左子树和右子树
如果左节点小于根节点且左节点的两个子节点都小于根节点,左子树成立
如果右节点大于根节点且右节点的两个子节点都大于根节点,右子树成立
class Solution {
public boolean isValidBST(TreeNode root) {
if(root==null)
return true;
return leftValid(root,root.left)&& rightValid(root,root.right) ;
}
public boolean leftValid(TreeNode root,TreeNode child){
if(root==null||child==null)
return true;
if(root.val<=child.val )
return false;
if(child.right!=null&&child.right.val>=root.val)
return false;
if(child.left!=null&&child.left.val>=root.val)
return false;
return leftValid(child,child.left)&& rightValid(child,child.right) ;
}
public boolean rightValid(TreeNode root,TreeNode child){
if(root==null||child==null)
return true;
if(root.val>=child.val )
return false;
if(child.left!=null&&child.left.val<=root.val)
return false;
if(child.right!=null&&child.right.val<=root.val)
return false;
return leftValid(child,child.left)&& rightValid(child,child.right) ;
}
}
判断条件有疏漏,下面的120和119就被漏判断了
思路2:中序遍历
递归方式
class Solution {
// int last=Integer.MIN_VALUE;//会报错,因为给的值可能就是Integer.MIN_VALUE
long last=Long.MIN_VALUE;
public boolean isValidBST(TreeNode root) {
if(root==null)
return true;
if(!isValidBST(root.left))
return false;
if( root.val<=last)
return false;
else
last=root.val;
if(!isValidBST(root.right))
return false;
return true;
}
}
last的初始值不能取Integer.MIN_VALUE会报错,因为给的值可能就是Integer.MIN_VALUE
中序遍历,迭代版本
class Solution {
// int last=Integer.MIN_VALUE;
long last=Long.MIN_VALUE;
public boolean isValidBST(TreeNode root) {
Stack<TreeNode>stack=new Stack();
while(!stack.isEmpty()||root!=null){
while(root!=null)
{
stack.push(root);
root=root.left;
}
root=stack.pop();
if(root.val<=last)
return false;
else
{
last=root.val;
if(root.right!=null)
//queue.add(root.right);
root=root.right;
else
root=null;//取出来的结点已经用了,如果该节点没有右节点,需要把root指向空
}
}
return true;
}
}
我是傻逼吗,代码简化如下
class Solution {
// int last=Integer.MIN_VALUE;
long last=Long.MIN_VALUE;
public boolean isValidBST(TreeNode root) {
Stack<TreeNode>stack=new Stack();
while(!stack.isEmpty()||root!=null){
while(root!=null)
{
stack.push(root);
root=root.left;
}
root=stack.pop();
if(root.val<=last)
return false;
last=root.val;
root=root.right;//取出来的结点已经用完了,需要把root指向空右节点,然后把右节点的左节点压入栈中
}
return true;
}
}
思路3 设定左右结点的取值范围,
因为结点取值可能是Integer的最大值和最小值,所以我们设定的上限和下限应该是Long的最大值和最小值
左节点的取值范围是最小值到根节点的值,且不能等于根节点的值
右节点的取值范围是根节点的值到最大值,且不能等于根节点的值
class Solution {
long low=Long.MIN_VALUE;
long upper=Long.MAX_VALUE;
public boolean isValidBST(TreeNode root) {
if(root==null)
return true;
return isValidBST( root.left,low,root.val)&& isValidBST( root.right,root.val,upper) ;
}
public boolean isValidBST( TreeNode root, long low, long upper){
if(root==null)
return true;
if(root.val>=upper||root.val<=low)
return false;
return isValidBST( root.left,low,root.val)&& isValidBST( root.right,root.val,upper);
}
}
class Solution {
public boolean isValidBST(TreeNode root) {
return isValidBST(root, Long.MIN_VALUE, Long.MAX_VALUE);
}
public boolean isValidBST(TreeNode node, long lower, long upper) {
if (node == null) {
return true;
}
if (node.val <= lower || node.val >= upper) {
return false;
}
return isValidBST(node.left, lower, node.val) && isValidBST(node.right, node.val, upper);
}
}
32 653. 两数之和 IV - 输入 BST
给定一个二叉搜索树 root 和一个目标结果 k,如果 BST 中存在两个元素且它们的和等于给定的目标结果,则返回 true。
思路1:中序遍历+双指针
因为BST即排序二叉树的中序遍历是升序排列的
所以我们可以先来个中序遍历,把结点的值都存进一个list,然后利用双指针在排序数组中查找目标和
class Solution {
List<Integer>list=new ArrayList();
public boolean findTarget(TreeNode root, int k) {
inOrder(root);
int i=0;int j=list.size()-1;
while(i<j){
if(list.get(i)+list.get(j)>k)
j--;
else if(list.get(i)+list.get(j)<k)
i++;
else
return true;
}
return false;
}
public void inOrder(TreeNode root){
if(root==null)
return ;
inOrder(root.left);
list.add(root.val);
inOrder(root.right);
}
}
思路2 HashSet
从上到下遍历结点,每遍历到一个结点,就在set里面去找有内有对应的结点加起来等于k,有就直接返回true
没有就把这个结点加入set中,继续遍历下一个结点。
class Solution {
Set<Integer>set=new HashSet();
public boolean findTarget(TreeNode root, int k) {
if(root==null)
return false;
if(set.contains(k-root.val))
return true;
set.add(root.val);
return findTarget(root.left, k)||findTarget(root.right, k);
}
}
思路3 :BFS+Set
public class Solution {
public boolean findTarget(TreeNode root, int k) {
Set < Integer > set = new HashSet();
Queue < TreeNode > queue = new LinkedList();
queue.add(root);
while (!queue.isEmpty()) {
if (queue.peek() != null) {
TreeNode node = queue.remove();
if (set.contains(k - node.val))
return true;
set.add(node.val);
queue.add(node.right);
queue.add(node.left);
} else
queue.remove();
}
return false;
}
}
33 235. 二叉搜索树的最近公共祖先
给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先
思路1,递归
判断pq与根结点的位置关系
p q中如果有一个是根节点,则返回根节点
p q 如果位于根节点的两边 则返回根节点
p q如果都位于根节点的左边,则向左边继续查找
p q如果都位于根节点的右边,则向右边继续查找
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if(root==null||(p.val<=root.val&&q.val>=root.val)||(p.val>=root.val&&q.val<=root.val))//pq位于根节点的两边,则返回根节点
return root;
if(p.val<root.val&&q.val<root.val)
return lowestCommonAncestor(root.left, p, q) ;
return lowestCommonAncestor(root.right, p, q) ;
}
}
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if(root.val < p.val && root.val < q.val) return lowestCommonAncestor(root.right, p, q);
if(root.val > p.val && root.val > q.val) return lowestCommonAncestor(root.left, p, q);
return root;
}
}
思路2:迭代
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
while(true){
if(p.val>root.val&&q.val>root.val)
root=root.right;
else if(p.val<root.val&&q.val<root.val)
root=root.left;
else
break;
}
return root;
}
}
这。。。。。。。。。,注意第二行用p和q判断都可以
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
while ((root.val - p.val) * (root.val - q.val) > 0)
root = p.val < root.val ? root.left : root.right;
//如果相乘的结果是负数,说明p和q位于根节点的两侧,如果等于0,说明至少有一个就是根节点
return root;
}
}
思路3:两次遍历,就是分别找出找到p和q需要经过的路径,把路径上的结点存起来,然后比较两个路径的最后一个相同结点
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
List<TreeNode> path_p = getPath(root, p);
List<TreeNode> path_q = getPath(root, q);
TreeNode ancestor = null;
for (int i = 0; i < path_p.size() && i < path_q.size(); ++i) {
if (path_p.get(i) == path_q.get(i)) {
ancestor = path_p.get(i);
} else {
break;
}
}
return ancestor;
}
public List<TreeNode> getPath(TreeNode root, TreeNode target) {
List<TreeNode> path = new ArrayList<TreeNode>();
TreeNode node = root;
while (node != target) {
path.add(node);
if (target.val < node.val) {
node = node.left;
} else {
node = node.right;
}
}
path.add(node);
return path;
}
}
作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/lowest-common-ancestor-of-a-binary-search-tree/solution/er-cha-sou-suo-shu-de-zui-jin-gong-gong-zu-xian-26/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。