重学我的数据结构

news2025/1/25 9:13:20

二叉树

1. 遍历 (Traversal)

  • 前序遍历 (Preorder Traversal): 先访问根节点,再访问左子树,最后访问右子树。

    void preorderTraversal(Node root) {
        if (root == null) return; 
        System.out.print(root.value + " "); 
        preorderTraversal(root.left); 
        preorderTraversal(root.right); 
    } 

  • 中序遍历 (Inorder Traversal): 先访问左子树,再访问根节点,最后访问右子树。

    void inorderTraversal(Node root) { 
        if (root == null) return; 
        inorderTraversal(root.left); 
        System.out.print(root.value + " "); 
        inorderTraversal(root.right); 
    } 

  • 后序遍历 (Postorder Traversal): 先访问左子树,再访问右子树,最后访问根节点。

    void postorderTraversal(Node root) { 
        if (root == null) return;     
        postorderTraversal(root.left); 
        postorderTraversal(root.right); 
        System.out.print(root.value + " "); 
    } 
     

  • 层序遍历 (Level Order Traversal): 按层级顺序从上到下、从左到右访问节点,通常使用队列实现。

    void levelOrderTraversal(Node root) { 
        if (root == null) return; 
        Queue<Node> queue = new LinkedList<>(); 
        queue.add(root);     
        while (!queue.isEmpty()) { 
            Node current = queue.poll(); 
            System.out.print(current.value + " "); 
            if (current.left != null) queue.add(current.left); 
            if (current.right != null) queue.add(current.right);
       } 
    } 

2. 插入节点 (Insertion)

  • 向二叉树中插入节点的操作,通常涉及递归地找到合适的空位置,然后将新节点插入其中。
    Node insert(Node root, int value) { 
        if (root == null) { 
            return new Node(value); 
        } 
        if (value < root.value) { 
            root.left = insert(root.left, value); 
        } 
        else {     
            root.right = insert(root.right, value); 
        } 
        return root; 
    } 

3. 查找节点 (Search)

  • 在二叉树中查找一个特定值,通常也使用递归或迭代的方法。
    boolean search(Node root, int value) { 
        if (root == null) { 
            return false; 
        } 
        if (root.value == value) { 
            return true; 
        } 
        else if (value < root.value) { 
            return search(root.left, value); 
        } 
        else { 
            return search(root.right, value); 
        } 
    } 

4. 删除节点 (Deletion)

  • 删除一个节点后,可能需要调整二叉树以保持其性质。删除操作通常要处理三种情况:
    1. 删除的节点没有子节点。
    2. 删除的节点有一个子节点。
    3. 删除的节点有两个子节点(通常需要找到右子树中的最小值节点替代)。
    Node delete(Node root, int value) { 
        if (root == null) return null; 
        if (value < root.value) { 
            root.left = delete(root.left, value); 
        } 
        else if (value > root.value) { 
            root.right = delete(root.right, value); 
        } 
        else { 
            if (root.left == null) return root.right; 
            if (root.right == null) return root.left; 
            Node minNode = findMin(root.right); 
            root.value = minNode.value; 
            root.right = delete(root.right, minNode.value); 
        } 
        return root; 
    } 
    Node findMin(Node root) { 
        while (root.left != null) { 
            root = root.left; 
        } 
        return root; 
    } 

5. 计算高度 (Height Calculation)

  • 计算二叉树的高度,即从根节点到最远叶子节点的最长路径的节点数。
     java 

    复制代码

    int height(Node root) { 
        if (root == null) return 0; 
        int leftHeight = height(root.left); 
        int rightHeight = height(root.right); 
        return Math.max(leftHeight, rightHeight) + 1; 
    } 

6. 判断是否是二叉搜索树 (Check if a Tree is a Binary Search Tree)

例子:

假设你有一个树形结构,level 表示当前节点的层级:

 

markdown

复制代码

NodeValue

在这个例子中," ".repeat(4) 生成了 4 个空格,然后与 "NodeValue" 连接,最终输出了带有 4 个空格缩进的节点值。

 

java

复制代码

int level = 2; System.out.println(" ".repeat(level * 2) + "NodeValue");

