Java-LeetCode刷题笔记

p485-最大连续1的个数

class Solution {
    public int findMaxConsecutiveOnes(int[] nums) { //输入数组
        int maxCount = 0, count = 0;
        int n = nums.length;       //得到数组长度
        for (int i = 0; i < n; i++) {
            if (nums[i] == 1) {
                count++;
            } else {
                maxCount = Math.max(maxCount, count);       //返回两个值中的最大值,存入maxCount中
                count = 0;      //遇到不是1的,count就会清零
            }
        }
        maxCount = Math.max(maxCount, count);       //比较
        return maxCount;
    }
}

561-数组拆分Ⅰ

//把排序的过程在脑中完成,直接编写求结果的程序

public static int arrayPairSum(int[] nums) {
    Arrays.sort(nums);      //排序
    int sum = 0;
    for (int i = 0; i < nums.length; i += 2) {
        sum += nums[i];     //选第1、3、5、7、9个,
    }       //min的过程在脑子里完成,排序之后:(1,2),(3,4),min一下的话,就是求1+3+...
    return sum;
}

566-重塑矩阵

方法1:技巧

public static int[][] matrixReshape(int[][] nums, int r, int c) {
    int row = nums.length;  //原来的行
    int col = nums[0].length;   //原来的列
    if (row * col != r * c) {
        return nums;
    }
    int[][] ans = new int[r][c];        //新的二维数组
    int n = row * col;
    for (int i = 0; i < n; i++) {
        ans[i / c][i % c] = nums[i / col][i % col];
    }
    return ans;
}

方法2:for循环

public static int[][] matrixReshape(int[][] nums, int r, int c) {
    int[][] arrtrans = new int[r][c];
    int rr = nums.length;
    int cc = nums[0].length;
    int m = 0;
    int n = 0;
    if (r * c == rr * cc) {
        for (int i = 0; i < r; i++) {
            for (int j = 0; j < c; j++) {
                arrtrans[i][j] = nums[m][n];
                n++;
                if (n == cc) {        //粗暴循环方法,关键在这里
                    n = 0;
                    m++;
                }
            }
        }
        return arrtrans;
    } else {
        return nums;
    }
}

001-两数之和
与其判断两个数怎么相加=目标值,不如找数组里有没有【目标值-数组中的数】,相当于先找出答案,再找答案所在的地方
方法1、暴力解法

//暴力解法
public static int[] twoSum(int[] nums, int target) {
    int[] list = {0, 0};
    for (int i = 0; i < nums.length-1; i++) {
        for (int j = i+1; j < nums.length; j++) {
            if (nums[i] + nums[j] == target) {
                list[0] = i;
                list[1] = j;
            }
        }
    }
    return list;
}

方法2、哈希表

//哈希表解法
public static int[] twoSum(int[] nums, int target) {
    Map<Integer, Integer> hm = new HashMap<>();
    int[] ints = {-1, -1};
    for (int i = 0; i < nums.length; i++) {
        if (hm.containsKey(target - nums[i])) {
            ints[0] = hm.get(target - nums[i]);
            ints[1] = i;
        }
        hm.put(nums[i], i);//把数组一个一个放进去,再一次for循环,这个时候i++了,就不会重复了。左k右v。
        //为什么把hm.put放到后面?防止出现第一个值就符合条件的情况,这样的话就会返回【0,0】
        //如{1,2,3,5},依次放入hm的是<1,0>、<2,1>、<3,2>、<5,3>,如果符合条件的话就会将哈希表中的键值赋值给ints数组
    }
    return ints;        //返回ints数组
}

020-有效的括号
方法一、栈

