Java图的遍历知识点(含面试大厂题和源码)

news/2024/7/20 22:22:14 标签: java, 面试, 深度优先

图的遍历是图论中的一个基本概念,主要指的是按照某种规则,系统地访问图中的每一个顶点,且每个顶点仅被访问一次。图遍历的主要目的是为了搜索图中的信息或检查图中是否存在特定的路径或圈。图的遍历算法主要有两种:深度优先搜索(DFS)和广度优先搜索(BFS)。

深度优先搜索(DFS)

深度优先搜索算法从图中的某个顶点开始,探索尽可能深的分支,直到找到目标顶点,或者遇到没有未探索的相邻顶点为止。然后回溯,探索下一个分支。

  • 算法步骤
    1. 从起始顶点开始,将其标记为已访问。
    2. 选择一个与当前顶点相连的未访问过的顶点,从该顶点再次执行DFS。
    3. 重复步骤2,直到没有未访问的顶点为止。
    4. 如果还有其他未访问的顶点,选择一个未访问的顶点,重复步骤1。

DFS常用于寻找所有可达的顶点、拓扑排序、检测环等。

广度优先搜索(BFS)

广度优先搜索算法从图的某个顶点开始,先访问距离起点最近的顶点,然后是次近的,依此类推,直到所有顶点都被访问过。

  • 算法步骤
    1. 从起始顶点开始,将其标记为已访问,并加入队列中。
    2. 从队列中取出一个顶点,并访问其所有未访问的邻接顶点,将它们标记为已访问,并加入队列。
    3. 重复步骤2,直到队列为空。

BFS常用于寻找最短路径、层次遍历、广播消息等。

实现注意点

  • 存储结构:图的遍历可以在图的两种主要存储结构上实现,即邻接矩阵和邻接表。
  • 避免重复访问:无论是DFS还是BFS,在访问顶点时都需要标记顶点,避免重复访问。
  • 选择起点:图的遍历可以从图中任意一个顶点开始,但不同的起点可能导致遍历的结果不同。

图的遍历是理解和实现更复杂图算法的基础,掌握DFS和BFS对于深入学习图论和解决实际问题非常重要。以下是三道常见的面试题目,涵盖了不同的编程概念和技术点,每个题目都附带了Java实现的源码。

题目1: 二叉树的右视图

题目描述:给定一个二叉树,想象自己站在它的右侧,按照从顶部到底部的顺序,返回从右侧所能看到的节点值。

示例

输入: [1,2,3,null,5,null,4]
输出: [1, 3, 4]

解题思路:使用广度优先搜索(BFS)进行层序遍历,每层遍历到的最后一个节点就是从右侧看到的节点。

Java代码实现

java">import java.util.*;

class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;
    TreeNode(int x) { val = x; }
}

public class Solution {
    public List<Integer> rightSideView(TreeNode root) {
        List<Integer> result = new ArrayList<>();
        if (root == null) return result;
        
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        
        while (!queue.isEmpty()) {
            int levelSize = queue.size();
            for (int i = 0; i < levelSize; i++) {
                TreeNode currentNode = queue.poll();
                if (i == levelSize - 1) {
                    result.add(currentNode.val);
                }
                if (currentNode.left != null) queue.offer(currentNode.left);
                if (currentNode.right != null) queue.offer

(currentNode.right);
            }
        }
        return result;
    }
}

题目2: LRU缓存机制

题目描述:设计和实现一个 LRU (最近最少使用) 缓存机制。实现 LRUCache 类:

  • LRUCache(int capacity) 以正整数作为容量 capacity 初始化 LRU 缓存
  • int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1
  • void put(int key, int value) 如果关键字已经存在,则变更其数据值;如果关键字不存在,则插入该组 key-value。当缓存容量达到上限时,它应该在写入新数据之前删除最久未使用的数据值,从而为新的数据值留出空间。

解题思路:使用哈希表加双向链表的方式实现。哈希表用于快速定位节点位置,双向链表用于表示节点间的先后顺序,便于快速添加和删除节点。

Java代码实现

java">import java.util.*;

class LRUCache {

    class DLinkedNode {
        int key;
        int value;
        DLinkedNode prev;
        DLinkedNode next;
        public DLinkedNode() {}
        public DLinkedNode(int _key, int _value) {key = _key; value = _value;}
   

    private Map<Integer, DLinkedNode> cache = new HashMap<>();
    private int size;
    private int capacity;
    private DLinkedNode head, tail;

    public LRUCache(int capacity) {
        this.size = 0;
        this.capacity = capacity;
        // 使用伪头部和伪尾部节点
        head = new DLinkedNode();
        tail = new DLinkedNode();
        head.next = tail;
        tail.prev = head;
    }
    
    public int get(int key) {
        DLinkedNode node = cache.get(key);
        if (node == null) return -1;
        // 如果 key 存在,先通过哈希表定位,再移动到头部
        moveToHead(node);
        return node.value;
    }
    
    public void put(int key, int value) {
        DLinkedNode node = cache.get(key);
        if (node == null) {
            // 如果 key 不存在,创建一个新的节点
            DLinkedNode newNode = new DLinkedNode(key, value);
            // 添加进哈希表
            cache.put(key, newNode);
            // 添加至双向链表的头部
            addToHead(newNode);
            ++size;
            if (size > capacity) {
                // 如果超出容量,删除双向链表的尾部节点
                DLinkedNode tail = removeTail();
                // 删除哈希表中对应的项
                cache.remove(tail.key);
                --size;
            }
        } else {
            // 如果 key 存在,先通过哈希表定位,再修改 value,并移动到头部
            node.value = value;
            moveToHead(node);
        }
    }
    