输出将是:

  • 验证一棵树是否符合二叉搜索树的性质(即对于每个节点,其左子树的所有节点值都小于该节点值,右子树的所有节点值都大于该节点值)。
  • boolean isBST(Node root, Integer min, Integer max) { 
        if (root == null) return true; 
        if (min != null && root.value <= min) return false; 
        if (max != null && root.value >= max) return false; 
        return isBST(root.left, min, root.value) 
        && 
        isBST(root.right, root.value, max); 
    } 
    import java.util.Stack;
    
    class Node {
        int value;
        Node left, right;
    
        Node(int value) {
            this.value = value;
            left = right = null;
        }
    }
    
    public class BinaryTree {
        // 检查二叉树是否为 BST 的中序遍历方法
        boolean isBST(Node root) {
            Stack<Node> stack = new Stack<>();
            Node current = root;
            Integer prev = null;
    
            while (current != null || !stack.isEmpty()) {
                // 走到左子树的最下方
                while (current != null) {
                    stack.push(current);
                    current = current.left;
                }
    
                current = stack.pop();
    
                // 如果当前节点值小于等于前一个节点值,则不满足 BST 条件
                if (prev != null && current.value <= prev) {
                    return false;
                }
    
                // 更新 prev 为当前节点值
                prev = current.value;
    
                // 处理右子树
                current = current.right;
            }
    
            return true;
        }
    
        public static void main(String[] args) {
            BinaryTree tree = new BinaryTree();
            Node root = new Node(10);
            root.left = new Node(5);
            root.right = new Node(20);
            root.left.left = new Node(3);
            root.left.right = new Node(7);
    
            if (tree.isBST(root)) {
                System.out.println("这棵树是二叉搜索树");
            } else {
                System.out.println("这棵树不是二叉搜索树");
            }
        }
    }
    

    在 Java 中," ".repeat(level * 2) 是用来生成一个包含多个空格的字符串。这个方法的作用是重复特定数量的空格,并在输出中形成缩进效果。下面是详细解释:

    详细解释:

  • " ":

    • 这是一个包含单个空格字符的字符串。
  • repeat(int count):

    • repeat(int count)String 类的一个方法,它将当前字符串重复 count 次,并返回一个新的字符串。
    • Java 11 及以上版本引入了这个方法。
    • 如果 count 是 0,则返回一个空字符串。
  • level * 2:

    • level 是一个整数,表示树的当前层次(或深度)。
    • level * 2 表示将 level 乘以 2,得到重复空格的次数。
    • 这个倍数(2)可以调整,决定了每层缩进的程度。
  • 组合:

    • level 为 1 时," ".repeat(level * 2) 生成 " "(两个空格)。
    • level 为 2 时," ".repeat(level * 2) 生成 " "(四个空格)。
    • 依此类推,随着 level 增加,生成的空格数量也增加,这会使打印出来的树结构更清晰地表现出层次。
  • 根节点 level = 0,没有缩进。
  • 第一层子节点 level = 1,缩进两个空格。
  • 第二层子节点 level = 2,缩进四个空格。

7. 求树中节点的个数 (Count Nodes)

  • 计算二叉树中所有节点的数量。
    int countNodes(Node root) { 
        if (root == null) return 0; 
        return 1 + countNodes(root.left) + countNodes(root.right); 
    }

8. 求树中叶子节点的个数 (Count Leaf Nodes)

  • 计算二叉树中所有叶子节点的数量。
    int countLeafNodes(Node root) { 
        if (root == null) return 0; 
        if (root.left == null && root.right == null) return 1; 
        return countLeafNodes(root.left) + countLeafNodes(root.right); 
    } 

这些是与二叉树操作相关的一些基本方法,涵盖了遍历、插入、查找、删除、计算高度、验证二叉搜索树、以及统计节点数等常见操作。在实际应用中,可以根据具体需求进行组合和扩展。

目标一:进行中序遍历

class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;

    TreeNode() {}

    TreeNode(int val) {
        this.val = val;
    }

    TreeNode(int val, TreeNode left, TreeNode right) {
        this.val = val;
        this.left = left;
        this.right = right;
    }
}

// structure class definition with inorderTraversal method
public class Solution{
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> list = new ArrayList<>();
        TreeNode cur = null;

