leetcode-day12

207.你这个学期必须选修 numCourses 门课程,记为 0 到 numCourses - 1 。
在选修某些课程之前需要一些先修课程。 先修课程按数组 prerequisites 给出,其中 prerequisites[i] = [ai, bi] ,表示如果要学习课程 ai 则 必须 先学习课程 bi 。
例如,先修课程对 [0, 1] 表示:想要学习课程 0 ,你需要先完成课程 1 。
请你判断是否可能完成所有课程的学习?如果可以,返回 true ;否则,返回 false 。
入度表(广度优先遍历):

class Solution {
    public boolean canFinish(int numCourses, int[][] prerequisites) {
        int[] indegrees = new int[numCourses];
        List<List<Integer>> adjacency = new ArrayList<>();
        Queue<Integer> queue = new LinkedList<>();
        for(int i = 0; i < numCourses; i++)
            adjacency.add(new ArrayList<>());
        //统计课程安排图中每个节点的入度(一对数里面的第一个数字),生成入度表
        for(int[] cp : prerequisites) {
            indegrees[cp[0]]++;
            //第cp[1](一对数里面的第二个数字)个列表添加cp[0]
            adjacency.get(cp[1]).add(cp[0]);
        }
        //借助一个队列queue,将所有入度为0的节点入队
        for(int i = 0; i < numCourses; i++){
            if(indegrees[i] == 0) 
            queue.add(i);  
        }
        //BFS.(若课程安排图中存在环,则一定有节点的入度始终不为0)
        while(!queue.isEmpty()) {
            int pre = queue.poll();
            numCourses--;
            for(int cur : adjacency.get(pre)){
                //如果减后入度为0则入队
                if(--indegrees[cur] == 0) 
                queue.add(cur);
            }  
        }
        return numCourses == 0;
    }
}

深度优先遍历:

class Solution {
    public boolean canFinish(int numCourses, int[][] prerequisites) {
        //遍历每一条路径,看是否形成环
        List<List<Integer>> adjacency = new ArrayList<>();
        for(int i = 0; i < numCourses; i++)
            adjacency.add(new ArrayList<>());
        int[] flags = new int[numCourses];
        for(int[] cp : prerequisites)
            //第cp[1](一对数里面的第二个数字)个列表添加cp[0]
            adjacency.get(cp[1]).add(cp[0]);
        for(int i = 0; i < numCourses; i++){
            if(!dfs(adjacency, flags, i)) 
            return false;
        }
        //最终都为-1返回true
        return true;
    }
    private boolean dfs(List<List<Integer>> adjacency, int[] flags, int i) {
        if(flags[i] == 1) return false;//从正在访问中,到正在访问中,表示遇到了环
        if(flags[i] == -1) return true;//表示在访问的过程中没有遇到环,这个节点访问过了
        //走到这里,是因为初始化此时flags[i]==0
        //表示正在访问中
        flags[i] = 1;
        for(Integer j : adjacency.get(i)){
            if(!dfs(adjacency, flags, j)) 
            return false;
        }
        flags[i] = -1;//只有一次DFS完整结束了,才能执行到这一步,标记为-1,说明这条路没问题,再遇到不需要遍历了
        return true;
    }
}

208.Trie(发音类似 “try”)或者说 前缀树 是一种树形数据结构,用于高效地存储和检索字符串数据集中的键。这一数据结构有相当多的应用情景,例如自动补完和拼写检查。
Trie Tree 的实现 @路漫漫我不畏⭐
包含三个单词 "sea","sells","she" 的 Trie 会长啥样

class Trie {
    //二十六叉树
    private Trie[] children;
    private boolean isEnd;

    public Trie() {
        children = new Trie[26];//字母映射表
        isEnd = false;//该结点是否是一个串的结束
    }

    public void insert(String word) {
        Trie node = this;
        for (char c : word.toCharArray()) {
            int index = c - 'a';
            if (node.children[index] == null) {
                node.children[index] = new Trie();
            }
            node = node.children[index];
        }
        node.isEnd = true;
    }
    
    public boolean search(String word) {
        Trie node = this;
        for (char c : word.toCharArray()) {
            node = node.children[c - 'a'];
            if (node == null) {
                return false;
            }
        }
        return node.isEnd;
    }
    
    public boolean startsWith(String prefix) {
        Trie node = this;
        for (char c : prefix.toCharArray()) {
            node = node.children[c - 'a'];
            if (node == null) {
                return false;
            }
        }
        return true;
    }
}

/**
 * Your Trie object will be instantiated and called as such:
 * Trie obj = new Trie();
 * obj.insert(word);
 * boolean param_2 = obj.search(word);
 * boolean param_3 = obj.startsWith(prefix);
 */

215※.给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。
请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。
partition 减治 + 优先队列@liweiwei1419⭐
基于快速排序的选择方法:

import java.util.Random;

public class Solution {

    private static Random random = new Random(System.currentTimeMillis());

