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