删除链表中所有元素的出现
为了理解数据结构,我开始在java中实现它们。deleteAll
的时间复杂度为O(n + n^2)
。我该如何改进deleteAll
方法?删除链表中所有元素的出现
/*
* Singly linked list
*/
package linkedlisttest;
class Node {
int data;
Node next;
public Node(int data) {
this.data = data;
}
}
class LinkedList {
Node head;
int size;
/**
*
* @param data element to add to list
* Time Complexity : O(n)
*/
public void add(int data) {
if (head == null) {
head = new Node(data);
size += 1;
return;
}
Node current = head;
while (current.next != null) {
current = current.next;
}
current.next = new Node(data);
size += 1;
}
/**
*
* @return size of list
* Time Complexity: O(1)
* This is because we use a class
* variable size to keep track of size of linked list
*/
public int getSize() {
return size;
}
/**
*
* @param data element to insert
* @param index position at which to insert the element (zero based)
* Time Complexity : O(n)
*/
public void add(int data, int index) {
if (index > getSize()) {
return; // invalid position
}
Node current = head; //iterate through whole list
int pos = 0;
Node newNode = new Node(data);
if (index == 0) // special case, since its a single reference change!
{
newNode.next = head;
head = newNode; // this node is now the head
size += 1;
return;
}
while (current.next != null) {
if (pos == index - 1) {
break;
}
pos++;
current = current.next;
}
// These are 2 reference changes, as compared to adding at index 0
newNode.next = current.next; // here we are changing a refernce
current.next = newNode; // changing a reference here as well
size += 1;
}
/**
* Find the first occurrence of an element
* @param data element to find
* @return index at which element is found , -1 if not exist
* Time Complexity: O(n)
*/
public int find(int data) {
Node current = head;
int pos = 0;
int index = -1;
if(head == null) { //empty list
return index;
}
while(current != null) {
if (current.data == data) {
index = pos;
break;
}
pos++;
current = current.next;
}
return index;
}
/**
* Delete the first occurrence of data
* @param data element to delete
* Time complexity : O(n)
*/
public void delete(int data) {
Node current = head;
if (head == null) { // list is empty
return;
}
if(head.data == data) { // if we want to delete the head , make next node head
head = head.next;
size -= 1;
return;
}
while(current.next != null) {
if (current.next.data == data) {
current.next = current.next.next;
size -= 1;
return;
}
current = current.next;
}
}
/**
* Delete all occurrences of data
* @param data element to delete
*
*/
public void deleteAll(int data) {
Node current = head;
if (head == null) { // list is empty
return;
}
//while loop to delete consecutive occurances of data
while(head.data == data) { // if we want to delete the head , make next node head
head = head.next;
size -= 1;
}
while(current.next != null) {
//while loop to delete consecutive occurances of data
while (current.next.data == data) {
current.next = current.next.next;
size -= 1;
}
current = current.next;
}
}
public void reverse() {
}
/**
* Prints the whole linked list
* Time Complexity : O(n)
*/
public void print() {
if(head == null) { //list is empty
return;
}
Node current = head;
while (current.next != null) {
System.out.print(current.data + "->");
current = current.next;
}
System.out.print(current.data + "\n");
}
}
public class LinkedListTest {
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
LinkedList lt = new LinkedList();
lt.print();
lt.add(3);
lt.add(5);
lt.add(6);
lt.print();
lt.add(4, 1);
lt.print();
lt.add(4, 7);// 7 is an invalid index
lt.add(8, 3);
lt.add(8, 4);
lt.print();
System.out.println("Position : " + lt.find(8));
lt.delete(5);
lt.print();
lt.deleteAll(8);
lt.print();
System.out.println("Size : " + lt.getSize());
}
}
如您所料,您实施的时间复杂度为O(n)
,而不是O(n + n^2)
。 尽管嵌套循环是O(n^2)
的常见标志,但并非总是如此。 重要的一点是迭代次数。 在你的情况下,如果你在嵌套循环中采用k
步骤,则 , 将外循环中的其余步骤减少k
。 总的来说, 你仍然会采取n
的步骤来达到目的。
但您的实现有一些错误, 也可以改进:
- 错误:您指定
current = head
,但如果head.data == data
那么它将被删除,并且current
仍将指向它 - 有不需要嵌套循环。头部用经过特殊处理,你可以简单地按照节点和删除相匹配的项目
像这样:
public void deleteAll(int data) {
while (head != null && head.data == data) {
head = head.next;
size -= 1;
}
if (head == null) {
return;
}
Node current = head;
while (current.next != null) {
if (current.next.data == data) {
current.next = current.next.next;
size -= 1;
} else {
current = current.next;
}
}
}
顺便说,头部的特殊处理可有点讨厌。 优雅的另一种方法是创建一个指向head
虚设:
public void deleteAll(int data) {
Node dummy = new Node();
dummy.next = head;
Node current = dummy;
while (current.next != null) {
if (current.next.data == data) {
current.next = current.next.next;
size -= 1;
} else {
current = current.next;
}
}
head = dummy.next;
}
感谢您的全面解释和代码示例。 – user2650277
'} else {current = current.next;}'是关键,我的早期实现是基于删除的,它总是迭代到下一个节点 – user2650277
请注意,Java中的所有内容都是引用,除了那些基本类型。所以current
仍然停留在原来的head
。
作为特殊情况不需要将连续事件对待。一个简单的迭代&通过与O(n)
链接列表来判断 - 线性复杂度将完全符合你的要求。
此外,嵌套循环并不一定意味着它肯定会花费O(n^2)。如果每次移动current
,它仍然以线性时间遍历链表。
另一种个人建议:如果您在处理链接列表时需要编写类似node.next.next
的东西,那么应该设置另一个引用或重构代码。
如果这个代码工作正常,那么这个问题是关上的堆栈溢出的话题,但可能是有利于我们的姊妹网站[代码审查(HTTPS ://codereview.stackexchange.com/)。 –