    public int findKthLargest(int[] nums, int k) {
        int len = nums.length;
        //转换一下,第 k 大元素的下标是 len - k
        int target = len - k;
        int left = 0;
        int right = len - 1;
        while (true) {
            int index = partition(nums, left, right);
            if (index < target) {
                left = index + 1;
            } else if (index > target) {
                right = index - 1;
            } else {
                return nums[index];
            }
        }
    }

    //在 while (true) 循环中,通过 left 与 right 向中间靠拢的方式逐步缩小搜索区间
    //对数组 nums 的子区间 [left..right] 执行 partition 操作,返回 nums[left] 排序以后应该在的位置
    private int partition(int[] nums, int left, int right) {
        //在区间随机选择一个元素作为标定点
        if (right > left) {
            int randomIndex = left + 1 + random.nextInt(right - left);
            swap(nums, left, randomIndex);
        }

        int pivot = nums[left];
        int j = left;
        for (int i = left + 1; i <= right; i++) {
            if (nums[i] < pivot) {
                //j 的初值为 left,先右移,再交换,小于 pivot 的元素都被交换到前面
                j++;
                swap(nums, j, i);
            }
        }
        //在之前遍历的过程中,满足 nums[left + 1..j] < pivot,并且 nums(j..i) >= pivot
        swap(nums, left, j);
        //交换以后 nums[left..j - 1] < pivot, nums[j] = pivot, nums[j + 1..right] >= pivot
        return j;
    }

    private void swap(int[] nums, int index1, int index2) {
        int temp = nums[index1];
        nums[index1] = nums[index2];
        nums[index2] = temp;
    }
} 

基于堆排序的选择方法:
手写大根堆:

class Solution {
    public int findKthLargest(int[] nums, int k) {
        int heapSize = nums.length;
        buildMaxHeap(nums, heapSize);
        //建堆完毕后,nums【0】为最大元素。逐个删除堆顶元素,直到删除了k-1个。
        for (int i = nums.length - 1; i >= nums.length - k + 1; --i) {
            //先将堆的最后一个元素与堆顶元素交换,由于此时堆的性质被破坏,需对此时的根节点进行向下调整操作。
            swap(nums, 0, i);
            //相当于删除堆顶元素,此时长度变为nums.length-2。即下次循环的i
            --heapSize;
            maxHeapify(nums, 0, heapSize);
        }
        return nums[0];
    }

    public void buildMaxHeap(int[] a, int heapSize) {
        //从最后一个父节点位置开始调整每一个节点的子树。数组长度为heasize,因此最后一个节点的位置为heapsize-1,所以父节点的位置为heapsize-1-1/2。
        for (int i = (heapSize-2)/ 2; i >= 0; --i) {
            maxHeapify(a, i, heapSize);
        } 
    }

    public void maxHeapify(int[] a, int i, int heapSize) {      //调整当前结点和子节点的顺序。
        //left和right表示当前父节点i的两个左右子节点。
        int left = i * 2 + 1, right = i * 2 + 2, largest = i;
        //如果左子点在数组内,且比当前父节点大,则将最大值的指针指向左子点。
        if (left < heapSize && a[left] > a[largest]) {
            largest = left;
        } 
        //如果右子点在数组内,且比当前父节点大,则将最大值的指针指向右子点。
        if (right < heapSize && a[right] > a[largest]) {
            largest = right;
        }
        //如果最大值的指针不是父节点,则交换父节点和当前最大值指针指向的子节点。
        if (largest != i) {
            swap(a, i, largest);
            //由于交换了父节点和子节点,因此可能对子节点的子树造成影响,所以对子节点的子树进行调整。
            maxHeapify(a, largest, heapSize);
        }
    }

    public void swap(int[] a, int i, int j) {
        int temp = a[i];
        a[i] = a[j];
        a[j] = temp;
    }
}

调库:
优先队列1:

class Solution {
    public int findKthLargest(int[] nums, int k) {
        PriorityQueue<Integer> heap = new PriorityQueue<>();
        for (int num : nums) {
            heap.add(num);
            if (heap.size() > k) {
                heap.poll();
            }
        }
        return heap.peek();
    }
}

优先队列2:

import java.util.Comparator;
import java.util.PriorityQueue;

public class Solution {

    public int findKthLargest(int[] nums, int k) {
        int len = nums.length;
        // 使用一个含有 k 个元素的最小堆,PriorityQueue 底层是动态数组,为了防止数组扩容产生消耗,可以先指定数组的长度
        PriorityQueue<Integer> minHeap = new PriorityQueue<>(k, Comparator.comparingInt(a -> a));
        // Java 里没有 heapify ,因此我们逐个将前 k 个元素添加到 minHeap 里
        for (int i = 0; i < k; i++) {
            minHeap.offer(nums[i]);
        }

        for (int i = k; i < len; i++) {
            // 看一眼,不拿出,因为有可能没有必要替换
            Integer topElement = minHeap.peek();
            // 只要当前遍历的元素比堆顶元素大,堆顶弹出,遍历的元素进去
            if (nums[i] > topElement) {
                // Java 没有 replace(),所以得先 poll() 出来,然后再放回去
                minHeap.poll();
                minHeap.offer(nums[i]);
            }
        }
        return minHeap.peek();
    }
}