        while (root != null) {
            if (root.left != null) {
                //保证当前节点进入root左侧二叉树,然后一直向右走到尽头
                cur = root.left;
                while (cur.right != null && cur.right != root) {
                    cur = cur.right;
                }
                //让当前指针cur的右指针指向root即记录当前中间的节点
                if (cur.right == null) {
                    cur.right = root;
                    root = root.left;
                }
                //左子树遍历完成需要断开链接
                else {
                    list.add(root.val);
                    cur.right = null;
                    root = root.right;
                }
            } 
            //如果没有左孩子,就访问右孩子
            else {
                list.add(root.val);
                root = root.right;
            }
        }
        return list;
    }

    public static void main(String[] args) {
        // Construct a sample binary tree
        TreeNode root = new TreeNode(1);
        root.left = new TreeNode(2);
        root.right = new TreeNode(3);
        root.left.left = new TreeNode(4);
        root.left.right = new TreeNode(5);
        root.right.left = new TreeNode(6);
        root.right.right = new TreeNode(7);

        // Create an instance of Structure
        Solution structure = new Solution();

        // Perform inorder traversal
        List<Integer> result = structure.inorderTraversal(root);

        // Print the result
        System.out.println("Inorder Traversal: " + result);
    }
}
Inorder Traversal: [4, 2, 5, 1, 6, 3, 7]

Process finished with exit code 0
public class Solution{
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> list = new ArrayList<>();
        TreeNode cur = null;
        Deque<TreeNode> stk = new LinkedList<>();

        while (root!=null || !stk.isEmpty()){
            while (root!=null){
                stk.push(root);
                root = root.left;
            }
            root = stk.pop();
            list.add(root.val);
            root = root.right;
            
            //这里有一点设计的很巧妙,就是如果在左子树的最后一个节点,由于没有子节点,while循环不会执行,root会重新接受一个新弹出的上级元素并开始寻找右子节点

        }
        return list;
    }
}

先序遍历

import java.util.ArrayList;
import java.util.Deque;
import java.util.LinkedList;
import java.util.List;

// 定义树节点
class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;
    TreeNode(int x) { val = x; }
}

public class Solution {
    // 前序遍历方法
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<>(); // 存储遍历结果
        if (root == null) {
            return res; // 如果根节点为空,直接返回空列表
        }

        Deque<TreeNode> stack = new LinkedList<>(); // 创建栈来帮助遍历
        TreeNode node = root; // 从根节点开始遍历

        while (!stack.isEmpty() || node != null) { // 当栈不为空或当前节点不为空时
            while (node != null) { // 处理当前节点及其左子树
                res.add(node.val); // 访问当前节点,添加到结果列表中
                stack.push(node); // 将当前节点压入栈中
                node = node.left; // 继续遍历左子树
            }
            node = stack.pop(); // 从栈中弹出一个节点(左子树遍历完成)
            node = node.right; // 继续遍历右子树
        }

        return res; // 返回最终的前序遍历结果
    }
}

     1
    / \
   2   3
  / \
 4   5

- 初始状态:`node = 1`, `stack = []`, `res = []`
- `node = 1`, `stack = [1]`, `res = [1]`
- `node = 2`, `stack = [1, 2]`, `res = [1, 2]`
- `node = 4`, `stack = [1, 2, 4]`, `res = [1, 2, 4]`
- `node = null`, 弹出 `4`, `node = null`
- 弹出 `2`, `node = 5`
- `node = null`, `stack = [1, 5]`, `res = [1, 2, 4, 5]`
- `node = null`, 弹出 `5`, `node = null`
- 弹出 `1`, `node = 3`
- `node = null`, `stack = []`, `res = [1, 2, 4, 5, 3]`

最终结果是 `[1, 2, 4, 5, 3]`。

力扣 3174 是个讨厌的字符串,看看相关方法吧,这里不想放

20, 有效括号的代码:这里主要是学习建立指定值的HashMap

class Solution {
    public boolean isValid(String s) {
        int n = s.length();
        if (n % 2 == 1)
        {
            return false;
        }
        Map<Character,Character> parirs = new HashMap<Character,Character>(){{
            put(')','(');
            put(']','[');
            put('}','{');
        }};

        Deque<Character> stack = new LinkedList<Character>();
        for(int i = 0; i<n ; i++){
            char ch = s.charAt(i);
            if(parirs.containsKey(ch)){
                if(stack.isEmpty() || stack.peek() != parirs.get(ch)){
                    return false;
                }
                stack.pop();
            }else{
                stack.push(ch);
            }
        }
        return stack.isEmpty();
    }
}

在Java中,Deque(双端队列)和Stack(栈)都可以用于实现栈的功能,但它们在设计、使用和推荐的场景上有所不同。以下是它们的主要区别:

1. 设计意图和历史

  • StackStack是Java中最早引入的栈类,继承自Vector。它是一个同步的类,也就是说,它在多线程环境中是线程安全的。但是,由于Stack继承了Vector,它带有Vector的一些不必要的方法,比如insertElementAt(),这与栈的基本操作无关。这使得Stack被认为是一种设计不良的选择。
  • DequeDeque接口是在Java 1.6中引入的,代表了一个双端队列,可以用于栈的实现。Deque接口比Stack更灵活,可以作为栈(LIFO)或队列(FIFO)使用。常见的实现类如ArrayDequeLinkedList

2. 推荐使用

  • Stack:由于Stack类继承自Vector,它的同步性带来了一些不必要的开销。在现代Java编程中,Stack通常被认为是过时的,不推荐在新代码中使用。
  • DequeDeque接口是实现栈功能的推荐选择。尤其是ArrayDeque,它比Stack性能更高,也没有同步开销。在单线程环境中使用Deque的实现类来实现栈功能是更好的选择。

3. 操作方法

  • Stack
    • push(E item):将元素压入栈顶。
    • pop():移除并返回栈顶的元素。
    • peek():查看栈顶的元素,但不移除。
  • Deque
    • push(E item):将元素压入栈顶。
    • pop():移除并返回栈顶的元素。
    • peek():查看栈顶的元素,但不移除。
    • addFirst(E e)/removeFirst():也可以用这些方法来模拟栈操作。

虽然两者的方法名称略有不同,但Deque的实现类(如ArrayDeque)提供的功能与Stack基本一致,并且在性能和设计上更优。

4. 性能

  • Stack:由于是同步的,性能可能会比非同步的Deque慢,尤其是在不需要线程安全的情况下。
  • Deque:通常情况下,ArrayDeque的性能要优于Stack,因为它是非同步的,并且没有Vector所带来的额外开销。

结论:

在现代Java开发中,使用Deque(特别是ArrayDeque)来实现栈的功能是推荐的做法,而不是使用过时的Stack类。

Map<Character,Character> parirs = new HashMap<Character,Character>(){{
            put(')','(');
            put(']','[');
            put('}','{');
        }};这里的put方法里面内容可以写成这样吗put('(',')');

在你给出的代码中,put(')', '('); 的意思是将右括号 ) 作为键,左括号 ( 作为值存储在 Map 中。这个 Map 的用法是将闭合括号(右括号)映射到对应的开括号(左括号),通常用来验证字符串中括号是否匹配。

如果你写成 put('(', ')');,则会将左括号 ( 作为键,右括号 ) 作为值,这样的映射关系就反过来了。

区别在于使用场景:

  • put(')', '(');

    • 适用于你想通过右括号查找对应的左括号的场景。
    • 例如,用于检查括号匹配时,弹出栈顶元素并对比当前的右括号,看它是否与预期的左括号匹配。
  • put('(', ')');

    • 适用于你想通过左括号查找对应的右括号的场景。
    • 例如,在某些括号匹配算法中,你可能需要根据当前左括号找到预期的右括号。

总结:

你可以写成 put('(', ')');,但这取决于你要实现的逻辑。如果你想通过左括号来查找右括号,这样写是合理的;如果你想通过右括号来查找左括号,那就应该用 put(')', '(');

队列相关方法与函数

在算法题中,队列是一种非常常用的数据结构,主要用于广度优先搜索(BFS)、滑动窗口、任务调度等场景。以下是一些常用的队列相关方法和它们的典型应用:

1. 基本操作

  • offer(E e) / add(E e): 将元素添加到队列尾部。
    • offer() 在添加失败时返回 false,而 add() 在失败时会抛出异常。
    • 用途:将新元素加入到处理队列中。
  • poll(): 移除并返回队列头部的元素,如果队列为空,则返回 null
    • 用途:处理或访问队列头部元素,同时将其从队列中移除。
  • remove(): 移除并返回队列头部的元素,如果队列为空,则抛出异常。
    • 用途:和 poll() 类似,但在队列为空时会抛出异常。
  • peek(): 查看队列头部的元素但不移除它,如果队列为空,则返回 null
    • 用途:查看即将处理的元素,而不移除它。
  • element(): 查看队列头部的元素但不移除它,如果队列为空,则抛出异常。
    • 用途:和 peek() 类似,但在队列为空时会抛出异常。

2. 双端队列操作

  • addFirst(E e): 在队列的头部添加元素。
  • addLast(E e): 在队列的尾部添加元素(等同于 add(E e))。
  • removeFirst(): 移除并返回队列头部的元素。
  • removeLast(): 移除并返回队列尾部的元素。
  • peekFirst(): 查看但不移除队列头部的元素。
  • peekLast(): 查看但不移除队列尾部的元素。
  • pollFirst(): 移除并返回队列头部的元素,如果队列为空,则返回 null
  • pollLast(): 移除并返回队列尾部的元素,如果队列为空,则返回 null

双端队列Deque)的这些操作在处理滑动窗口问题时非常有用,例如求最大值、最小值等。

