链表总结
经典题目707
https://leetcode.cn/problems/design-linked-list/
单链表
Node
链表的节点Node包含节点的值val和指向下一个节点的指针next。注意在Node的class中要写Node的构造函数。
class Node{
Node next;
int val;
Node(int _val, Node _next){
val = _val;
next = _next;
}
Node(int _val){
val = _val;
}
Node(){
}
}
初始化
对于单链表,它包含头节点Node head和链表的容量size。
在初始化链表时,将构造head以及将size的值初始化为0。
int size;
Node head;
public MyLinkedList() {
head = new Node();
size = 0;
}
链表的常规操作有获取某一下标的节点,插入新节点(包含头插法,尾插法,在某一下标处插入),删除某一下标的节点。
获取某一下标的节点
获取某一下标的节点的函数为getNode,参数为int类型的下标index,返回类型为Node。
首先判断下标是否有效,如果无效,就直接返回null。
设置当前节点curr来遍历,循环条件是i作为下标,当i≤index时停止,此时的curr移动到了index下标的Node节点
返回curr。
Node getNode(int index)
{
if(index < 0 || index >= size) return null;
Node curr = head;
for (int i = 0; i <= index; i++)
{
curr = curr.next;
}
return curr;
}
头插法
头插法插入新节点的函数为addAtHead,参数是int类型的val,是新节点的val。
创建新节点n
将n的next指针指向头节点head的下一个节点
将头节点的next指针指向n
链表的size+1
public void addAtHead(int val) {
Node n = new Node(val);
n.next = head.next;
head.next = n;
size++;
}
尾插法
尾插法插入新节点的函数为addAtTail,参数为int类型的val,是新节点的val。
创建新节点n
使用getNode函数获取到尾节点,由于下标是从0开始,因此尾节点的index = size - 1
判断尾节点是否为空
如果尾节点为null,此时的链表是空链表,那么使用头插法插入n即可
否则
使尾节点的next指针指向n
n的next指针指向null
链表的size+1
public void addAtTail(int val) {
Node n = new Node(val);
Node tail = getNode(size - 1);
if(tail == null){
addAtHead(val);
return;
}
tail.next = n;
n.next = null;
size++;
}
在下标index的位置插入
在下标index的位置插入的函数为addAtIndex,参数为int类型的下标index和int类型的新节点的值val
首先判断下标index是否有效
然后和尾插法的思路一样,事实上尾插法是在index= size - 1的下标处插入新节点。而头插法是在index = 0处插入新节点。
public void addAtIndex(int index, int val) {
if(index > size) return ;
else if(index <= 0) addAtHead(val);
else if(index == size) addAtTail(val);
else{
Node n = new Node(val);
Node curr = getNode(index - 1);
n.next = curr.next;
curr.next = n;
size++;
}
}
删除某一下标的节点
删除某一下标的节点的函数为deleteAtIndex,参数为int类型的下标index
首先判断是否是删除头节点
如果删除的是头节点,那么头节点修改为头节点的下一个节点即可。
否则
获取index节点的前一个节点pre以及index节点curr
如果pre或者curr为null的话,无法删除不存在的节点,返回null
否则
pre节点的next指针指向curr节点的下一个节点
size的值-1
public void deleteAtIndex(int index) {
if(index == 0) head = head.next;
Node pre = getNode(index - 1);
Node curr = getNode(index);
if(pre == null || curr == null) return ;
else{
pre.next = curr.next;
size--;
}
}
单链表—整体设计
单链表的整体设计如下
class MyLinkedList {
class Node{
Node next;
int val;
Node(int _val, Node _next){
val = _val;
next = _next;
}
Node(int _val){
val = _val;
}
Node(){
}
}
int size;
Node head;
public MyLinkedList() {
head = new Node();
size = 0;
}
public int get(int index) {
Node n = getNode(index);
return n == null ? -1 : n.val;
}
public void addAtHead(int val) {
Node n = new Node(val);
n.next = head.next;
head.next = n;
size++;
}
public void addAtTail(int val) {
Node n = new Node(val);
Node tail = getNode(size - 1);
if(tail == null){
addAtHead(val);
return;
}
tail.next = n;
n.next = null;
size++;
}
public void addAtIndex(int index, int val) {
if(index > size) return ;
else if(index <= 0) addAtHead(val);
else if(index == size) addAtTail(val);
else{
Node n = new Node(val);
Node curr = getNode(index - 1);
n.next = curr.next;
curr.next = n;
size++;
}
}
public void deleteAtIndex(int index) {
if(index == 0) head = head.next;
Node pre = getNode(index - 1);
Node curr = getNode(index);
if(pre == null || curr == null) return ;
else{
pre.next = curr.next;
size--;
}
}
Node getNode(int index)
{
if(index < 0 || index >= size) return null;
Node curr = head;
for (int i = 0; i <= index; i++)
{
curr = curr.next;
}
return curr;
}
}
双链表
Node
链表的节点Node包含节点的值val和指向下一个节点的指针next,以及指向上一个节点的指针pre。注意在Node的class中要写Node的构造函数。
class Node{
Node pre, next;
int val;
Node(int _val){
val = _val;
}
}
双链表的构造
对于双链表,它包含头节点Node head,尾节点tail,和链表的容量size。
在初始化链表时,将构造head,tail以及将size的值初始化为0。
Node head = new Node(-1);
Node tail = new Node(-1);
int size = 0;
public MyLinkedList() {
head.next = tail;
tail.pre = head;
}
获取某一下标的节点
获取某一下标的节点的函数为getNode,参数为int类型的下标index,返回类型为Node。
由于是双链表,因此既可以从head方向遍历,也可以从tail方向遍历。
所以先判断index是在链表中点的左边还是右边,如果是右边就从tail方向遍历
Node getNode(int index)
{
boolean isLeft = index < size / 2;
if(!isLeft)
{
index = size - index - 1;
}
for(Node curr = isLeft ? head.next :tail.pre;
curr != tail && curr != head;
curr = isLeft ? curr.next : curr.pre)
{
if(index-- == 0) return curr;
}
return null;
}
头插法
头插法插入新节点的函数为addAtHead,参数是int类型的val,是新节点的val。
创建新节点n
将n的next指针指向头节点head的下一个节点
将n的pre指针指向头节点head
将头节点的下一个节点的pre指针指向n
将头节点的next指针指向n
链表的size+1
public void addAtHead(int val) {
Node node = new Node(val);
node.next = head.next;
node.pre = head;
head.next.pre = node;
head.next = node;
size++;
}
尾插法
尾插法插入新节点的函数为addAtTail,参数为int类型的val,是新节点的val。
创建新节点node
node的next指针指向尾节点tail
node的pre指针指向tail的上一个节点tail.pre
tail的上一个节点tail.pre的next指针指向node
tail的pre指针指向node
size的值+1
需要注意指针修改的顺序
public void addAtTail(int val) {
Node node = new Node(val);
node.next = tail;
node.pre = tail.pre;
tail.pre.next = node;
tail.pre = node;
size++;
}
在下标index的位置插入
在下标index的位置插入的函数为addAtIndex,参数为int类型的下标index和int类型的新节点的值val
首先判断下标index是否有效
否则
创建新节点node
使用getNode函数获取index下标的节点curr
node的next指针指向curr
node的pre指针指向curr的上一个节点curr.pre
curr的上一个节点curr.pre的next指针指向node
curr的pre指针指向node
size的值+1
public void addAtIndex(int index, int val) {
if(index > size) return ;
if(index <= 0)
{
addAtHead(val);
}
else if(index == size)
{
addAtTail(val);
}
else
{
Node node = new Node(val);
Node curr = getNode(index);
node.next = curr;
node.pre = curr.pre;
curr.pre.next = node;
curr.pre = node;
size++;
}
}
删除某一下标的节点
删除某一下标的节点的函数为deleteAtIndex,参数为int类型的下标index
首先使用getNode函数获取index下标的节点curr
如果这个节点为null,那么直接返回
否则
curr的前一个节点的next指针指向curr的下一个节点
curr的下一个节点的pre指针指向curr的上一个节点
size的值-1
public void deleteAtIndex(int index)
{
Node curr = getNode(index);
if(curr == null) return ;
else
{
curr.pre.next = curr.next;
curr.next.pre = curr.pre;
size--;
}
}
双链表整体设计
双链表的整体设计如下
class MyLinkedList {
class Node{
Node pre, next;
int val;
Node(int _val){
val = _val;
}
}
Node head = new Node(-1);
Node tail = new Node(-1);
int size = 0;
public MyLinkedList() {
head.next = tail;
tail.pre = head;
}
public int get(int index) {
Node node = getNode(index);
return node == null ? -1 : node.val;
}
public void addAtHead(int val) {
Node node = new Node(val);
node.next = head.next;
node.pre = head;
head.next.pre = node;
head.next = node;
size++;
}
public void addAtTail(int val) {
Node node = new Node(val);
node.next = tail;
node.pre = tail.pre;
tail.pre.next = node;
tail.pre = node;
size++;
}
public void addAtIndex(int index, int val) {
if(index > size) return ;
if(index <= 0)
{
addAtHead(val);
}
else if(index == size)
{
addAtTail(val);
}
else
{
Node node = new Node(val);
Node curr = getNode(index);
node.next = curr;
node.pre = curr.pre;
curr.pre.next = node;
curr.pre = node;
size++;
}
}
public void deleteAtIndex(int index)
{
Node curr = getNode(index);
if(curr == null) return ;
else
{
curr.pre.next = curr.next;
curr.next.pre = curr.pre;
size--;
}
}
Node getNode(int index)
{
boolean isLeft = index < size / 2;
if(!isLeft)
{
index = size - index - 1;
}
for(Node curr = isLeft ? head.next :tail.pre;
curr != tail && curr != head;
curr = isLeft ? curr.next : curr.pre)
{
if(index-- == 0) return curr;
}
return null;
}
}
/**
* Your MyLinkedList object will be instantiated and called as such:
* MyLinkedList obj = new MyLinkedList();
* int param_1 = obj.get(index);
* obj.addAtHead(val);
* obj.addAtTail(val);
* obj.addAtIndex(index,val);
* obj.deleteAtIndex(index);
*/