public boolean isValid(String s) {
    if (s.isEmpty())
        return true;
    Stack<Character> stack = new Stack<>();
    //栈的用法
    for (char k : s.toCharArray()) {//取每一个字符
        if (k == '(')
            stack.push(')');//压栈,压入对应的另一半
        else if (k == '{')
            stack.push('}');
        else if (k == '[')
            stack.push(']');
        else if (k == ')' || k == '}' || k == ']') {
            if (stack.empty()) {
                return false;
            }
            Character pop = stack.pop();
            if (k!=pop){
                return false;
            }
        }
    }
    return stack.empty();
}

//重点是学习栈怎么使用
//栈是FILO模式

stack<Character> stack = new Stack<>();
//创建空栈
stack.push('}');
//压栈,可以压入各种类型
stack.pop();
//弹栈,可以加返回值

方法二、暴力解法

class Solution {
    public boolean isValid(String s) {
        while (s.contains("()") || s.contains("[]") || s.contains("{}")) {
            if (s.contains("()")) {
                s = s.replace("()", "");
            }
            if (s.contains("[]")) {
                s = s.replace("[]", "");
            }
            if ((s.contains("{}"))) {
                s = s.replace("{}", "");
            }
        }
        return s.length() == 0;
    }
}

101-对称二叉树
//方法传入的结点直接进行比较,然后末尾再传入结点的子节点,即可完成递归操作
//递归法,迭代法还不懂

public boolean isSymmetric(TreeNode root) {
   return check(root, root);//关键:传入两个root
}

public boolean check(TreeNode p, TreeNode q) {
   if (p == null && q == null) {
       return true;
   }
   if (p == null || q == null) {
       return false;
   }
   return p.val == q.val && check(p.left, q.right) && check(p.right, q.left);
    }

剑指OFFER
029-顺时针打印矩阵
//重点:观察四条边的个数是如何变化的
//我的思路是先转置一下,读的时候读一行原矩阵,再读一行转置矩阵,还是有点麻烦,不需要转置

public int[] spiralOrder(int[][] matrix) {
    if (matrix == null || matrix.length == 0 || matrix[0].length == 0)
        return new int[0];
    int[] ans = new int[matrix.length * matrix[0].length];//一维数组的声明
    helper(matrix, 0, 0, matrix[0].length, matrix.length, ans);
    return ans;
   }

public void helper(int[][] matrix, int x, int y, int width, int height, int[] ans) {
    int count = 0;
    while (width > 0 && height > 0) {   //每次转完一圈之后判断一下
        for (int i = 0; i < width; i++)    //读行
            ans[count++] = matrix[y][x + i];
        for (int i = 1; i < height; i++)   //读右边列
            ans[count++] = matrix[y + i][x + width - 1];
        if (height > 1) {                 //高度大于1,转完一圈高度值才会减小。如果是=1的话,会重复读
            for (int i = width - 2; i >= 0; i--)   //逆序读下面一行
                ans[count++] = matrix[y + height - 1][x + i];
        }
        if (width > 1) {                  //宽度大于1
            for (int i = height - 2; i > 0; i--)     //逆序读左面一列
                ans[count++] = matrix[y + i][x];
        }
        width -= 2;//每转完一圈,宽度-2
        height -= 2;//每转完一圈,高度-2
        x += 1;
        y += 1;
    }
}

136-只出现一次的数字
//方法一、new一个ArrayList集合,利用ArrayList的contains方法判断集合中是否存在该元素,利用remove方法可以删除元素或者指定索引处的元素

public int singleNumber(int[] nums) {
    List<Integer> list = new ArrayList<>();
    for (int i = 0; i < nums.length; i++) {
        if (!list.contains(nums[i])) {
            list.add(nums[i]);//不包含则添加
        } else {
            Integer num = nums[i];//如果用int类型的话,会让系统以为是索引,强制转成Integer类型,只能是元素了
            list.remove(num);//包含则删除
        }
    }
    return list.get(0);
}

方法二、位运算:异或
原理是:相异才为1,相同则为0

