2021-6-4剑指笔记01

笔记01

_11_数组中的逆序对

在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。

示例 1:

输入: [7,5,6,4]
输出: 5

限制:

0 <= 数组长度 <= 50000

package LeetCode;

import org.junit.Test;
import sun.plugin2.gluegen.runtime.CPU;

import java.util.Arrays;

public class _11_数组中的逆序对 {
    /*
    归并排序:在合并的时候进行统计
    * */
    public int reverseCount=0;
    public int reversePairs(int[] nums){
        if (nums==null||nums.length==0) return 0;
        copy=new int[nums.length];
        mergeSort(nums,0,nums.length-1);
        return reverseCount;
    }
    //分
    public void mergeSort(int[] nums,int s,int e){
        if (s>=e) return;
        //[0,mid],[mid+1,e]
        int mid=(s+e)>>1;
        //后序遍历
        mergeSort(nums,s,mid);
        mergeSort(nums,mid+1,e);
        merge(nums,s,mid,e);
    }
    //合并:需要用到辅助数组,双指针
    public int[] copy=null;
    public void merge(int[] nums,int s,int mid,int e){
        //复制数组
        int i=s;
        int j=mid+1;
        for (;i<=mid;i++) copy[i]=nums[i];
        for (;j<=e;j++) copy[j]=nums[j];
        int k=s;
        i=s;
        j=mid+1;
        for (;i<=mid&&j<=e;k++){//双指针,两份数组已经有序
            if (copy[i]>copy[j]){
                nums[k]=copy[j];
                j++;
                reverseCount+=mid-i+1;
            }
            else {//不存在逆序
                nums[k]=copy[i];
                i++;
            }
        }
        //处理两数组剩余部分
        for (;i<=mid;i++) nums[k++]=copy[i];
        for (;j<=e;j++) nums[k++]=copy[j];
    }




    @Test
    public void test(){
       int[] nums={4,5,6,7};
        int i = reversePairs(nums);
        System.out.println(i);
    }


}

_12_两链表的第一个公共节点

输入两个链表,找出它们的第一个公共节点。

如下面的两个链表:

在节点 c1 开始相交。

示例 1:

输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, skipB = 3
输出:Reference of the node with value = 8
输入解释:相交节点的值为 8 (注意,如果两个列表相交则不能为 0)。从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,0,1,8,4,5]。在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。

示例 2:

输入:intersectVal = 2, listA = [0,9,1,2,4], listB = [3,2,4], skipA = 3, skipB = 1
输出:Reference of the node with value = 2
输入解释:相交节点的值为 2 (注意,如果两个列表相交则不能为 0)。从各自的表头开始算起,链表 A 为 [0,9,1,2,4],链表 B 为 [3,2,4]。在 A 中,相交节点前有 3 个节点;在 B 中,相交节点前有 1 个节点。

示例 3:

输入:intersectVal = 0, listA = [2,6,4], listB = [1,5], skipA = 3, skipB = 2
输出:null
输入解释:从各自的表头开始算起,链表 A 为 [2,6,4],链表 B 为 [1,5]。由于这两个链表不相交,所以 intersectVal 必须为 0,而 skipA 和 skipB 可以是任意值。
解释:这两个链表不相交,因此返回 null。

注意:

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

package LeetCode;

import java.util.ArrayDeque;

public class _12_两链表的第一个公共节点 {
    public class ListNode {
       int val;
       ListNode next;
       ListNode(int x) {
           val = x;
           next = null;
       }
    }
    /*
    两种方法:
    方法一:逆向思维,从后开始往前比较,要用到栈,O(m+n) O(m)
    方法二:快慢指针
    * */
    public ListNode getIntersectionNode01(ListNode headA, ListNode headB) {
       if (headA==null||headB==null) return null;
        ArrayDeque<ListNode> dequeA = new ArrayDeque<>();
        ArrayDeque<ListNode> dequeB = new ArrayDeque<>();
        ListNode A=headA;
        ListNode B=headB;
        while (A!=null){
            dequeA.addFirst(A);
            A=A.next;
        }
        while (B!=null){
            dequeB.addFirst(B);
            B=B.next;
        }
        ListNode temp=null;
        while (!dequeA.isEmpty()&&!dequeB.isEmpty()){
            if (dequeA.getFirst()!=dequeB.getFirst()){//有个bug就是当只有一个节点时不进这个判断
                break;
            }
            temp=dequeA.removeFirst();
            dequeB.removeFirst();
        }
        return temp;
    }

    public ListNode getIntersectionNode(ListNode headA, ListNode headB){
        if (headA==null||headB==null) return null;
        int countA=0;
        int countB=0;
        ListNode A=headA;
        ListNode B=headB;
        while (A!=null){
            countA++;
            A=A.next;
        }
        while (B!=null){
            countB++;
            B=B.next;
        }
        A=headA;
        B=headB;
        ListNode common=null;
        if (countA>countB){
            for (int i = 0; i < countA - countB; i++) {
                A=A.next;
            }
            while (A!=null){
                if (A==B){
                    common=A;
                    break;
                }
                A=A.next;
                B=B.next;
            }
        }
        else {
            for (int i = 0; i < countB - countA; i++) {
                B=B.next;
            }
            while (A!=null){
                if (A==B){
                    common=A;
                    break;
                }
                A=A.next;
                B=B.next;
            }
        }
        return common;
    }
}

