嗨,我正在尝试使用链接列表进行练习.
我定义了一个名为的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;
}
}
Run Code Online (Sandbox Code Playgroud)
然后是一个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;
}
}
Run Code Online (Sandbox Code Playgroud)
和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)
}
Run Code Online (Sandbox Code Playgroud)
我需要使用"链表数据结构类型"来组织这些数据.
包含链接列表的所有方法作为我已经学习的向列表中添加节点 - >(插入),从列表中删除节点,如我所知 - >((删除),遍历我学到的列表 - - >((PrintList),在我学习的时候在列表中找到一个节点 - >((Find,FindPrevious)我自学的问题我试图搜索网络并从愚蠢的C#中读取更多信息是一场灾难.我做得太多了,我很难过,我不知道如何完成它.
我正在努力使用这个类来编写可执行程序并进行测试.
如果你不想帮助完成这个程序(希望不是)至少告诉我一些真实的例子或想法,毕竟我是一个自学者的极客:-)
Rob*_*son 14
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);
}
Run Code Online (Sandbox Code Playgroud)
我将学生添加到列表中,然后将其打印出来.
我不需要我的客户端代码来查看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 */ }
}
Run Code Online (Sandbox Code Playgroud)
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;
}
Run Code Online (Sandbox Code Playgroud)
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;
}
Run Code Online (Sandbox Code Playgroud)
现在,当我们添加时,我们不会迭代.我们只需要跟踪头部和尾部参考.
接下来:foreach循环.StudentList是一个集合,是我们想要枚举它并使用C#的集合foreach
.C#编译器不能神奇地迭代.为了使用foreach循环我们需要为编译器提供一个枚举器来使用,即使我们编写的代码没有显式地使用枚举器.
首先,让我们重新访问我们如何迭代链表.
// don't add this to StudentList
void IterateOverList( ListNode current )
{
while (current != null)
{
current = current.Next;
}
}
Run Code Online (Sandbox Code Playgroud)
好的.所以让我们挂钩C#的foreach循环并返回一个枚举器.为此,我们改变StudentList以实现IEnumerable.这有点先进,但你应该能够弄清楚发生了什么.
// StudentList now implements IEnumerable<Student>
public class StudentList : IEnumerable<Student>
{
// previous code omitted
#region IEnumerable<Student> Members
public IEnumerator<Student> GetEnumerator()
{
ListNode current = _firstElement;
while (current != null)
{
yield return current.Element;
current = current.Next;
}
}
#endregion
#region IEnumerable Members
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
#endregion
}
Run Code Online (Sandbox Code Playgroud)
您应该能够在那里发现链表迭代.不要被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; } }
}
Run Code Online (Sandbox Code Playgroud)