public static int singleNumber(int[] nums) {
    int ans = 0;
    for (int num : nums) {
        ans ^= num;//连续的两个数字自动转化成二进制后进行异或运算
        //如果第三个数字和之前的数字相同,再进行异或运算的时候会自动把该数字代表的值转为0
    }
    return ans;
}

剑指OFFER
面试题10-2-青蛙跳台阶
Java-LeetCode刷题笔记_第1张图片
转化成了斐波那契问题,可以建立斐波那契数列,然后检索即可,
也可以使用动态规划方法,如下:

public int numWays(int n) {
    int a = 1, b = 1, sum;//a代表跳0阶有多少方法,b代表跳1阶有多少方法
    for (int i = 0; i < n; i++) {
        sum = (a + b) % 1000000007;
        //sum代表跳下一阶有多少方法,有该阶的前两阶的方法之和(见图)
        //取模是为了防止内存越界
        a = b;//a,b,sum,滚动前进,sum先更新(算出这次有多少方法),然后a更新,然后b更新
        b = sum;//辅助变量 sum 使 a, b两数字交替前进
    }
    return a;
}

206-反转单链表
Java-LeetCode刷题笔记_第2张图片
单链表的next指向下一个结点

public static ListNode reverseList(ListNode head) {
    ListNode prev = null;
    ListNode curr = head;
    while (curr != null) {//当前不为空的话
        ListNode next = curr.next;//记住下一个结点
        curr.next = prev;//链表的next指向前一个结点,实现反转
        prev = curr;//prev往后走
        curr = next;//curr往后走
    }
    return prev;    //返回的是这个
}

234-回文链表
确定数组列表是否回文很简单,我们可以使用双指针法来比较两端的元素,并向中间移动。一个指针从起点向中间移动,另一个指针从终点向中间移动。因此最简单的方法就是将链表的值复制到数组列表中,再使用双指针法判断。(摘自力扣)
如果要使用反转的方法,需要一半一半进行反转,不能反转整个链表

public boolean isPalindrome(ListNode head) {
    List<Integer> vals = new ArrayList<Integer>();
    // 将链表的值复制到数组中
    ListNode curr = head;
    while (curr != null) {
        vals.add(curr.val);
        curr = curr.next;
    }
    // 使用双指针判断是否回文
    int front = 0;
    int back = vals.size() - 1;
    while (front < back) {
        if (!vals.get(front).equals(vals.get(back))) {
            return false;
        }
        front++;
        back--;
    }
    return true;
}

剑指OFFER
22-链表中倒数第k个节点
//快慢指针
//必须学会快慢指针的用法

public ListNode getKthFromEnd(ListNode head, int k) {
    ListNode former = head, latter = head;
    for (int i = 0; i < k; i++) {
        former = former.next;//快指针先走k步
    }
    while (former != null) {//跳出条件:快指针走完
        former = former.next;
        latter = latter.next;
    }
    return latter;
}

283-移动零

public void moveZeroes(int[] nums) {
    int index = 0;
    for (int num : nums) {
        if (num != 0) {
            nums[index++] = num;//不是0的话,把数组的第1个变成这个数
        }
    }//结束后index就变成了最后一个非0元素的索引
    for (int i = index; i < nums.length; i++) {
        nums[i] = 0;//从index后一个元素到数组结束,全置为0
    }//完成移动
}

155-最小栈
Java-LeetCode刷题笔记_第3张图片
//因为要保证,栈顶元素pop出后,上一个最小值还在,所以使用栈结构,FILO

public class MinStack {
    private Stack<Integer> dataStack;
    private Stack<Integer> minStack;//辅助栈

    public MinStack() {
        dataStack = new Stack<>();
        minStack = new Stack<>();
    }

    public void push(int x) {
        dataStack.push(x);
        // 注意:这里必须是 x <= minStack.peek() 的时候,就 push minStack
        // 如果去掉等于的话,可能会出现 dataStack 不为空,但是 minStack 为空了
        if (minStack.isEmpty() || x <= minStack.peek()) {//peak是栈顶
            minStack.push(x);
        }
    }

