目录
LinkedList
的实现思路,本文将会对双向链表进行总结:下面是leetcode上面关于链表的题目及解题思路。
设计链表的实现。您可以选择使用单链表或双链表。单链表中的节点应该具有两个属性:val 和 next。val 是当前节点的值,next 是指向下一个节点的指针/引用。如果要使用双向链表,则还需要一个属性 prev 以指示链表中的上一个节点。假设链表中的所有节点都是 0-index 的。
在链表类中实现这些功能:
我的思路是:
class MyLinkedList {
int size = 0;
Node first;
Node last;
/* determine the scope of delete operation*/
public void elementRangeCheck(int index) {
if(index <0 || index >= size)
throw new IndexOutOfBoundsException();
}
/* determine the scope of add operation*/
public void positionRangeCheck(int index) {
if(index < 0 && index > size)
throw new IndexOutOfBoundsException();
}
/**
* Initialize your data structure here.
*/
public MyLinkedList() {
}
/**
* get node by specified index
*/
private Node getNode(int index) {
if (index > (size >> 1)) {
Node temp = last;
for (int i = size - 1; i > index; i--) {
temp = temp.prev;
}
return temp;
} else {
Node temp = first;
for (int i = 0; i < index; i++) {
temp = temp.next;
}
return temp;
}
}
/**
* Get the value of the index-th node in the linked list. If the index is invalid, return -1.
*/
public int get(int index) {
try{
elementRangeCheck(index);
}catch (IndexOutOfBoundsException e){
return -1;
}
return (Integer) getNode(index).item;
}
/**
* Add a node of value val before the first element of the linked list. After the insertion, the new node will be the first node of the linked list.
*/
public void addAtHead(int val) {
final Node f = first;
final Node newNode = new Node(null, val, f);
if (f == null)
last = newNode;
else
f.prev = newNode;
first = newNode;
size++;
}
/**
* Append a node of value val to the last element of the linked list.
*/
public void addAtTail(int val) {
final Node l = last;
final Node newNode = new Node(l, val, null);
if (l == null)
first = newNode;
else
l.next = newNode;
last = newNode;
size++;
}
/**
* Add a node of value val before the index-th node in the linked list. If index equals to the length of linked list, the node will be appended to the end of linked list. If index is greater than the length, the node will not be inserted.
*/
public void addAtIndex(int index, int val) {
positionRangeCheck(index);
if (index == size) {
addAtTail(val);
return;
}
final Node currNode = getNode(index);
final Node preNode = currNode.prev;
final Node newNode = new Node(preNode, val, currNode);
currNode.prev = newNode;
if (preNode==null))
first = newNode;
else
preNode.next = newNode;
size++;
}
/**
* Delete the index-th node in the linked list, if the index is valid.
*/
public void deleteAtIndex(int index) {
elementRangeCheck(index);
final Node succ = getNode(index);
final Node prev = succ.prev;
final Node next = succ.next;
succ.next = null;
succ.prev = null;
if (prev==null) {
first = next;
} else {
prev.next = next;
}
if (next==null)
last = prev;
else
next.prev = prev;
size--;
}
private static class Node {
int item;//store value;
Node next;//point to next node;
Node prev;//point to prev node;
public Node(Node prev, int item, Node next) {
this.item = item;
this.next = next;
this.prev = prev;
}
}
}
力扣官方题解提供了一种使代码简化的方式,忽视表头和表尾的边界条件,提供哨兵(sentinel)对象(哑对象),让删除的代码更加简洁。https://leetcode-cn.com/problems/design-linked-list/solution/she-ji-lian-biao-by-leetcode/
具体的操作就是:
nil
,此时nil.next
指向表头,nil.prev
指向表尾,表尾的next
和表头的prev
同时指向nil
。first
就可以用nil.next
来代替。虽然哨兵可以降低某些操作的渐进时间界,例如在插入和删除的时候会节约O(1)的时间。但是,应该尽量慎用,因为哨兵对象将会消耗额外的存储空间。
参考:《算法导论》
原文:https://www.cnblogs.com/summerday152/p/12203042.html