java中的二叉树,图和树的深度和广度搜索

java中的二叉树

创建一个基本的二叉树,其中节点的插入不依赖于任何特定的排序规则(例如二叉搜索树的规则),你可以设计一个简单的插入逻辑,比如总是向最左侧未填满的位置插入节点。以下是一个简单的Java实现,使用队列来辅助按层次顺序插入节点,不进行大小比较。

1.创建二叉树

步骤 1: 定义 TreeNode 类

首先,定义TreeNode类,这个类将包含数据和指向左右子节点的引用。

左右节点都为TreeNode类

public class TreeNode {
    int data;
    TreeNode left;
    TreeNode right;

    public TreeNode(int data) {
        this.data = data;
        this.left = null;
        this.right = null;
    }
}
步骤 2: 创建 BinaryTree 类

接下来,创建一个BinaryTree类,其中包含插入和层次遍历(用于验证树的结构)的方法。

import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;

public class BinaryTree {
    TreeNode root;

    public BinaryTree() {
        root = null;
    }

    public void insert(int data) {
        if (root == null) {
            root = new TreeNode(data);
            return;
        }

        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);

        while (!queue.isEmpty()) {
            TreeNode temp = queue.poll();
          //将队列头取出并移除,判断队列头节点的左子节点是否为空,不为空加入队列,然后判断右节点
            if (temp.left == null) {
                temp.left = new TreeNode(data); //新建左节点,连接上一节点
                break;
            } else {
                queue.offer(temp.left);
            }

            if (temp.right == null) {
                temp.right = new TreeNode(data);
                break;
            } else {
                queue.offer(temp.right);
            }
        }
    }

    public void printLevelOrder() {
        if (root == null) return;

        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        while (!queue.isEmpty()) {
            TreeNode temp = queue.poll();
            System.out.print(temp.data + " ");
            if (temp.left != null) {
                queue.offer(temp.left);
            }
            if (temp.right != null) {
                queue.offer(temp.right);
            }
        }
    }
}
步骤 3: 主函数来驱动程序

最后,创建一个主函数来驱动程序,从用户那里接收输入并插入到树中。

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        BinaryTree tree = new BinaryTree();

        System.out.println("Enter the number of nodes:");
        int count = scanner.nextInt();
        System.out.println("Enter " + count + " integers:");

        for (int i = 0; i < count; i++) {
            int data = scanner.nextInt();
            tree.insert(data);
        }

        System.out.println("Level order traversal of the binary tree:");
        tree.printLevelOrder();

        scanner.close();
    }
}

这个程序首先定义了一个基本的二叉树节点类TreeNode,然后定义了一个BinaryTree类来管理树的插入和层次遍历。在主函数中,程序从用户接收一个整数序列并将它们逐个插入到树中。插入操作不考虑节点数据的大小,而是简单地在第一个找到的可用位置插入新节点。通过层次遍历方法,可以检查树的结构是否正确。

2.遍历二叉树:

示例二叉树:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

前序遍历:先根,再左,再右

从根节点开始,在找不到左字节点后再找右子节点:

前序结束结果为:A B D F E C G I J H K

中序遍历:先左,再根,再右

把根节点放中间,左右子树放俩边

结果为:F D B E A I G J C H K

后序遍历:先左,再右,再根

从最后一行,左子树最左节点开始,先遍历左子节点然后右子节点然后根节点,根节点放最后

结果为:F D E B I J G K H C A

代码示例:
1. 前序遍历(Pre-order)

前序遍历首先访问根节点,然后递归地执行前序遍历左子树,接着递归地执行前序遍历右子树。

public void preorder() {
    preorderRec(root);
}

private void preorderRec(TreeNode node) {
    if (node != null) {
        System.out.print(node.data + " ");
        preorderRec(node.left);
        preorderRec(node.right);
    }
}
2. 中序遍历(In-order)

中序遍历首先递归地访问左子树,然后访问根节点,最后递归地访问右子树。对于二叉搜索树,这种遍历会按照升序访问所有节点。

public void inorder() {
    inorderRec(root);
}

private void inorderRec(TreeNode node) {
    if (node != null) {
        inorderRec(node.left);
        System.out.print(node.data + " ");
        inorderRec(node.right);
    }
}
3. 后序遍历(Post-order)

后序遍历首先递归地访问左子树,然后递归地访问右子树,最后访问根节点。

public void postorder() {
    postorderRec(root);
}

private void postorderRec(TreeNode node) {
    if (node != null) {
        postorderRec(node.left);
        postorderRec(node.right);
        System.out.print(node.data + " ");
    }
}
4. 层次遍历(Level-order)

层次遍历从根节点开始,一层层向下遍历,每层从左至右访问所有节点。这通常需要使用队列来实现。