    public void pop() {
        int topElement = dataStack.pop();
        if (topElement == minStack.peek()) {
            minStack.pop();
        }
    }

    public int top() {
        return dataStack.peek();
    }

    public int getMin() {
        return minStack.peek();
    }
}

剑指offer-42-连续子数组的最大和
动态规划问题
Java-LeetCode刷题笔记_第4张图片

public int maxSubArray(int[] nums) {
    int res = nums[0];//0索引处的值
    for(int i = 1; i < nums.length; i++) {
        nums[i] += Math.max(nums[i - 1], 0);//前一个计算结果是否对下一个值做贡献
        res = Math.max(res, nums[i]);//不管有没有贡献,比较一下,返回最大值
    }
    return res;
}

21-合并两个有序链表

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;//l1后移
        } else {
            prev.next = l2;
            l2 = l2.next;
        }
        prev = prev.next;//prev后移
    }
    // 合并后 l1 和 l2 最多只有一个还未被合并完,我们直接将链表末尾指向未合并完的链表即可
    prev.next = l1 == null ? l2 : l1;
    return prehead.next;
}

剑指21-调整数组顺序使奇数在前偶数在后
方法一:简单粗暴,申请一个临时数组

public int[] exchange(int[] nums) {
    if (nums == null || nums.length == 0)
        return nums;
    int[] ints = new int[nums.length];//设置的时候,带上占用的空间
    int j = 0;
    int length = nums.length - 1;
    for (int i = 0; i < nums.length; i++) {
        if (nums[i] % 2 != 0) {
            ints[j++] = nums[i];
        } else {
            ints[length--] = nums[i];
        }
    }
    return ints;
}

方法二:双指针实现
Java-LeetCode刷题笔记_第5张图片
Java-LeetCode刷题笔记_第6张图片

public int[] exchange(int[] nums) {
    int left = 0;
    int right = nums.length - 1;
    while (left < right) {
        // 当找到一个偶数时,就跳出循环。
        // (这里有个求奇偶数的小技巧,就是当一个数是奇数时,它的二进制表示的最后一位肯定是1
        while (left < right && (nums[left] & 1) == 1) {
            left++;
        }
        // 当找到一个奇数时,就跳出循环
        while (left < right && (nums[right] & 1) == 0) {
            right--;
        }
        // 如果两个指针还没有碰到一起时,说明找到了需要交换的位置
        if (left < right) {
            int temp = nums[left];
            nums[left] = nums[right];
            nums[right] = temp;
        }
    }
    return nums;
}

53-最大子序和
动态规划
Java-LeetCode刷题笔记_第7张图片

public int maxSubArray(int[] nums) {
    int pre = 0;//前i项的最大值
    int maxAns = nums[0];
    for (int x : nums) {
        pre = Math.max(pre + x, x);//f(i-1)+nums[i]与nums[i]比较
        maxAns = Math.max(maxAns, pre);//全部数组中的最大连续子序和,有更大的值就更新
    }
    return maxAns;
}

104-二叉树的最大深度

public int maxDepth(TreeNode root) {
    if (root == null) {
        return 0;
    } else {
        int leftHeight = maxDepth(root.left);
        int rightHeight = maxDepth(root.right);
        return Math.max(leftHeight, rightHeight) + 1;	//包含根节点
    }
}

剑指58-Ⅰ

public String reverseWords(String s) {
    String[] strs = s.trim().split(" "); // 删除首尾空格,分割字符串
    StringBuilder res = new StringBuilder();
    for (int i = strs.length - 1; i >= 0; i--) { // 倒序遍历单词列表
        if (strs[i].equals("")) continue; // 遇到空单词则跳过
        res.append(strs[i] + " "); // 将单词拼接至 StringBuilder
    }
    return res.toString().trim(); // 转化为字符串,删除尾部空格,并返回
}

