1.
我的解法:将树中节点以递归的方式放入ArrayList这个容器中,然后调用Collection.sort()方法将其排序,然后输出第k大的节点。
这种方法相当于没有利用好二叉查找树的特点,导致速度较慢。
6ms
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
ArrayList<Integer> arrays = new ArrayList<>();
public int kthLargest(TreeNode root, int k) {
//将全部的节点放入一个容器
TreeNode node = root;
Append(node);
Collections.sort(arrays);
int Klarge = arrays.get(arrays.size()-k);
return Klarge;
}
public void Append(TreeNode root){
if(root != null){
arrays.add(root.val);
}
if(root.left != null){
Append(root.left);
}
if(root.right != null){
Append(root.right);
}
}
}
看了大佬的两个解法
法一:1ms
使用栈来储存各个节点,先把root节点和右边这一路往下的节点,依次储存。然后pop出最右边的节点,它肯定是最大的那个,第二大的是哪个?如果它拥有左节点的话,那第二大的并不是它的父节点。所以pop出一个节点,我们就得检测出它是否有左节点,如果有压栈,因为左节点肯定比栈里其他的节点都大。如此循环后,很明显,被第几个pop出来的,就是第几大,用一个count计数就行。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
Deque<TreeNode> Stack = new LinkedList<>();
public int kthLargest(TreeNode root, int k) {
int count = 1;
while(root != null || !Stack.isEmpty()){
while(root != null){
Stack.push(root);
root = root.right;
}
TreeNode pop = Stack.pop();
if(count == k){
return pop.val;
}
count++;
root = pop.left;
}
//循环了半天,没找到
return 0;
}
}
法二:
实际上这个思路是基于中序遍历上修改的,中序遍历是 左子树,根结点,右子树,这样它本身就是从小到大的顺序。附上一个中序遍历的程序:
这里有个细节就是打印t.element一定要放在t.left和t.right递归调用之间,倘若我们需要从大到小的顺序,只要调换t.left和t.right就可以了。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
int count;//记录次数
int result = -1; //要返回的数值
public int kthLargest(TreeNode root, int k) {
count = k;
Kth(root);
return result;
}
public void Kth(TreeNode root){
if(root != null){
Kth(root.right);
if(count == 1){
result = root.val;
count--;
return;//找到了,就返回吧
}
count--;
Kth(root.left);
}
}
}
2.
我的解法:思路使用队列的层次遍历法,一层一层输入进List中,这里要判断何时换行(new),这里采用设置两个变量parent,current。当队列poll出元素时,parent--;这样当parent归0时,相当于这一行结束了,准备换行了。当添加一个元素的左右节点时(子节点),current++;明确了下一行有几个元素,这样在一个行结束时,就会将current的值赋给parent。
这里还遇到问题,这里 List<Integer> list2 = new ArrayList<>();list2后续再换行时,我需要清空它,有三种方法。
1.list2.clear()方法,但这里不行,会把写入List1里面的值也清空
2.list2 =null;这里也不行,在下一次循环时会出现空指针异常
3.这里采用最后一种方法。用new ArrayList()来清空list
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
Deque <TreeNode> queue = new LinkedList<>();
int parent = 1;
int current = 0;
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> list1 = new ArrayList<>();
//List<Integer> tmp = new ArrayList<>();
if(root ==null)
return list1;
queue.add(root);
List<Integer> list2 = new ArrayList<>();
while(!queue.isEmpty()){
TreeNode node = queue.poll();
parent--;
list2.add(node.val);
if(node.left != null){
queue.add(node.left);
current++;
}
if(node.right != null){
queue.add(node.right);
current++;
}
if(parent == 0){//这一层是否走完
list1.add(list2);
list2 = new ArrayList<>(); ;//清空
parent = current;
current = 0;
}
}
return list1;
}
}
还看到也是层次遍历法,但它判断换行用的是队列的size,我觉得比我这个稍好理解。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> results = new ArrayList<>();
if (root == null) {
return results;
}
Queue<TreeNode> queue = new LinkedList<>();
queue.add(root);
while (!queue.isEmpty()) {
int size = queue.size();
List<Integer> list = new ArrayList<>();
for (int i = 0; i < size; i++) {
TreeNode treeNode = queue.poll();
list.add(treeNode.val);
if (treeNode.left != null) {
queue.add(treeNode.left);
}
if (treeNode.right != null) {
queue.add(treeNode.right);
}
}
results.add(list);
}
return results;
}
}
法二:使用递归,转一个大佬的答案
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
private List<List<Integer>> list =new ArrayList<>();
public List<List<Integer>> levelOrder(TreeNode root) {
helper(root,0);
return list;
}
private void helper(TreeNode node,int index){//index记录树的深度
if (node==null)
return;
if (index==list.size()) {//如果深度超出了list的size,就说明到了最新的深度
List<Integer> mid = new ArrayList<>();
mid.add(node.val);
list.add(mid);
}
else{//若没超出,则添加到已有的深度
List<Integer> mid = list.get(index);
mid.add(node.val);
list.set(index,mid);
}
helper(node.left,index+1);//继续递归
helper(node.right,index+1);
}
}
3.
解法一:移窗法
思路如下:
代码如下:复现这个思路的时候,其实里面细节很多。出了很多bug,调试了1小时才排完。
class Solution {
public int[][] findContinuousSequence(int target) {
int i = 1;//窗头
int j = 1;//窗尾
int sum =1;
ArrayList <int[]> array = new ArrayList<>();
while(i <= target/2){
if(sum < target){
sum += ++j;
}else if(sum > target){
sum -= i++;
}else{//相等
int[] nums = new int[j-i+1];
for(int k=i;k<=j;k++){
nums[k-i] = k;
}
sum -= i++;
array.add(nums);
}
}
return array.toArray(new int[array.size()][]);
}
}
方法二:削尖法 这个方法非常巧妙
举个例子,比如9,如果它能被2个连续正整数a,b。a+b=9 b =a+1,很明显9-1=2a,所以9-1一定能被2整除。
同理如果它能被3个连续正整数表示,a+b+c=9,那3a=9-1-2,所以9-1-2一定能被3整除....
class Solution {
public int[][] findContinuousSequence(int target) {
int i=2;//代表的是能成功分成连续正整数时,正整数的个数,最小2个
ArrayList <int[]> array = new ArrayList<>();
int origal = target;
while(i<origal/2){
target = target-i+1;
if(target <=0) break;
if(target % i == 0){
//可以组成i个连续正整数
int[] nums = new int[i];
int start = target / i;
for(int k =0;k<i;k++){
nums[k] = start++;
}
array.add(nums);
i++;
}else{
i++;
}
}
Collections.reverse(array);
return array.toArray(new int[array.size()][]);
}
}
4.
我的解法: 排序,找相同3 ms
class Solution {
public int findRepeatNumber(int[] nums) {
Arrays.sort(nums);
int result = -1;
for(int i=0;i<nums.length-1;i++){
if(nums[i] == nums[i+1]){
result = nums[i];
break;
}
}
return result;
}
}
解法二:哈希表 HashSet 6 ms
class Solution {
public int findRepeatNumber(int[] nums) {
Set<Integer> set = new HashSet<Integer>();
int repeat = -1;
for (int num : nums) {
if (!set.add(num)) {
repeat = num;
break;
}
}
return repeat;
}
}
解法三:原地置换 时间复杂最低 0ms
如果没有重复数字,那么正常排序后,数字i应该在下标为i的位置,所以思路是重头扫描数组,遇到下标为i的数字如果不是i的话,(假设为m),那么我们就拿与下标m的数字交换。在交换过程中,如果有重复的数字发生,那么终止返回ture。
这道题能原地置换的关键点在于n个长度数组,并且它的范围又是0到n-1,一个数对应一个位置。
class Solution {
public int findRepeatNumber(int[] nums) {
int temp;
for(int i=0;i<nums.length;i++){
while (nums[i]!=i){
if(nums[i]==nums[nums[i]]){
return nums[i];
}
temp=nums[i];
nums[i]=nums[temp];
nums[temp]=temp;
}
}
return -1;
}
}
5.
解法一:递归 6ms
因为二叉搜索树的特点,这里要分析几种情况,就拿上面的树距离。
1. root == null,那就返回root就好
2.p或者q等于root,那不用说,公共祖先一定是root,返回root。
3.p,q的值一个比root小,一个比root大,公共祖先也一定是root,返回root。
4.p,q的值都比root小,那公共祖先就往root的左节点走了。
5.p,q的值都比root大,那公共祖先就往root的右节点走了。
这里,其实if只要写4和5,其余都算else,就行。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if(p.val > root.val && q.val > root.val){
return lowestCommonAncestor(root.right,p,q);
}else if(p.val < root.val && q.val < root.val){
return lowestCommonAncestor(root.left,p,q);
}else{
return root;
}
}
}
解法二:非递归 6ms
一个思路,不多说
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
TreeNode tmp = root;
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{
return root;
}
}
}
}
6.
这一题和第5题,都是找公共祖先,区别在于第5题更加简单一点。因为第5题是二叉搜索树,但是他们都是判定情况是一样的。
1.p,q如果分别在root的两侧,那就返回root。
2.p,q如果都在root的左侧,那就返回root.left
3.p,q如果都在root的右侧,那就返回root.right
因为这里无法通过p,q和root的值直接进行判别p,q分布的位置,所以这里的边界条件是不同了:
1.向左侧探,如果左节点为null,说明p,q都在右节点
2.向右侧探,如果右节点为null,说明p,q都在左节点
3.不是以上的情况,那就说明正好分布于两侧,返回当前root
8MS
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if(root==null || root==p || root==q)
return root;
TreeNode leftNode=lowestCommonAncestor(root.left,p,q);
TreeNode rightNode=lowestCommonAncestor(root.right,p,q);
if(leftNode==null)
return rightNode;
if(rightNode==null)
return leftNode;
return root;
}
}
7.
我的解法:17ms 时间复杂度高了
运用了哈希图这个容器,一个key对应一个value,所以我将数组里每一个数,都存入图中,遍历一遍数组,发现图中没有,就加入,有就相应地value值+1。思路很简单 其实我们并没有返回超过数组长度一般的数字,而是返回了出现次数最多的数字,因为题目中说了该数组只有一个数字超过了数组长度的一半,所以他就是次数最多的数字。
class Solution {
public int majorityElement(int[] nums) {
HashMap<Integer, Integer> map = new HashMap<Integer, Integer>();
for(int i=0;i<nums.length;i++){
if(map.containsKey(nums[i])){
map.put(nums[i],map.get(nums[i])+1);
}else{
map.put(nums[i],1);
}
}
int maxNumber = 0;
int maxCount = Collections.max(map.values());
for(Map.Entry<Integer, Integer> entry:map.entrySet()){
if(maxCount == entry.getValue()){
maxNumber = entry.getKey();
}
}
return maxNumber;
}
}
看看别人的思路,列举两个:
方法一:正常思路可以先排序,再取中间值,中间值就是数组中出现次数超过一半的数字。
2ms
class Solution {
public int majorityElement(int[] nums) {
if(nums.length == 0){
return 0;
}
Arrays.sort(nums);
if(nums.length %2 == 0){
return nums[nums.length/2-1];
}else{
return nums[(nums.length+1)/2-1];
}
}
}
方法二:时间复杂度最低 1ms
思路简述:target用来记录上一个值,count来计数,遍历数组,当target等于当前数值时,count++;不等于时count--。当count 归0时,target的值要更新成现在这个值,并且count 值重新归1,最终返回的target就是超过数组一般长度的值。
为什么?因为这个数超过数组长度的一半,所以不管这个数分布在数组哪些位置,最终count不会归0 。
class Solution {
public int majorityElement(int[] nums) {
int target = nums[0];
int count = 1;
for(int i = 1;i<nums.length;i++) {
if(target == nums[i]) {
count++;
}else {
count--;
}
if(count == 0) {//当count=0时,更换target的值为当前访问的数组元素的值,次数设为1
target = nums[i];
count = 1;
}
}
return target;
}
}
8.
我的解法: 14ms,有点慢。滑窗方法
class Solution {
public int[] twoSum(int[] nums, int target) {
if(nums.length < 2) return null;
int[] result = new int[2];
int pre = 0;
int cur = 1;
while(nums[pre] < target){
if(nums[pre]+nums[nums.length-1]<target){
pre++;
cur = pre+1;
continue;
}else{
int sum = nums[pre]+nums[cur];
if(sum < target){
cur++;
}else if(sum > target){
pre++;
cur = pre+1;
}else{
result[0] = nums[pre];
result[1] = nums[cur];
break;
}
}
}
return result;
}
}
大佬的双指针法,其实和我的方法有些类似,只不过,大佬定位pre在头,cur在尾。而我呢,就是cur放在pre后面一位。
class Solution {
public int[] twoSum(int[] nums, int target) {
int left=0,right=nums.length-1;
int [] res=new int[2];
while(left<right){
if (nums[left]+nums[right]==target){
res[0]=nums[left];
res[1]=nums[right];
return res;
}
else if (nums[left]+nums[right]<target)
left++;
else
right--;
}
return res;
}
}
9.
我的思路:双指针思想 2ms
class Solution {
public int[] exchange(int[] nums) {
int tmp;
int left = 0;
int right = nums.length - 1;
while(right>left){
if(nums[right] %2 ==1){
//后面出现了奇数
if(nums[left] %2 == 0){
//前面出现了偶数
tmp = nums[right];
nums[right] = nums[left];
nums[left] = tmp;//交换了
}else{
left++;
}
}else{
right--;
}
}
return nums;
}
}
10.
借鉴了别人的思想:我复现了他的思想 1ms
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
ListNode tmpA = headA;//初始化双指针
ListNode tmpB = headB;
int count = 0;//计数的,因为只能走一遍,走完一遍还是没有的话就得返回null
if(headA == null || headB == null) return null;
while(count<3){
if(tmpA == tmpB){
return tmpA;
}
if(tmpA.next == null){
tmpA = headB;
count++;
}else{
tmpA = tmpA.next;
}
if(tmpB.next == null){
tmpB = headA;
count++;
}else{
tmpB = tmpB.next;
}
}
return null;
}
}
解法二:看到一个,速度虽然有点慢,但是比较容易想得到 9ms
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
ListNode cur=headA;
Set<ListNode> set=new HashSet();
while(cur!=null){
set.add(cur);
cur=cur.next;
}
cur=headB;
while(cur!=null){
if(set.contains(cur)) return cur;
else cur=cur.next;
}
return null;
}
}