我想要一些有关我编写的有关在链表中插入元素的方法的反馈。给出的链表类定义为:
public class List {
private Node head;
private static class Node {
public String data;
public Node next;
public Node(String data, Node next) {
//Assign data and next here
}
//Optional Node helper methods are written here
}
//List methods and data go here
基于此定义,我正在尝试创建一个insert方法。我不确定给出的定义是否包含size()
方法,但我认为它没有,并尝试找到一种方法来在我的insert方法中查找大小(基本上是列表中有多少个节点) 。该方法具有签名public void insert(String s, int psn)
,当给出非法索引(psn)值(超出范围)时,此方法还应该引发IllegalArgumentException。我的猜测是psn
的值范围是从0到第一个null元素出现的时间(如果我对此不正确,请更正我。这是我编写的方法:
public void insert(String s, int psn) {
Node temp = head; //Save original head
int c = 0;
while (temp != null) { //Traverse linked list
c++;
temp = temp.next;
}
if (psn < 0 || psn >= c) { //Should throw exception when illegal value is used
throw new IllegalArgumentException();
}
if (psn == 0) { //Special case when inserting an element at the front of the list
head = new Node(s, head);
} else if (head != null) {
Node current = head; //Save original head while traversing list
while (current != null) {
current = current.next;
psn--;
}
if (current != null) { //We are at the position where we want to insert the element
current.next = new Node(s, current.next);
}
}
}
有人可以告诉我在查找链表的长度时是否正确使用了第一个while循环,以及是否正确使用了例外情况?这是我最关心的部分。提前非常感谢您!
答案 0 :(得分:1)
您始终可以通过在某些测试用例上运行代码来测试代码。但是,这是我对这段代码的评论:
ViewStart
将始终返回false,因为在此之前的while循环将使if (current != null)
在结尾处为空。current
是否在第一个while循环之前,以减少计算的浪费。psn < 0
的字段,该字段将处理列表的大小。首先将其设置为0,然后在每个size
和每个insert
之后,您将更新此字段。然后代码看起来像这样:
remove
答案 1 :(得分:0)
是的,这是在不存储大小的情况下检索LinkedList中元素数量的一种正确方法。
这是链表的一个缺点,即遍历O(n),但如果我们存储“ tail”元素,则插入1次。
也请参阅Matan Kintzlinger的逻辑建议
答案 2 :(得分:0)
按现状,您的insert
遍历整个列表两次,一个遍历整个列表,另一个遍历整个列表。最基本的实现没有任何数据可以帮助您喜欢每次写入都会更新一个size字段,因此只需遍历一次即可,因为如果您早于预期到达sentinel,您就会知道所需位置无效。这就是我的实现方式:
public void insert(String item, int position) {
if(position == 0) {
head = new Node(item, head);
return;
}
List.Node previousNode = head;
for(int i = 1; i < position && previousNode != null; ++i) {
previousNode = previousNode.next;
}
if(position < 0 || previousNode == null) {
throw new IllegalArgumentException("index " + position + " is out of bounds");
}
previousNode.next = new Node(item, previousNode.next);
}
其他一些注意事项是:
请尝试使用描述性变量名,而不要使用psn
之类的缩写,因为如果没有完整的上下文,它们可能很难阅读。在大多数现实情况下,优化人类可读性更为重要,因为调试和维护代码的人类越容易理解它,出现错误的可能性就越小。很多时候,变量名实际上是您首先了解的情况,并且是真正的明智之选!上一次,我试图使用外部npm库来破坏代码的原因是为什么?实际上是几天前。
尝试使用guard clauses和早期返回值以避免arrow code。 Multiple return points are not necessarily bad,如果您避免在方法中使用excessive cyclomatic complexity,发现它们并不是问题。例如,在这种情况下,在零位置插入的特殊情况是一次提前返回的机会,因为不需要检查,并且在完成后也无需执行任何操作。
使您的异常尽可能有帮助。这意味着,当您因为捕获到另一个异常而引发异常时,将您捕获的那个异常按顺序包括在not to break the stack trace中;在这种情况下,不存在更早的异常,一条有用的消息可以帮助开发人员弄清楚发生了什么。在这种情况下,IllegalArgumentException
并没有告诉我们什么原因导致抛出此异常,因此没有消息。几乎每个异常构造函数都允许消息和Throwable
原因。