221.在一个由 ‘0’ 和 ‘1’ 组成的二维矩阵内,找到只包含 ‘1’ 的最大正方形,并返回其面积。
动态规划:

class Solution {
    public int maximalSquare(char[][] matrix) {
        int maxSide = 0;
        if (matrix == null || matrix.length == 0 || matrix[0].length == 0) {
            return maxSide;
        }
        int rows = matrix.length, columns = matrix[0].length;
        int[][] dp = new int[rows][columns];
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < columns; j++) {
                if (matrix[i][j] == '1') {
                    if (i == 0 || j == 0) {
                        dp[i][j] = 1;
                    } else {
                        dp[i][j] = Math.min(Math.min(dp[i - 1][j], dp[i][j - 1]), dp[i - 1][j - 1]) + 1;
                    }
                    maxSide = Math.max(maxSide, dp[i][j]);
                }
            }
        }
        int maxSquare = maxSide * maxSide;
        return maxSquare;
    }
}

226.给你一棵二叉树的根节点 root ,翻转这棵二叉树,并返回其根节点。
递归:

class Solution {
	public TreeNode invertTree(TreeNode root) {
		//递归函数的终止条件,节点为空时返回
		if(root==null) {
			return null;
		}
		//下面三句是将当前节点的左右子树交换
		TreeNode tmp = root.right;
		root.right = root.left;
		root.left = tmp;
		//递归交换当前节点的左子树
		invertTree(root.left);
		//递归交换当前节点的右子树
		invertTree(root.right);
		//函数返回时就表示当前这个节点以及它的左右子树都已经交换完了
		return root;
	}
}

234.给你一个单链表的头节点 head ,请你判断该链表是否为回文链表。如果是,返回 true ;否则,返回 false 。
将值复制到数组中后用双指针法;递归:
回文链表⭐@力扣官方题解
快慢指针:(链表指针这一块还不太懂)

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    // 使用双指针
    public boolean isPalindrome(ListNode head) {
        if (head == null || head.next == null) {//没有或只有一个为true
            return true;
        }
        //之所以要设置flag,是因为通过本题解法后,链表的前半部分结构被翻转了,为了不破坏原有结构,还要对其进行复原操作
        boolean flag = true;
        //快慢指针的起点均在head处
        ListNode fast = head;
        ListNode slow = head;
        //保存在fast走完后,slow的上一个结点
        ListNode pre = head;
        //设置ppre指针,便于翻转
        ListNode ppre = null;
        while (fast != null && fast.next != null) {
            pre = slow;
            //快指针步长为 2,慢指针步长为 1
            slow = slow.next; 
            fast = fast.next.next;
            pre.next = ppre;
            ppre = pre;
        }
        //当链表长度为偶数时,fast最终必为null,偶数slow为中间的第二个
        //当链表长度为奇数时,fast.next为null,fast不为null,奇数slow为中间的一个
        if (fast != null) {
            slow = slow.next;
        }
        ListNode tempL = pre;   //记录中间经过翻转的前部分指针
        ListNode tempR = slow;  //记录翻转部分的下一个结点
        while (pre != null && slow != null) {
            if (pre.val != slow.val) {
                flag = false;
                break;
            }
            pre = pre.next;
            slow = slow.next;
        }
        //将已经翻转的链表复原
        while (tempL != null) {
            ListNode tempLL = tempL.next;//设置一个next域,即保存前一个结点
            tempL.next = tempR;
            tempR = tempL;
            tempL = tempLL;
        }
        return flag;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
LeetCode-Editor是一种在线编码工具,它提供了一个用户友好的界面编写和运行代码。在使用LeetCode-Editor时,有时候会出现乱码的问题。 乱码的原因可能是由于编码格式不兼容或者编码错误导致的。在这种情况下,我们可以尝试以下几种解决方法: 1. 检查文件编码格式:首先,我们可以检查所编辑的文件的编码格式。通常来说,常用的编码格式有UTF-8和ASCII等。我们可以将编码格式更改为正确的格式。在LeetCode-Editor中,可以通过界面设置或编辑器设置来更改编码格式。 2. 使用正确的字符集:如果乱码是由于使用了不同的字符集导致的,我们可以尝试更改使用正确的字符集。常见的字符集如Unicode或者UTF-8等。在LeetCode-Editor中,可以在编辑器中选择正确的字符集。 3. 使用合适的编辑器:有时候,乱码问题可能与LeetCode-Editor自身相关。我们可以尝试使用其他编码工具,如Text Editor、Sublime Text或者IDE,看是否能够解决乱码问题。 4. 查找特殊字符:如果乱码问题只出现在某些特殊字符上,我们可以尝试找到并替换这些字符。通过仔细检查代码,我们可以找到导致乱码的特定字符,并进行修正或替换。 总之,解决LeetCode-Editor乱码问题的方法有很多。根据具体情况,我们可以尝试更改文件编码格式、使用正确的字符集、更换编辑器或者查找并替换特殊字符等方法来解决这个问题。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值