使用List<T>模拟队列的实现

本文通过C#代码实现了一个简单的队列操作演示,包括队列的入队和出队过程,使用List泛型集合存储车辆类实例,并将其转换为队列进行操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

直接上代码,有注释,分为两个类:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace List模拟队列
{
    //车辆类
    class Arr
    {
        //车辆名称
        private string car;
        public string CarName
        {
            get { return car; }
            set { car = value; }
        }
    }
}

程序入口:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace List模拟队列
{
    class Program
    {
        //创建List泛型集合
        public static List<Arr> ar = new List<Arr>();
        static void Main(string[] args)
        { 
            //初始化三辆车
            Arr arr1 = new Arr();
            arr1.CarName = "奔驰";
            Arr arr2 = new Arr();
            arr2.CarName = "法拉利";
            Arr arr3 = new Arr();
            arr3.CarName = "劳斯莱斯";
            //向List中添加数据
            ar.Add(arr1);
            ar.Add(arr2);
            ar.Add(arr3);

            Console.WriteLine("开始入队列");
            //创建队列
            Queue<Arr> que = new Queue<Arr>();
            //将List集合中的数据,循环添加到队列中
            foreach (Arr item in ar)
            {
                //将对象添加到结尾处
                que.Enqueue(item);
                //打印输出车辆名称
                Console.WriteLine("入队列-{0}",item.CarName);
            }
            Console.WriteLine("---------------");
            
            //调用打印队列方法
            Print(que);

            Console.WriteLine("---------------");
            Console.WriteLine("开始出队列");
            //循环移除并返回开始处的对象
            while (que.Count > 0)
            {
                //使用对象接收
                Arr ao =  que.Dequeue();
                //打印输出出队列的车辆名称
                Console.WriteLine("出队列-{0}",ao.CarName);
            }
        }

        //打印队列方法
        private static void Print(Queue<Arr> eue)
        {
            Console.WriteLine("开始打印队列");
            //循环遍历队列中的数据
            foreach (Arr item in eue)
            {
                Console.WriteLine(item.CarName);
            }
        }
    }
}