_13_一个数字在排序数组中出现的次数

统计一个数字在排序数组中出现的次数。

示例 1:

输入: nums = [5,7,7,8,8,10], target = 8
输出: 2
示例 2:

输入: nums = [5,7,7,8,8,10], target = 6
输出: 0

限制:

0 <= 数组长度 <= 50000

package LeetCode;

import org.junit.Test;

public class _13_一个数字在排序数组中出现的次数 {
    /*
    用二分查找算法找到第一个k和最后一个k出现的位置
    * */
    @Test
    public void  test(){
        int[] a={2,2};
        int search = search(a, 2);
        System.out.println(search);
    }

    public int search(int[] nums, int target) {
        if (nums==null) return 0;
        if (nums.length==0) return 0;
        int first=getFirstK(nums,0,nums.length-1,target);
        int last=getLastK(nums,0,nums.length-1,target);
        //如果没有k
        if (first==-1&&last==-1) return 0;
        //如果有k
        else if (first==last) return 1;
        else return last-first+1;
    }
    public int getFirstK(int[] nums,int s,int e,int target){
        int mid=0;
        while (s<=e){
            mid=(s+e)>>1;
            if (nums[mid]>target) e=mid-1;
            else if (nums[mid]<target) s=mid+1;
            else {
                if (mid-1>=0&&nums[mid-1]==target) e=mid-1;
                else break;
            }
        }
        if (s>e) mid=-1;
        return mid;
    }
    public int getLastK(int[] nums,int s,int e,int target){
        int mid=0;
        while (s<=e){
            mid=(s+e)>>1;
            if (nums[mid]>target) e=mid-1;
            else if (nums[mid]<target) s=mid+1;
            else {
                if (mid+1<=nums.length-1&&nums[mid+1]==target) s=mid+1;
                else break;
            }
        }
        if (s>e) mid=-1;
        return mid;
    }

}

_14_0到n减1中缺失的数字

一个长度为n-1的递增排序数组中的所有数字都是唯一的,并且每个数字都在范围0~n-1之内。在范围0~n-1内的n个数字中有且只有一个数字不在该数组中,请找出这个数字。

示例 1:

输入: [0,1,3]
输出: 2
示例 2:

输入: [0,1,2,3,4,5,6,7,9]
输出: 8

限制:

1 <= 数组长度 <= 10000

package LeetCode;

public class _14_0到n减1中缺失的数字 {
    /*
    方法一:先求出0~n-1的和(n-1)*n/2,再求数组,做差即可  O(N)
    方法二:利用数组递增  二分  找出第一个数组元素与下标不同的
    * */
    public int missingNumber(int[] nums) {
        if (nums==null) return -1;
        if (nums.length==0) return -1;
        int s=0;
        int e=nums.length-1;
        int mid=0;
        while (s<=e){
            mid=(s+e)>>1;
            if (mid==nums[mid]) s=mid+1;
            else if (mid!=nums[mid]){
                if (mid-1>=0&&mid-1!=nums[mid-1]) e=mid-1;
                else break;//找出第一个不等
            }
        }
        //程序有个bug,就是[0] 输出0
        if (mid==nums[mid]) mid=mid+1;
        return mid;
    }
}

_15_二叉搜索树的第k大节点

给定一棵二叉搜索树,请找出其中第k大的节点。

示例 1:

输入: root = [3,1,4,null,2], k = 1
3
/
1 4

2
输出: 4
示例 2:

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

限制:

1 ≤ k ≤ 二叉搜索树元素个数

package LeetCode;

import org.junit.Test;

import javax.swing.tree.TreeNode;
import java.util.ArrayDeque;

public class _15_二叉搜索树的第k大节点 {
    public class TreeNode {
        int val;
        TreeNode left;
        TreeNode right;
        TreeNode(int x) { val = x; }
    }

    //中序遍历的稍微变形
    public int K=0;
    public int numK=-1;
    public int kthLargest(TreeNode root, int k) {
        if (root==null) return -1;
        K=k;
        order(root);
        return numK;
    }
    public void order(TreeNode root){
        if (root==null)  return;
        order(root.right);
        K--;
        if (K==0){
            numK=root.val;
            return;
        }
        order(root.left);
    }


}

_16_二叉树的深度

输入一棵二叉树的根节点,求该树的深度。从根节点到叶节点依次经过的节点(含根、叶节点)形成树的一条路径,最长路径的长度为树的深度。

例如:

给定二叉树 [3,9,20,null,null,15,7],

3

/
9 20
/
15 7
返回它的最大深度 3 。

提示:

节点总数 <= 10000

package LeetCode;

import org.junit.Test;

public class _16_二叉树的深度 {
    public class TreeNode {
        int val;
        TreeNode left;
        TreeNode right;
        TreeNode(int x) { val = x; }
    }