169-多数元素
Boyer-Moore 投票算法
如果候选人不是maj 则 maj,会和其他非候选人一起反对 会反对候选人,所以候选人一定会下台(maj==0时发生换届选举)
如果候选人是maj , 则maj 会支持自己,其他候选人会反对,同样因为maj 票数超过一半,所以maj 一定会成功当选

public int majorityElement(int[] nums) {
    int count = 0;
    Integer candidate = null;
    for (int num : nums) {
        if (count == 0) {
            candidate = num;
        }
        count += (num == candidate) ? 1 : -1;
    }
    return candidate;
}

剑指52-Ⅱ
0~n-1中缺失的数字
二分法

public int missingNumber(int[] nums) {
    int i = 0, j = nums.length - 1;//数组头尾
    while(i <= j) {
        int m = (i + j) / 2;//取数组中间
        if(nums[m] == m) i = m + 1;//如果符合要求,说明目标在后半部分
        //就把头变成[旧的中间+1],下一次循环的时候,[新的中间]就变成了[旧的中间+1]和[尾]的中间的值
        else j = m - 1;//不符合的话,说明在前半部分,调整尾的指向
    }
    return i;
}

剑指65
不用±*/做加法
Java-LeetCode刷题笔记_第8张图片

public int add(int a, int b) {
    int sum = a ^ b;
    int carry = (a & b) << 1;	//注意括号
    while (carry != 0) {
        a = sum;
        b = carry;
        sum = a ^ b;
        carry = (a & b) << 1;	//注意括号
    }
    return sum;
}

461-汉明距离
方法一、使用内置函数

public int hammingDistance(int x, int y) {
    return Integer.bitCount(x ^ y); +
    //bitCount 数出整数二进制下 1 的个数
    //1^0 = 1 ,0^1 =1 ,0^0 = 0 ,1^1 = 0
}

方法二、异或运算
异或运算:相同的为0,不同的为1
将两个数做异或运算,然后计算1的个数即是答案。

将x,y按位异或得到i,将问题转化为求i的二进制位中1的个数count
当i不为0时,将i与1按位与,判断二进制末尾是不是1,是,count++
将i右位移一位
重复第二,第三步,直到第二步条件不满足,,即i==0时终止统计, 即可得到i的二进制位中1的个数,问题得解。

public int hammingDistance(int x, int y) {
	int xor = x ^ y;	//求异或,不同的位=1
	int distance = 0;	//初始距离为0
	while (xor != 0) {	//异或后的结果的每一位都运算完毕后,跳出循环
	  if (xor % 2 == 1)	//对2取余,相当于看最后一位是不是1
	    distance += 1;	//最后一位是1的话,距离+1
	  xor = xor >> 1;	//一开始异或后的结果往后移一位
	}
	return distance;
}

70-爬楼梯
动态规划:滚动数组
for循环求出斐波那契数列,一步一步往后滚动
迭代法不行,会超出时间限制

public int climbStairs(int n) {
    int p = 0, q = 0, r = 1;
    for (int i = 1; i <= n; ++i) {	//是++i
        p = q; 
        q = r; 
        r = p + q;
    }
    return r;
}

剑指
面试题55-1
方法一、DFS
KEY:此树的深度和其左(右)子树的深度之间的关系。
显然,此树的深度等于左子树的深度 与 右子树的深度中的最大值 +1 。

public int maxDepth(TreeNode root) {
    //DFS,后序遍历,递归实现
    if(root == null) return 0;
    return Math.max(maxDepth(root.left), maxDepth(root.right)) + 1;
}

方法二、BFS
层序遍历,就是要把每一层的子节点一个一个排好队