<think>我们面对的问题:在树形结构中,根据父节点递归查找所有子节点,并且每个链路(节点之间的边)有时间区间属性。我们需要根据时间区间来区分不同的链路。数据结构设计:-节点类(Node)包含节点唯一标识(id)、父节点引用(可选,但这里我们可能需要从父节点找子节点,所以通常节点会有子节点列表)、时间区间属性(注意:时间区间是边上的属性,所以实际上应该放在边的信息中)-但通常,在树结构中,我们更常见的是节点包含子节点列表,而边信息隐含在父子关系中。如果边上有附加属性(如时间区间),那么我们需要将边也抽象出来。因此,我们有两种设计方式:1.将时间区间作为节点的一个属性?这不太合适,因为同一个节点可能通过不同的边(时间区间不同)与父节点相连,但节点本身是同一个。2.将边抽象为一个类(Edge),每个边包含子节点、时间区间。这样,父节点就可以有多个边(每个边指向一个子节点,并带有时间区间)。考虑到需求是“根据时间区间区分链路”,我们采用第二种方式:在父节点中存储一个边的列表,每条边包含子节点和时间区间。数据结构定义如下:```java//时间区间类class TimeRange{private DatestartTime;privateDate endTime;publicTimeRange(Date startTime, DateendTime) {this.startTime= startTime;this.endTime= endTime;}//检查一个时间点是否在区间内publicboolean contains(Date time) {return!time.before(startTime)&& !time.after(endTime);}//检查两个时间区间是否有重叠(可选,根据需求)public booleanoverlaps(TimeRangeother){return this.startTime.before(other.endTime)&& other.startTime.before(this.endTime);}//gettersand setters...} ``````java//树边类(包含子节点和时间区间)class TreeEdge {privateNodechildNode;private TimeRange timeRange;publicTreeEdge(Node childNode,TimeRangetimeRange) {this.childNode= childNode;this.timeRange= timeRange;}//gettersand setters...}``` ```java//节点类classNode {privateString id; //节点唯一标识privateList<TreeEdge> edges; //指向子节点的边(包含时间区间)publicNode(Stringid){this.id= id;this.edges =new ArrayList<>();}publicvoid addEdge(Nodechild,TimeRangetimeRange) {this.edges.add(newTreeEdge(child,timeRange));}publicList<TreeEdge> getEdges() {returnedges;}//根据时间区间获取符合条件的子节点边(注意:这里返回的是边,因为需要时间区间信息)public List<TreeEdge>getEdgesInTimeRange(TimeRangequeryRange) {List<TreeEdge>result =new ArrayList<>();for (TreeEdgeedge :edges){//如果边的时间区间与查询的时间区间有重叠(或者完全包含,根据需求调整)if(edge.getTimeRange().overlaps(queryRange)) {result.add(edge);}}returnresult;}} ```递归查找所有子节点的算法:输入:一个父节点,一个时间区间(表示我们要找在这个时间区间内有效的链路)输出:所有满足条件的子节点(包括子孙节点)的列表,注意:这里我们可能需要返回节点列表,但有时也需要边的信息?根据需求,我们假设需要返回节点列表。但是,注意:同一个节点可能通过不同的边(不同时间区间)被多次包含?我们的递归算法需要考虑到这一点:在给定的查询时间区间内,每个节点可能通过不同的路径被访问,但通常我们只关心节点本身,或者关心节点以及到达该节点的路径(边)的时间区间。根据问题描述,我们可能需要返回所有在给定时间区间内与父节点有链路连接的子节点(包括间接连接的子孙节点)。并且,链路的时间区间必须与查询的时间区间有重叠(或者包含,具体看需求)。算法步骤:1.从父节点开始,查找在给定时间区间内的所有直接子节点(通过边的时间区间判断)。2.对于每个直接子节点,递归地查找该子节点在相同时间区间内的所有子节点(子孙节点)。3.将直接子节点和递归得到的子孙节点合并,作为结果返回。注意:为了避免循环引用(树结构通常不会,但如果是图则需要考虑),我们假设树结构没有环。代码示例: ```javaimport java.util.ArrayList; importjava.util.Date;import java.util.List; publicclass TreeQuery{//递归获取某个节点在指定时间区间内的所有子节点(包括直接和间接)publicstatic List<Node>getAllChildren(Nodeparent, TimeRange queryRange){List<Node> children= newArrayList<>();//获取当前节点在查询时间区间内的直接子节点边List<TreeEdge>edgesInRange =parent.getEdgesInTimeRange(queryRange);for(TreeEdge edge: edgesInRange) {Nodechild =edge.getChildNode();children.add(child);//添加直接子节点//递归获取子节点的子节点children.addAll(getAllChildren(child, queryRange));}returnchildren;}} ```但是,上述方法返回的节点列表可能包含重复的节点吗?在树结构中,一个节点只能有一个父节点(根节点除外),所以不会重复。但是,如果同一个节点通过两条不同的路径(在树中不可能,树中节点只能有一个父节点)?所以不会重复。然而,如果需求是返回所有节点,包括通过不同路径访问的同一个节点(但树中不可能有多个路径),所以没问题。但是,注意:我们的边允许同一个节点被同一个父节点通过不同的时间区间多次添加吗?在数据结构中,我们允许同一个子节点被添加到父节点的边列表中多次(即同一个子节点,不同的时间区间)。那么,在递归查找时,同一个子节点可能会被多次加入结果?不会,因为即使同一个子节点通过不同的边被包含,我们每次递归处理的是同一个节点对象,然后我们只是把这个节点对象加入列表,而列表是允许重复对象的?但是,我们并不希望同一个节点在结果列表中出现多次。所以,我们需要避免重复添加同一个节点吗?在树结构中,一个节点只能被访问一次(因为每个节点只有一个父节点),所以不会出现同一个节点通过不同路径被访问的情况。但是,在同一个父节点下,同一个子节点被添加了两次(不同的时间区间边),那么我们在一次查询中,如果两条边都符合时间区间,那么同一个子节点会被添加两次。根据需求,我们是否希望同一个节点在结果中出现多次?如果同一个节点在同一个父节点下通过两条不同的边(时间区间)与父节点相连,那么在查询时间区间同时覆盖这两个时间区间时,这个节点应该被算作两次吗?这取决于业务需求。假设需求:我们只关心节点本身,不关心不同的边,那么同一个节点即使通过多条边与父节点相连,我们也只应返回一次。此时,我们需要去重。修改:我们可以使用一个Set来存储节点(需要重写Node的equals和hashCode,基于id)。或者,如果需求要求返回多次,则保留列表。这里,我们假设需求是返回节点列表(不重复,即同一个节点只出现一次),那么我们可以修改为: ```javapublicstatic Set<Node>getAllChildrenUnique(Nodeparent, TimeRange queryRange){Set<Node> childrenSet= newHashSet<>();collectChildren(parent, queryRange,childrenSet);return childrenSet;} privatestatic voidcollectChildren(Node node, TimeRange queryRange,Set<Node> childrenSet){List<TreeEdge> edgesInRange= node.getEdgesInTimeRange(queryRange);for (TreeEdgeedge :edgesInRange){Node child= edge.getChildNode();//如果这个子节点还没有被访问过,则加入集合并递归if (childrenSet.add(child)) {//如果成功添加(即之前没有)collectChildren(child, queryRange,childrenSet);}} }```但是,注意:这样我们避免了重复节点,但是也忽略了同一个节点通过不同路径被访问的情况(在树中不可能有不同路径,所以没关系)。另外,这里我们使用了Set,所以返回的节点是无序的。如果希望有序,可以使用LinkedHashSet。然而,如果需求要求我们同时返回边的时间区间信息,那么上述方法只返回节点就不够了。所以,我们需要根据实际需求调整。如果需求是返回所有链路(包括路径),那么我们就需要记录路径。但原问题只要求“根据父节点查找所有子节点”,所以返回节点列表即可。考虑到时间区间在边上的特性,我们可能需要在结果中保留边的信息?但问题没有明确要求。这里我们按照只返回节点列表(不重复)来设计。另外,我们还需要注意时间区间的重叠判断逻辑。在`TimeRange`类中,我们定义了一个`overlaps`方法,它判断两个时间区间是否有重叠。如果需求是“链路的时间区间完全包含在查询时间区间内”,那么我们就需要修改判断条件。修改`TimeRange`类中的方法以满足不同的需求:-重叠(overlaps):当前时间区间与查询时间区间有交集就算。-包含(contains):当前时间区间完全包含在查询时间区间内。根据需求选择。这里我们假设使用重叠。总结:-我们设计了节点、边、时间区间的数据结构。-提供了递归查找子节点的方法,并支持按时间区间过滤。-使用Set避免重复节点。完整示例代码:注意:这里我们假设节点id唯一,并以此作为equals和hashCode的依据。 ```javaimport java.util.*;import java.util.Date; classTimeRange{private DatestartTime;private DateendTime;public TimeRange(DatestartTime, DateendTime) {this.startTime= startTime;this.endTime= endTime;}//检查两个时间区间是否有重叠publicboolean overlaps(TimeRange other) {returnthis.startTime.before(other.endTime) &&other.startTime.before(this.endTime);}// GetterspublicDate getStartTime() {return startTime;}public DategetEndTime(){ returnendTime; }} classTreeEdge{private NodechildNode;private TimeRange timeRange;publicTreeEdge(Node childNode,TimeRangetimeRange) {this.childNode= childNode;this.timeRange= timeRange;}//Getterspublic NodegetChildNode(){ returnchildNode; }publicTimeRangegetTimeRange(){ returntimeRange; }} classNode {privateString id;private List<TreeEdge>edges;publicNode(Stringid){this.id= id;this.edges =new ArrayList<>();}public voidaddEdge(Node child, TimeRange timeRange){this.edges.add(new TreeEdge(child, timeRange));}publicList<TreeEdge> getEdgesInTimeRange(TimeRangequeryRange) {List<TreeEdge>result =new ArrayList<>();for (TreeEdgeedge :edges){if (edge.getTimeRange().overlaps(queryRange)) {result.add(edge);}}returnresult;}//重写equals和hashCode,基于id@Overridepublic booleanequals(Objecto){if (this ==o)return true;if (o ==null ||getClass() !=o.getClass()) returnfalse;Nodenode =(Node) o;return Objects.equals(id, node.id);}@Overridepublicint hashCode() {returnObjects.hash(id);}} publicclass TreeQueryExample{publicstatic Set<Node>getAllChildren(Node parent, TimeRange queryRange){Set<Node> childrenSet =new HashSet<>();collectChildren(parent,queryRange, childrenSet);returnchildrenSet;}private staticvoid collectChildren(Nodenode,TimeRangequeryRange, Set<Node>childrenSet) {for(TreeEdge edge: node.getEdgesInTimeRange(queryRange)) {Nodechild =edge.getChildNode();if (childrenSet.add(child)) {collectChildren(child, queryRange,childrenSet);}}}publicstatic voidmain(String[] args) {//示例:构建树Noderoot =new Node("root");//创建两个时间区间TimeRange tr1 =new TimeRange(newDate(1000000), newDate(2000000));TimeRange tr2 =new TimeRange(newDate(1500000),new Date(2500000));TimeRange tr3 =new TimeRange(newDate(3000000),new Date(4000000));Node child1 =new Node("child1");Nodechild2= newNode("child2");Node grandchild1= newNode("grandchild1");root.addEdge(child1, tr1);root.addEdge(child2, tr2);child1.addEdge(grandchild1,tr3);//查询时间区间:与tr1和tr2都有重叠,但与tr3不重叠TimeRangequery =new TimeRange(newDate(1500000),new Date(1600000));Set<Node> children= getAllChildren(root, query);//应该包含child1和child2,但不包含grandchild1(因为tr3与query不重叠)System.out.println(children);//输出 [child1, child2](实际输出是节点的toString,需要重写Node的toString)}} ```注意:上面的示例中,我们重写了Node的equals和hashCode,但没有重写toString。为了输出方便,可以在Node中重写toString方法返回id。在Node类中添加:```java@OverridepublicString toString() {returnid;} ```这样,输出就是节点的id。如果需求发生变化,比如需要返回路径信息,那么我们就需要修改算法,记录到达每个节点的路径(边列表)。这里不展开。最后,关于递归的注意事项:递归深度过大可能导致栈溢出。对于非常深的树,可以考虑使用栈来模拟递归(迭代深度优先搜索)或使用广度优先搜索。但通常,树结构的深度不会太大(如DNS树、部门组织结构树等),所以递归是可以接受的。如果树很深,可以改为迭代方式:```javapublicstatic Set<Node>getAllChildrenIterative(Node parent, TimeRange queryRange){Set<Node> visited= newHashSet<>();Stack<Node>stack =new Stack<>();//首先将父节点的满足条件的直接子节点入栈(注意:父节点本身不加入结果,我们找的是子节点)for(TreeEdge edge: parent.getEdgesInTimeRange(queryRange)) {Nodechild =edge.getChildNode();if (visited.add(child)){stack.push(child);}}while(!stack.isEmpty()){Node node= stack.pop();visited.add(node); //其实上面已经加过了,这里主要是为了下面继续添加它的子节点for (TreeEdgeedge :node.getEdgesInTimeRange(queryRange)){Node child= edge.getChildNode();if(!visited.contains(child)) {stack.push(child);visited.add(child);//标记已访问,避免重复}}}//注意:我们一开始并没有把父节点放入visited,所以visited中都是子节点return visited; }```但是,注意:我们这里使用深度优先搜索(栈),也可以使用队列实现广度优先搜索。根据需求选择。以上,我们提供了递归和迭代两种方式,并考虑了时间区间的过滤和节点去重。希望这能满足您的需求。</think>### 递归查找树形结构子节点的 Java 实现(带时间区间) 以下是一个完整的 Java 实现,用于在树形结构中根据父节点递归查找所有子节点,并支持按时间区间过滤链路。数据结构采用邻接表表示法,每个节点存储其子节点及对应的时间区间。 #### 数据结构设计 ```java import java.util.*; // 时间区间类 class TimeRange { Date start; Date end; public TimeRange(Date start, Date end) { this.start = start; this.end = end; } // 检查时间区间是否重叠 public boolean overlaps(TimeRange other) { return !end.before(other.start) && !start.after(other.end); } } // 树节点类(包含时间区间) class TreeNode { String id; List<ChildLink> children = new ArrayList<>(); // 子节点链表 public TreeNode(String id) { this.id = id; } // 添加带时间区间的子节点 public void addChild(TreeNode child, TimeRange timeRange) { children.add(new ChildLink(child, timeRange)); } } // 子节点链路(包含时间区间) class ChildLink { TreeNode child; TimeRange timeRange; public ChildLink(TreeNode child, TimeRange timeRange) { this.child = child; this.timeRange = timeRange; } } ``` #### 递归查找算法 ```java class TreeQuery { // 递归查找所有符合条件的子节点 public static List<TreeNode> findChildren( TreeNode parent, TimeRange queryRange, boolean includeParent ) { List<TreeNode> result = new ArrayList<>(); if (includeParent) { result.add(parent); } for (ChildLink link : parent.children) { // 检查时间区间是否匹配 if (link.timeRange.overlaps(queryRange)) { // 递归获取子节点的子节点 result.addAll(findChildren(link.child, queryRange, true)); } } return result; } // 查找直接子节点(非递归) public static List<TreeNode> findDirectChildren( TreeNode parent, TimeRange queryRange ) { List<TreeNode> result = new ArrayList<>(); for (ChildLink link : parent.children) { if (link.timeRange.overlaps(queryRange)) { result.add(link.child); } } return result; } } ``` #### 使用示例 ```java public class Main { public static void main(String[] args) throws Exception { // 创建日期格式 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); // 构建树结构 TreeNode root = new TreeNode("A"); TreeNode nodeB = new TreeNode("B"); TreeNode nodeC = new TreeNode("C"); TreeNode nodeD = new TreeNode("D"); // 添加带时间区间的链路 root.addChild(nodeB, new TimeRange(sdf.parse("2023-01-01"), sdf.parse("2023-06-30"))); root.addChild(nodeC, new TimeRange(sdf.parse("2023-03-01"), sdf.parse("2023-09-30"))); nodeB.addChild(nodeD, new TimeRange(sdf.parse("2023-04-01"), sdf.parse("2023-05-31"))); // 定义查询时间区间 TimeRange queryRange = new TimeRange(sdf.parse("2023-05-01"), sdf.parse("2023-07-31")); // 执行递归查询 List<TreeNode> result = TreeQuery.findChildren(root, queryRange, false); // 输出结果 System.out.println("符合时间区间的节点:"); for (TreeNode node : result) { System.out.println(node.id); } // 输出: B, C, D (A被排除,因为includeParent=false) } } ``` #### 算法说明 1. **数据结构**: - `TreeNode` 表示树节点,包含子节点链表 - `ChildLink` 存储子节点及其时间区间 - `TimeRange` 处理时间区间逻辑 2. **递归过程**: - 从父节点开始深度优先遍历 - 检查每条链路的时间区间是否与查询区间重叠 - 符合条件的子节点继续递归搜索 - 时间复杂度:$O(n)$(n 为节点数) 3. **时间区间处理**: - 使用 `overlaps()` 方法检查区间重叠 - 支持开区间、闭区间等各种时间关系 - 可根据需要修改为完全包含等逻辑 #### 性能优化建议 1. **剪枝优化**:当时间区间完全不匹配时提前终止递归 2. **缓存机制**:为节点添加缓存字段存储有效时间范围 3. **迭代替代**:对于深度大的树可使用栈迭代避免栈溢出 4. **区间索引**:使用区间树(Interval Tree)优化查询效率 树形结构的时间区间处理在 DNS 系统[^3]、版本控制系统等领域有广泛应用,此实现可扩展支持更复杂的层级数据查询需求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

柳落青

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值