    /*
    采用递归,后序遍历的方法:
    根节点的深度=(左子树的深度,右子树的深度)的最大值+1
    * */
    public int maxDepth(TreeNode root) {
        if (root==null) return 0;
        int left=maxDepth(root.left);
        int right=maxDepth(root.right);
        int max= Math.max(left, right)+1;
        return max;
    }

}

_17_平衡二叉树

输入一棵二叉树的根节点,判断该树是不是平衡二叉树。如果某二叉树中任意节点的左右子树的深度相差不超过1,那么它就是一棵平衡二叉树。

示例 1:

给定二叉树 [3,9,20,null,null,15,7]

3

/
9 20
/
15 7
返回 true 。

示例 2:

给定二叉树 [1,2,2,3,3,null,null,4,4]

   1
  / \
 2   2
/ \

3 3
/
4 4
返回 false 。

限制:

0 <= 树的结点个数 <= 10000

package LeetCode;

import org.junit.Test;

public class _17_平衡二叉树 {
    public class TreeNode {
        int val;
        TreeNode left;
        TreeNode right;
        TreeNode(int x) { val = x; }
    }

    //判断一颗树是否为平衡二叉树
    public boolean isBalanced(    TreeNode root){
        if (root==null) return true;//空的时候为平衡
        int[] depth= {0};
        return balanced(root,depth);
    }

    //只需要在后序遍历的时候记录某一节点的深度,一遍遍历,一遍判断
    public boolean balanced(    TreeNode root, int[] depth){
        if (root==null) return true;
        int[] left= {0};
        int[] right= {0};
        boolean l=balanced(root.left,left);
        boolean r=balanced(root.right,right);
        if (l&&r){//左右子树是平衡的
            //判断当前根节点是否平衡
            int diff=left[0]-right[0];
            if (diff<=1&&diff>=-1){
                depth[0]=1+Math.max(left[0],right[0]);//得到当前根结点深度
                return true;
            }
        }
        return false;
    }


    @Test
    public void test(){
        TreeNode node3=new TreeNode(3);
        TreeNode node1=new TreeNode(1);
        TreeNode node2=new TreeNode(2);
        node3.left=node1;
        node1.right=node2;
        System.out.println(isBalanced(node3));
    }
}

_18_只出现一次的两个数字

一个整型数组 nums 里除两个数字之外,其他数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)。

示例 1:

输入:nums = [4,1,4,6]
输出:[1,6] 或 [6,1]
示例 2:

输入:nums = [1,2,10,4,1,4,3,3]
输出:[2,10] 或 [10,2]

限制:

2 <= nums.length <= 10000

package LeetCode;

import org.junit.Test;

import java.util.Arrays;

public class _18_只出现一次的两个数字 {

    /*
    首先考虑一个数组只有一个数出现一次,其他数字出现两次:异或
    现在有两个数只出现一次:我们要做的就是拆分成两个数组
    全部异或的结果二进制肯定不为0,找第一个1的位置假设第n位,以此划分数组
    * */
    public int[] singleNumbers(int[] nums) {
        if (nums==null) return null;
        if (nums.length<2) return null;
        int exclusiveOr=0;
        for (int num : nums)  exclusiveOr^=num;
        int n=1;
        while (true){
            if((exclusiveOr&n)!=0) break;//犯了个错:==1
            n<<=1;
        }
        int num1=0;
        int num2=0;
        for (int num : nums) {
            if ((num&n)==0) num1^=num;
            else num2^=num;
        }
        return new int[]{num1,num2};
    }
    @Test
    public void test(){
        int[] nums={1,2,10,4,1,4,3,3};
        int[] c=singleNumbers(nums);
        System.out.println(Arrays.toString(c));
    }


}

_19_只出现1次其他出现三次

在一个数组 nums 中除一个数字只出现一次之外,其他数字都出现了三次。请找出那个只出现一次的数字。

示例 1:

输入:nums = [3,4,3,3]
输出:4
示例 2:

输入:nums = [9,1,7,9,7,9,7]
输出:1

限制:

1 <= nums.length <= 10000
1 <= nums[i] < 2^31

package LeetCode;

public class _19_只出现1次其他出现三次 {
    /*
    沿用有两个1次其他出现两次的二进制位的思路;
    此处每个三次的数那么对应的位0/1就有三个,所以能被3整除,
    所以若能被3整除怎单独数字此位为0,否则为1
    一个整数4字节,32位
    * */
    public int singleNumber(int[] nums) {
       if (nums==null) return -1;
       if (nums.length<4) return -1;
       int[] bit=new int[32];
       for (int num : nums) {
            //得到num的二进制表示
            int n=1;
            for (int i = 31; i >=0; i--) {//从低位到高位
                //1000&1000并不是等于1不要再犯这个错误了
                if ((n&num)!=0) bit[i]++;
                n<<=1;
            }
       }
       int res=0;
       for (int i = 0; i < 32; i++) {
           bit[i]=bit[i]%3;
           //先往前移动一位空出位置再加
           res<<=1;
           res+=bit[i];
       }
       return res;
    }

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值