3. 常见应用场景

  • 广度优先搜索(BFS):

    • 使用队列来实现层次遍历或最短路径搜索。
    • 常用方法:offer(E e)poll()peek()
  • 滑动窗口问题:

    • 例如,滑动窗口最大值问题。
    • 常用方法:addLast(E e)pollFirst()peekFirst(),配合双端队列 Deque 使用。
  • 循环队列:

    • 用来处理循环任务,模拟环形结构。
    • 常用方法:offer(E e)poll()peek()
  • 任务调度:

    • 使用队列管理任务执行的顺序,尤其是在处理依赖关系或资源调度问题时。
    • 常用方法:offer(E e)poll()peek()

4. 具体实现类

  • LinkedList: 实现了 QueueDeque 接口,适合需要频繁插入和删除操作的场景。
  • ArrayDeque: 比 LinkedList 更高效的实现,特别适合用于栈和队列操作。
  • PriorityQueue: 基于优先级的队列,通常用于贪心算法、Dijkstra 算法等场景。

5. 线程安全的队列

  • ConcurrentLinkedQueue: 适用于多线程环境的无界非阻塞队列。
  • BlockingQueue: 提供阻塞操作的队列接口,适用于生产者-消费者模式。

常用的实现:

  • ArrayBlockingQueue
  • LinkedBlockingQueue
  • PriorityBlockingQueue
  • DelayQueue

这些方法和场景涵盖了大多数常见的算法题中的队列使用情况。了解这些操作的具体应用有助于更好地解题和优化代码。

字符串相关

Java 中的 String 类是不可变的(即一旦创建,内容无法改变),并且是最常用的类之一。以下是 String 类的一些常用方法及其典型用途:

1. 字符串创建与初始化

  • String s = "Hello";: 直接赋值创建字符串。
  • String s = new String("Hello");: 使用构造函数创建字符串。

2. 字符串长度

  • length(): 返回字符串的长度。
     java 

    复制代码

    String s = "Hello"; int len = s.length(); // len = 5

3. 字符串比较

  • equals(Object obj): 比较字符串的内容是否相等(区分大小写)。
       

    s1.equals(s2);

  • equalsIgnoreCase(String anotherString): 比较字符串的内容是否相等(不区分大小写)。
     

    s1.equalsIgnoreCase(s2);

  • compareTo(String anotherString): 按字典顺序比较两个字符串。
       

    s1.compareTo(s2); // 返回负数、零或正数

  • compareToIgnoreCase(String str): 按字典顺序比较两个字符串(不区分大小写)。
       

    s1.compareToIgnoreCase(s2);

4. 查找与定位

  • charAt(int index): 返回指定索引处的字符。
       

    char c = s.charAt(1); // c = 'e'

  • indexOf(String str): 查找子字符串首次出现的位置。
       

    int index = s.indexOf("lo"); // index = 3

  • lastIndexOf(String str): 查找子字符串最后一次出现的位置。
       

    int index = s.lastIndexOf("l"); // index = 3

  • contains(CharSequence s): 判断字符串是否包含某子字符串。
       

    boolean contains = s.contains("He"); // contains = true

5. 字符串替换

  • replace(char oldChar, char newChar): 替换所有出现的指定字符。
       

    String replaced = s.replace('l', 'p'); // replaced = "Heppo"

  • replace(CharSequence target, CharSequence replacement): 替换所有出现的指定字符串。
       

    String replaced = s.replace("He", "We"); // replaced = "Wello"

  • replaceAll(String regex, String replacement): 使用正则表达式替换。
       

    String replaced = s.replaceAll("l+", "p"); // replaced = "Hepo"

  • replaceFirst(String regex, String replacement): 使用正则表达式替换首个匹配。
       

    String replaced = s.replaceFirst("l+", "p"); // replaced = "Hepo"