public void printLevelOrder() {
    if (root == null) return;

    Queue<TreeNode> queue = new LinkedList<>();
    queue.offer(root);
    while (!queue.isEmpty()) {
        TreeNode temp = queue.poll();
        System.out.print(temp.data + " ");
        if (temp.left != null) {
            queue.offer(temp.left);
        }
        if (temp.right != null) {
            queue.offer(temp.right);
        }
    }
}

我将使用一个简单的图表示方法,并展示如何在该图上实现这两种搜索算法。

3.图的表示

首先,定义图的数据结构。我们将使用邻接表来表示图,这是图表示的一种高效方式:

import java.util.*;
class Graph {
    private int numVertices; // 图中的顶点数
    private LinkedList<Integer>[] adjLists; // 邻接表

    // 构造函数
    public Graph(int vertices) {
        numVertices = vertices;
        adjLists = new LinkedList[vertices];
        for (int i = 0; i < vertices; i++) {
            adjLists[i] = new LinkedList<>();
        }
    }

    // 添加边
    void addEdge(int src, int dest) {
        adjLists[src].add(dest);
    }

    // 打印图
    void printGraph() {
        for (int i = 0; i < numVertices; i++) {
            System.out.println("Adjacency list of vertex " + i);
            System.out.print("head");
            for(Integer node : adjLists[i]){
                System.out.print(" -> " + node);
            }
            System.out.println("\n");
        }
    }
}

4.深度优先搜索(DFS)

深度优先搜索(DFS)是一种图遍历算法,它的目标是尽可能深地探索图的分支。DFS使用递归或栈数据结构来实现,其基本过程如下:

  1. 访问起始节点:从图中的某个起始节点开始。
  2. 标记节点:将起始节点标记为已访问。
  3. 递归探索:从当前节点出发,递归地访问每个未被访问的邻接节点,直到所有可达的节点都被访问。
  4. 回溯:一旦到达一个没有未访问邻接节点的节点,算法将回溯到上一个节点,继续探索未被访问的邻接节点。

优点

  • 简单且容易实现。
  • 可以用来发现图中所有连接的分量。
  • 在找到所有从起点到特定点的路径方面非常有效。

缺点

  • 不保证找到最短路径。
  • 递归实现可能导致大量的递归调用,对于非常大的图可能导致栈溢出。

DFS 可以递归实现或使用栈。这里提供递归的实现方法:

void DFS(int vertex, boolean[] visited) {
    visited[vertex] = true;
    System.out.print(vertex + " ");

    for (int adjVertex : adjLists[vertex]) {
        if (!visited[adjVertex]) {
            DFS(adjVertex, visited);
        }
    }
}

void DFS(int startVertex) {
    boolean[] visited = new boolean[numVertices];
    DFS(startVertex, visited);
}

5.广度优先搜索(BFS)

广度优先搜索(BFS)从图的根节点开始,一层层地向外扩展,即它先访问离根节点最近的节点,然后是次近的节点,以此类推。BFS通常使用队列来实现,其步骤如下:

  1. 初始化队列:将起始节点加入队列,并标记为已访问。
  2. 队列操作:只要队列不为空,就从队列中移除一个节点,并访问其所有未被访问的邻接节点。
  3. 标记并入队:将访问的邻接节点标记为已访问,并加入队列。
  4. 重复执行:重复上述过程,直到队列为空。

优点

  • 可以保证在无权图中找到从起始点到任何可达点的最短路径。
  • 层次性的搜索使得BFS特别适用于层次化或最短路径问题。

缺点

  • 在所有层的节点都必须保持在内存中的情况下,可能会消耗较多内存。
  • 对于路径的搜索不如DFS那样自然和直接。

BFS 使用队列实现,并逐层遍历图的节点:

void BFS(int startVertex) {
    boolean[] visited = new boolean[numVertices];
    Queue<Integer> queue = new LinkedList<>();

    visited[startVertex] = true;
    queue.add(startVertex);

    while (!queue.isEmpty()) {
        int vertex = queue.poll();
        System.out.print(vertex + " ");

        for (int adjVertex : adjLists[vertex]) {
            if (!visited[adjVertex]) {
                visited[adjVertex] = true;
                queue.add(adjVertex);
            }
        }
    }
}
应用对比

深度优先搜索(DFS)和广度优先搜索(BFS)是两种基本的图遍历技术,它们在解决许多计算机科学问题中起到核心作用,如网络爬虫、路径查找、社交网络分析等。下面是对这两种搜索策略的详细解释和对比。

  • 路径查找:BFS用于在未加权图中查找最短路径;DFS用于探索所有可能的路径。
  • 连通性:DFS可以用来分析图的连通性,比如检测图中是否存在环。
  • 层次遍历:BFS经常用于需要按层次顺序遍历节点的场合,如在社交网络的好友推荐。
  • 26
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值