嗨,我正在尝试使用链接列表进行练习.
我定义了一个名为的Object类Student
:
public class Student { protected string Student_Name; protected int Student_ID; protected int Student_Mark; protected char Student_Grade; public Student() // default Constructor { Student_Name = " "; Student_ID = 0; Student_Mark = 0; Student_Grade = ' '; } public Student(string Sname, int Sid, int Smark, char Sgrade) // Constructor { int len = sname.Length; Student_Name = sname.Substring(0, len); //Student_Name = sname.Substring(0, sname.Length); Student_ID = Sid; Student_Mark = Smark; Student_Grade = Sgrade; } }
然后是一个Node
班级:
public class S_Node { public Student Element; public S_Node Link; public S_Node() { Element = new Student(); Link = null; } public Node(Student theElement) { Element = theElement; Link = null; } }
和LinkedList
:
public class S_LinkedList { protected S_Node header; protected S_Node tail; public S_LinkedList() { header = new S_Node(); Tail = new S_Node(); header.Link = Tail; } // METHODS which i don't know how to do it (never use linkedlist before) }
我需要使用"链表数据结构类型"来组织这些数据.
包含链接列表的所有方法作为我已经学习的向列表中添加节点 - >(插入),从列表中删除节点,如我所知 - >((删除),遍历我学到的列表 - - >((PrintList),在我学习的时候在列表中找到一个节点 - >((Find,FindPrevious)我自学的问题我试图搜索网络并从愚蠢的C#中读取更多信息是一场灾难.我做得太多了,我很难过,我不知道如何完成它.
我正在努力使用这个类来编写可执行程序并进行测试.
如果你不想帮助完成这个程序(希望不是)至少告诉我一些真实的例子或想法,毕竟我是一个自学者的极客:-)
the head of the list. ( item1 Element: student1 Next ------------> ( item2 ) Element: student2 Next ------------> ( item3 ) Element: student3 Next: null ) the tail of the list.
首先,为了能够编写StudentList类,您需要首先编写客户端代码.客户端代码是使用学生列表的代码.另外,不要一次只写一件东西扔掉.而是编写一大堆[测试]案例,这些案例练习了与StudentList交互所需的不同方式.写出特殊情况.但是,不要试图写一个瑞士军刀,只是因为它可以做一切.写下完成工作的最少量代码.
如何使用该类将严重决定类的构造方式.这是TDD或测试驱动设计的本质.
我能看到的最大问题是你不知道如何使用这门课程.所以我们先做.
// create a list of students and print them back out. StudentList list = new StudentList(); list.Add( new Student("Bob", 1234, 2, 'A') ); list.Add( new Student("Mary", 2345, 4, 'C') ); foreach( Student student in list) { Console.WriteLine(student.Name); }
我将学生添加到列表中,然后将其打印出来.
我不需要我的客户端代码来查看StudentList内部.因此,StudentList隐藏了它如何实现链表.让我们来写一下StudentList的基础知识.
public class StudentList { private ListNode _firstElement; // always need to keep track of the head. private class ListNode { public Student Element { get; set; } public ListNode Next { get; set; } } public void Add(Student student) { /* TODO */ } }
StudentList非常基础.在内部,它跟踪第一个或头部节点.显然始终需要跟踪第一个节点.
您也可能想知道为什么在StudentList中声明ListNode.会发生什么是ListNode类只能由StudentList类访问.这样做是因为StudentList不想向其内部实现提供详细信息,因为它控制对列表的所有访问.StudentList从不显示列表的实现方式.实现隐藏是一个重要的OO概念.
如果我们确实允许客户端代码直接操作列表,那么将StudentList放在第一位就没有意义了.
让我们继续并实现Add()操作.
public void Add(Student student) { if (student == null) throw new ArgumentNullException("student"); // create the new element ListNode insert = new ListNode() { Element = student }; if( _firstElement == null ) { _firstElement = insert; return; } ListNode current = _firstElement; while (current.Next != null) { current = current.Next; } current.Next = insert; }
Add操作必须找到列表中的最后一项,然后将新的ListNode放在最后.虽然效率不是很高.它现在是O(N),随着列表变长,添加会变慢.
让我们对插入进行一点优化,然后重写Add方法.为了使Add更快,我们需要做的就是让StudentList跟踪列表中的最后一个元素.
private ListNode _lastElement; // keep track of the last element: Adding is O(1) instead of O(n) public void Add(Student student) { if( student == null ) throw new ArgumentNullException("student"); // create the new element ListNode insert = new ListNode() { Element = student }; if (_firstElement == null) { _firstElement = insert; _lastElement = insert; return; } // fix up Next reference ListNode last = _lastElement; last.Next = insert; _lastElement = insert; }
现在,当我们添加时,我们不会迭代.我们只需要跟踪头部和尾部参考.
接下来:foreach循环.StudentList是一个集合,是我们想要枚举它并使用C#的集合foreach
.C#编译器不能神奇地迭代.为了使用foreach循环我们需要为编译器提供一个枚举器来使用,即使我们编写的代码没有显式地使用枚举器.
首先,让我们重新访问我们如何迭代链表.
// don't add this to StudentList void IterateOverList( ListNode current ) { while (current != null) { current = current.Next; } }
好的.所以让我们挂钩C#的foreach循环并返回一个枚举器.为此,我们改变StudentList以实现IEnumerable.这有点先进,但你应该能够弄清楚发生了什么.
// StudentList now implements IEnumerablepublic class StudentList : IEnumerable { // previous code omitted #region IEnumerable Members public IEnumerator GetEnumerator() { ListNode current = _firstElement; while (current != null) { yield return current.Element; current = current.Next; } } #endregion #region IEnumerable Members IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } #endregion }
您应该能够在那里发现链表迭代.不要被yield
关键字抛出.所有产量正在做的是将当前学生返回到foreach循环.当它到达链表的末尾时,enumarator停止返回学生.
就是这样!代码以我们想要的方式工作.
*这绝不是实施清单的唯一方法.我选择将列表逻辑放在StudentList中并保持ListNode非常基本.但是代码只是我第一次单元测试所需要的,仅此而已.您可以进行更多优化,还有其他构建列表的方法.
展望未来:您需要做的是首先为代码需要做的事情创建[unit]测试,然后添加您需要的实现.
*fyi我也改写了学生班.来自C#persepctive的错误命名和奇怪的外壳,更不用说你提供的代码无法编译.我更喜欢_
作为私有成员变量的领导者.有些人不喜欢这样,但是你是新手,所以我会留下他们,因为他们很容易被发现.
public class Student { private string _name; private int _id; private int _mark; private char _letterGrade; private Student() // hide default Constructor { } public Student(string name, int id, int mark, char letterGrade) // Constructor { if( string.IsNullOrEmpty(name) ) throw new ArgumentNullException("name"); if( id <= 0 ) throw new ArgumentOutOfRangeException("id"); _name = name; _id = id; _mark = mark; _letterGrade = letterGrade; } // read-only properties - compressed to 1 line for SO answer. public string Name { get { return _name; } } public int Id { get { return _id; } } public int Mark { get { return _mark; } } public char LetterGrade { get { return _letterGrade; } } }
检查参数
注意属性,类和变量的不同外壳.
隐藏默认构造函数.为什么我要创建没有真实数据的学生?
提供一些只读属性.
这个类是不可改变的(即一旦你创建了一个学生,你就无法改变它).