6. 字符串分割

  • split(String regex): 按指定正则表达式分割字符串,返回一个数组。
       

    String[] parts = s.split("l"); // parts = ["He", "", "o"]

  • split(String regex, int limit): 按指定正则表达式分割字符串,并限制结果数组的长度。
       

    String[] parts = s.split("l", 2); // parts = ["He", "lo"]

7. 字符串截取

  • substring(int beginIndex): 返回从指定索引开始到字符串末尾的子字符串。
       

    String sub = s.substring(2); // sub = "llo"

  • substring(int beginIndex, int endIndex): 返回从 beginIndex 开始到 endIndex(不包括)之间的子字符串。
       

    String sub = s.substring(1, 4); // sub = "ell"

8. 字符串转换

  • toLowerCase(): 将字符串转换为小写。
       

    String lower = s.toLowerCase(); // lower = "hello"

  • toUpperCase(): 将字符串转换为大写。
       

    String upper = s.toUpperCase(); // upper = "HELLO"

  • trim(): 去除字符串首尾的空白字符。
       

    String trimmed = s.trim(); // trimmed = "Hello"(如果 s 包含前后空格)

  • valueOf(int i): 将其他数据类型转换为字符串。
       

    String str = String.valueOf(123); // str = "123"

9. 字符串拼接

  • concat(String str): 拼接两个字符串。
       

    String concatenated = s.concat(" World"); // concatenated = "Hello World"

  • + 运算符: 直接使用 + 拼接字符串。
       

    String concatenated = s + " World"; // concatenated = "Hello World"

10. 字符串判断

  • isEmpty(): 判断字符串是否为空(长度为0)。
       

    boolean empty = s.isEmpty(); // empty = false

  • isBlank(): 判断字符串是否为空或仅包含空白字符(Java 11+)。
       

    boolean blank = s.isBlank(); // blank = false

  • startsWith(String prefix): 判断字符串是否以指定前缀开头。
       

    boolean starts = s.startsWith("He"); // starts = true

  • endsWith(String suffix): 判断字符串是否以指定后缀结尾。
       

    boolean ends = s.endsWith("lo"); // ends = true

11. 格式化字符串

  • String.format(String format, Object... args): 返回格式化后的字符串。
     java 

    复制代码

    String formatted = String.format("Hello %s, your age is %d", "John", 25); // formatted = "Hello John, your age is 25"

12. 字符串内容判断

  • matches(String regex): 判断字符串是否匹配正则表达式。
     java 

    复制代码

    boolean match = s.matches("[A-Za-z]+"); // match = true

13. 字符数组操作

  • toCharArray(): 将字符串转换为字符数组。
     java 

    复制代码

    char[] chars = s.toCharArray(); // chars = ['H', 'e', 'l', 'l', 'o']

  • getBytes(): 将字符串转换为字节数组。
     java 

    复制代码

    byte[] bytes = s.getBytes(); // 根据平台的默认字符编码转换

  • char 与字符串的区别

  • char 是单个字符

    • char 类型表示单个字符,例如 'A''3''@'。它只能存储一个字符,而不是一组字符。
  • 字符串是多个字符的组合

    • 字符串通常是由多个字符组成的字符序列。字符串可以由多个 char 组成,通常用双引号括起来,例如 "Hello""12345""@!$"
    • 在 C 语言中,字符串通常是由字符数组(例如 char str[] = "Hello";)或指向字符的指针(例如 char *str = "Hello";)表示。

 . - 力扣(LeetCode)

public class Solution {
    public int numIslands(char[][] grid) {
        // 将 char[][] 转换为 int[][]
        int[][] intGrid = new int[grid.length][grid[0].length];
        for (int r = 0; r < grid.length; r++) {
            for (int c = 0; c < grid[0].length; c++) {
                intGrid[r][c] = grid[r][c] - '0';  // '1' 转换为 1, '0' 转换为 0
            }
        }
        
        // 调用原有的 numIslands 方法
        return numIslands(intGrid);
    }

    private int numIslands(int[][] grid) {
        int islandCount = 0;
        
        for (int r = 0; r < grid.length; r++) {
            for (int c = 0; c < grid[0].length; c++) {
                if (grid[r][c] == 1) {
                    dfs(grid, r, c);
                    islandCount++;
                }
            }
        }
        
        return islandCount;
    }