    private void addToHead(DLinkedNode node) {
        node.prev = head;
        node.next = head.next;
        head.next.prev = node;
        head.next = node;
    }
    
    private void removeNode(DLinkedNode node) {
        node.prev.next = node.next;
        node.next.prev = node.prev;
    }
    
    private void moveToHead(DLinkedNode node) {
        removeNode(node);
        addToHead(node);
    }
    
    private DLinkedNode removeTail() {
        DLinkedNode res = tail.prev;
        removeNode(res);
        return res;
    }
}

题目3: 合并K个升序链表

题目描述:给你一个链表数组,每个链表都已经按升序排列。请你将所有链表合并到一个升序链表中,返回合并后的链表。

示例

输入:lists = [[1,4,5],[1,3,4],[2,6]]
输出:[1,1,2,3,4,4,5,6]
解释:链表数组如上所示,将它们合并到一个升序链表中得到。

解题思路:可以使用优先队列来优化合并过程。优先队列可以帮助我们每次从各个链表中选出当前最小的元素添加到结果链表中。

Java代码实现

java">class ListNode {
    int val;
    ListNode next;
    ListNode() {}
    ListNode(int val) { this.val = val; }
    ListNode(int val, ListNode next) { this.val = val; this.next = next; }
}

public class Solution {
    public ListNode mergeKLists(ListNode[] lists) {
        if (lists == null || lists.length == 0) return null;
        PriorityQueue<ListNode> queue = new PriorityQueue<>(lists.length, (a, b) -> a.val - b.val);
        ListNode dummy = new ListNode(0);
        ListNode tail = dummy;
        
        for (ListNode node : lists) {
            if (node != null) queue.add(node);
        }
        
        while (!queue.isEmpty()) {
            tail.next = queue.poll();
            tail = tail.next;
            if (tail.next != null) queue.add(tail.next);
        }
        
        return dummy.next;
    }
}

这三个题目分别涉及到二叉树的遍历、设计数据结构以及使用优先队列合并链表,是面试中常见的题型,熟练掌握它们对面试有很大帮助。


http://www.niftyadmin.cn/n/5449070.html

相关文章

如何配置元数据?(如何使用Spring容器)

目录 一、引出问题&#xff08;如何配置元数据&#xff1f;&#xff09;二、没有Spring的时代三、XML配置文件&#xff08;xml配bean&#xff09;1 格式1.1 示例 2 实例化一个Spring容器3 使用Spring容器4 后言 四、基于注解的配置 【[1.9. Annotation-based Container Configu…

Mac电脑高清媒体播放器:Movist Pro for mac下载

Movist Pro for mac是一款专为Mac操作系统设计的高清媒体播放器&#xff0c;支持多种常见的媒体格式&#xff0c;包括MKV、AVI、MP4等&#xff0c;能够流畅播放高清视频和音频文件。Movist Pro具有强大的解码能力和优化的渲染引擎&#xff0c;让您享受到更清晰、更流畅的观影体…

代码随想录算法训练营第day55|583. 两个字符串的删除操作 、72. 编辑距离

目录 583. 两个字符串的删除操作 72. 编辑距离 583. 两个字符串的删除操作 力扣题目链接 给定两个单词 word1 和 word2&#xff0c;找到使得 word1 和 word2 相同所需的最小步数&#xff0c;每步可以删除任意一个字符串中的一个字符。 示例&#xff1a; 输入: "sea&…

SpringCloud-Feign远程调用

使用Feign替代RestTemplate进行远程服务调用&#xff1a; 远程调用配置 1. 引入依赖 我们在order-service服务的pom文件中引入feign的依赖&#xff1a; <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starte…

QTabWidget的tabbar不同方向显示 文字方向设置 图标跟随变化 实现方式 qt控件绘制原理

先来看结果图&#xff1a;&#xff08;参考博客&#xff1a;QTabWidget中tab页文本水平或垂直设置_pyqt tab_widget.settabposition(qtabwidget.west) 字体-CSDN博客&#xff09; 从图中可知&#xff0c;"普通"是qt自己的样式&#xff0c;但是很明显&#xff0c;在垂…

Kotlin的lateinit关键词

Kotlin的lateinit关键词 lateinit&#xff0c;延迟初始化。有时&#xff0c;并不能定义一个变量或对象值为空&#xff0c;而也没办法在对象或变量在定义声明时就为它赋值初始化&#xff0c;那么这时就需要用到Kotlin提供的延迟初始化lateinit。比如&#xff0c;有些依赖注入框架…

c++ aria2命令行下载实例

采用aria2命令行告诉下载&#xff0c;示例代码如下所示&#xff1a; 1、配置aria2目录&#xff0c;网上下载即可 2、 CString strPath L"C:\\Users\\14713\\Desktop\\Aria2Sample\\Debug\\aria2"; SetCurrentDirectory(strPath); std::wstring strCmd L…

Spring单元测试+Mockito

一&#xff0c;背景 单元测试基本上是开发逃不过的一个工作内容&#xff0c;虽然往往因为过于无聊&#xff0c;或者过于麻烦&#xff0c;而停止于项目的迭代之中&#xff0c;不了了之了。其实不是开发们懒&#xff0c;而是上头要求的测试覆盖率高&#xff0c;但是又没有好用的…