public int maxDepth(TreeNode root) {
    if (root == null)
        return 0;
    //创建一个队列
    Deque<TreeNode> deque = new LinkedList<>();
    //树的根节点压入链表
    deque.push(root);
    //深度初始化为0
    int count = 0;
    //链表非空的话,就循环
    while (!deque.isEmpty()) {
        //size方法取出每一层结点的个数
        int size = deque.size();
        //该层的个数>0就循环
        while (size > 0) {
            //弹出链表中的结点,并置为当前结点
            TreeNode cur = deque.pop();
            //把当前结点的左右子结点放入链表(非空的话)【层序遍历就是这么来的】
            if (cur.left != null)
                deque.addLast(cur.left);	//添加到尾部
            if (cur.right != null)
                deque.addLast(cur.right);
            //执行完该层的一个结点后,size--
            size--; 
        }
        //内循环完成一次执行后,就把该层的所有结点都弹出了,并把该层所有子节点加入链表(非空的话)
        //所以深度++,准备下一次的循环
        count++;
    }
    return count;
}

705-设计哈希集合

class MyHashSet {
    //定义哈希表的大小=769,一个质数,有效解决哈希冲突
    private static final int BASE = 769;
    //定义链表
    private LinkedList[] data;

	//构造方法:创建了哈希集合,集合的元素都是链表
    public MyHashSet() {
    	//创建链表,大小=BASE
        data = new LinkedList[BASE];
        for (int i = 0; i < BASE; ++i) {
        	//数组里面的每个元素,都new一个链表
            data[i] = new LinkedList<Integer>();
        }
    }
    
    //add方法
    public void add(int key) {
        //找到key对应的h处
        int h = hash(key);
        //使用h处链表的迭代器,用于遍历
        Iterator<Integer> iterator = data[h].iterator();
        //h处有值的话,就一直找链表的下一个值
        while (iterator.hasNext()) {
            Integer element = iterator.next();
            //直到找到下一个元素=key,退出while循环
            if (element == key) {
                return;		//这里不一样
            }
        }
        //h处链表的末尾增加一个key
        data[h].offerLast(key);
    }
    
    //remove方法
    public void remove(int key) {
        //找到key对应的h处
        int h = hash(key);
        Iterator<Integer> iterator = data[h].iterator();
        while (iterator.hasNext()) {
            Integer element = iterator.next();
            //直到找到下一个元素=key,删除该处的key,退出while循环
            if (element == key) {
                data[h].remove(element);		//这里不一样
                return;
            }
        }
    }
    
	//存在就返回true
    public boolean contains(int key) {
        int h = hash(key);
        Iterator<Integer> iterator = data[h].iterator();
        while (iterator.hasNext()) {
            Integer element = iterator.next();
            if (element == key) {
                return true;		//这里不一样
            }
        }
        return false;
    }

	//判断新的值放到哪里
    private static int hash(int key) {
    	//对BASE取余
        return key % BASE;
    }
}

121-买卖股票的最佳时机

public int maxProfit(int[] prices) {
    int minprice = Integer.MAX_VALUE;//Integer的最大值
    int maxprofit = 0;//暂时=0
    for (int i = 0; i < prices.length; i++) {
        if (prices[i] < minprice) {//如果某一项数字是最小的(目前最小的,以后循环可能更小)
            minprice = prices[i];//找到目前循环数组里面的最小值
        } else if (prices[i] - minprice > maxprofit) {
            //某一项大于目前循环的最小值,如果大于maxprofit
            maxprofit = prices[i] - minprice;//更改maxprofit
        }
    }
    return maxprofit;
}

剑指50
第一个只出现一次的字符

public char firstUniqChar(String s) {
    HashMap<Character, Boolean> map = new HashMap<>();//创建HashMap
    char[] sc = s.toCharArray();//字符串转换为字符串数组
    for(char c : sc)//循环字符串数组
        map.put(c, !map.containsKey(c));//如果有值了,就把对应的项改为false
    for(char c : sc)//循环字符串数组
        if(map.get(c)) return c;//循环,返回第一个true对应的字母
    return ' ';//没有找到的话,返回空串
}

你可能感兴趣的:(leetcode,数据结构,算法,java)