    private void dfs(int[][] grid, int r, int c) {
        if (!inArea(grid, r, c)) {
            return;
        }
        if (grid[r][c] != 1) {
            return;
        }
        grid[r][c] = 2;
        
        dfs(grid, r - 1, c);
        dfs(grid, r + 1, c);
        dfs(grid, r, c - 1);
        dfs(grid, r, c + 1);
    }

    private boolean inArea(int[][] grid, int r, int c) {
        return 0 <= r && r < grid.length 
            	&& 0 <= c && c < grid[0].length;
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2041067.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

韦东山瑞士军刀项目之I2C控制ssd 1306 OLED显示屏幕驱动源码分析(硬核)

太硬核了&#xff0c;但即便如此&#xff0c;我也只分析了如何实现ssd 1306的控制命令与显示命令的代码。尽管如此&#xff0c;我也了解了如何实现I2C的字符输出显示。意外收获是知道了ASCII码到底是个什么玩意儿。

python pygame如何实现碰撞检测

前言&#xff1a; 在python中&#xff0c;我们实现两个物品的碰撞检测往往是判断两个物体的x、y坐标是否有重合&#xff0c;根据坐标来进行判断&#xff0c;但是这种判断方式往往不太准确&#xff0c;对于一些透明部分&#xff0c;会出现误判的情况&#xff0c;今天介绍的是一…

古印度的未解之谜——哈拉帕印章文字

关注我们 - 数字罗塞塔计划 - 在之前的文章中&#xff0c;我们知晓了古埃及莎草纸的制作工艺&#xff08;参见《莎草纸——数千年前的信息记录载体》&#xff09;&#xff0c;也了解了由粘土变为陶片可保存数千年的苏美尔泥板书&#xff08;参见《泥板书&#xff1a;两河文明传…

暑期破防实录——捡漏腾讯

序 经历了整整三个月的折磨&#xff0c;暑期实习终于尘埃落定。 其实还没收到 offer 的时候&#xff0c;还会想着到时候录用了该怎么大写特写小作文&#xff0c;但真到了这一天&#xff0c;只剩下一种解脱感&#xff0c;一种摆脱了漫长的焦虑与压抑的淡淡喜悦。 或许就像久病…

Java垃圾收集底层算法实现

垃圾收集底层算法实现 三色标记 在并发标记的过程中&#xff0c;因为标记期间应用线程还在继续跑&#xff0c;对象间的引用可能发生变化&#xff0c;多标和漏标的情况就有可能发生。漏标的问题主要引入了三色标记算法来解决。 三色标记算法是把Gc roots可达性分析遍历对象过…

QT翻金币小游戏

目录 QT翻金币小游戏 效果展示 图片 视频 实现代码 main.cpp mymainwindow.h mymainwindow.cpp startscene.h startscene.cpp selectscene.cpp playscene.h playscene.cpp mypushbutton.h mypushbutton.cpp dataconfig.h dataconfig.cpp QT翻金币小游戏 效果展示…

什么是进程?C语言

进程的概念 进程就是执行中的程序&#xff0c;是系统资源分配的最小单位。 进程的内存分配 进程的作用 宏观上是并行的&#xff0c;微观上是串行的 进程的状态 对于基本的操作系统&#xff1a;有三个状态&#xff1a; 就绪态->执行态-> 阻塞态 在LInux中有四种&am…

docker数据卷、资源控制

一、docker数据卷&#xff1a; 1.容器和宿主机之间数据共享----挂载卷----容器内的目录和宿主机的目录进行挂载。实现数据文件共享容器的生命周期有限&#xff0c;一旦重启所有对容器内部文件数据的修改以及保存的数据都会被初始化&#xff0c;所以为了防止数据丢失重要的组件…

前端学习大纲 | 主流前端技术 | 学习路线

需要完整的学习路线的宝子可以点击获取&#xff1a;点击即可获取完整的学习路线 第一阶段&#xff08;页面还原能力&#xff09; HTML5、CSS3、Git 第二阶段&#xff08;专攻 JS 逻辑能力&#xff09; JavaScript 基础、JavaScript 进阶、JavaScript 高级、ES6 第三阶段&a…

【Java】如何使用jdbc连接并操作MySQL,一文读懂不迷路,小白也能轻松学会

JDBC的原理 JDBC&#xff08;Java Database Connectivity&#xff09;是Java提供的用于连接和操作数据库的API。它允许Java应用程序与各种数据库进行交互&#xff0c;以下是JDBC的基本原理&#xff1a; 驱动程序管理&#xff1a;JDBC使用不同的数据库驱动程序来连接不同类型的…

微信小程序--24(列表渲染)

一、wx&#xff1a;for 1.作用 根据指定数组&#xff0c;循环渲染重复的组件结构 2.语法 <view wx:for"{{data中的数据}}"> 索引是&#xff1a;{{index}}, item项是&#xff1a;{{item}}</view> index:表索引item&#xff1a;表当前循环项 …

【网络】局域网LAN、广域网WAN、TCP/IP协议、封装和分用

文章目录 局域网 LAN广域网 WAN网络中的重要概念IP 地址端口号 认识协议协议分层是什么OSI 七层网络模型TCP/IP 五层网络模型&#xff08;或四层&#xff09;物理层传输层网络层数据链表层应用层网络设备所在分层 封装和分用[站在发送方视角]&#xff08;封装&#xff09;[站在…

新工种,AI商业化变现思路

本文由 ChatMoney团队出品 AI变现&#xff0c;你我都能成为创收高手! 不必是科技大咖&#xff0c;也无需深厚背景&#xff0c;让我们一起探索Chatmoney全能知识库AI的奥秘&#xff0c;轻松步入收益之门! 想象一下&#xff0c;你的智慧和创意通过ChatmoneyAI技术转化为可观的收益…

如何使用 Go 连接 MO

MatrixOne 是一款超融合异构分布式数据库&#xff0c;与 MySQL 高度兼容&#xff0c;通过云原生化和存储、计算、事务分离的架构构建 HSTAP 超融合数据引擎&#xff0c;实现单一数据库系统支持 OLTP、OLAP、流计算等多种业务负载&#xff0c;通过为用户提供一站式超融合数据解决…

【数据结构与算法】最短路径算法

最短路径算法目录 一.什么是最短路径二.最短路径算法的实现1.准备工作2.拆解为子问题——递归 三.完整代码 一.什么是最短路径 顾名思义根据需求,可以获取的最优的路径. 比如说: 我标的数值,就是时间,那么假如我们是A点到D点. 那么我们可以看到有三条路径: A->E->D所花…

AI绘画:一篇文章带你解析Stable Diffusion 原理!

前言 Stable Diffusion原理 1. Stable Diffusion能做什么 直白地说&#xff0c;SD是一个text-to-image模型&#xff0c;通过给定text prompt&#xff08;文本提示词&#xff09;&#xff0c;它可以返回一个匹配文本的图片。 2. Diffusion 模型 Stable Diffusion属于深度学习…

2003-2023年高铁线路信息数据

2003-2023年高铁线路信息数据 1、时间&#xff1a;2003-2023年 2、来源&#xff1a;高铁航线数据库&#xff08;Chinese High-speed Rail and Airline Database&#xff0c;CRAD&#xff09; 3、指标&#xff1a;高铁线路名称、起点名、终点名、开通时间、线路长度(km)、设计…

直接插入排序(C语言)

一、图解 思想: 把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中&#xff0c;直到所有的记录插入完为 止&#xff0c;得到一个新的有序序列 。 当插入第i(i>1)个元素时&#xff0c;前面的array[0],array[1],…,array[i-1]已经排好序&#xff0c;此时…

解决idea中注释部分的中文乱码问题

问题背景&#xff1a; application.properties注释部分突然出现中文乱码问题&#xff0c;重启idea仍乱码&#xff0c;如下&#xff1a; 解决方案&#xff1a;设置UTF-8 注意不要漏步骤&#xff0c;设置好后重启idea 如果还不行&#xff0c;说明可能是在文件保存的时候没有按U…

vue3 快速入门 (七) : Vue打包并部署到Nginx服务器上

1. 本文环境 Vue版本 : 3.4.29Node.js版本 : v20.15.0系统 : Windows11 64位IDE : VsCode 2. vue打包&#xff0c;减少体积 打包之前我们可以对包的体积进行一些优化&#xff0c;比如可以实现自动按需引入、开启图片压缩、文件压缩等&#xff0c;具体详见这篇文章 : 分享基…