链表
一、概述
线性表是最常用的也是最简单的一种数据结构,而线性表又分为顺序表和链表,顺序表就是Java中的ArrayList集合了,而链表则是对应的是LinkedList。相对来说,线性表的随机访问速度较快,而链表必须要从头节点开始查找;链表的优势在于插入和删除的复杂度较低,时间复杂度为O(n)。一个单链表的示意图如下:
https://www.cnblogs.com/skywang12345/p/3561803.html
一个简单Java单链表的成员域如下:
/* Domain */ private int size; Node Head; /* Node Definition */ class Node <T> { T val; Node<T> next; Node(T x) { val = x; } }
务必谨记节点成员类与链表类的关系。
除单链表外,双向链表也是很常用的一种链表,双链表不仅可以指向后继,还将指向前驱节点,将访问前驱节点的时间复杂度从O(n)将为O(1),性能更加优异,相应的实现也会更加复杂一些,需增加前驱指针。
事实上,还有一种链表不被经常使用,这里做简单介绍——循环链表(circular linked list),特点是最后一个节点的指针指向头节点,这样,单链表也能通过任一节点找到其他所有节点。基本操作和单链表相同,只是遍历时将判空改为判断是否等于头节点。如果将双向链表和循环链表结合一起就产生了循环双向链表或者叫双向循环链表。这样从链表中的一个节点访问另一个节点效率将会十分之高。
注意:一般大家所说的双向链表均为本文中的循环双向链表,我在此引入这些概念只是为了更好的解释和介绍。
/* Domain */
private int size;
Node Head;
Node Tail;
/* Node Definition */
class Node <T>
{
T val;
Node<T> next;
Node<T> prior;
Node(T x) { val = x; }
}
二、链表实现
类
class List <T> { /* Domain */ private int size; Node Head; /* Node Definition */ class Node <T> { T val; Node<T> next; Node(T x) { val = x; } } /* Constructor */ public List() { size = 0; Head = null; } /* Function */ public int len() { return size; } public void add (T v, int i) { Node <T> N = new Node (v); if (i > size || i < 0) { System.out.print("Out of length"); } else if (i == 0) { Head = N; size++; } else { int m=1; Node p = Head; while(m < i) { p = p.next; m++; } N.next = p.next; p.next = N; size++; } } public void delete (int j) { if (j > size || j <= 0) { System.out.print("Out of length"); } else if (j == 1) { Head = Head.next; } else { int m=1; Node p = Head; while(m < j - 1) { p = p.next; m++; } p.next = p.next.next; size--; } } public void print() { System.out.print("Head->"); Node q = Head; while (q!=null) { System.out.print(q.val+"->"); q=q.next; } System.out.print("Tail\n"); } }
测试小case代码:
public static void main(String [] args) { List <Integer> l = new List(); for (int i = 0; i < 5; i++) l.add(i+1,l.len()); l.print(); System.out.print(l.len()+"\n"); l.delete(4); l.print(); System.out.print(l.len()+"\n"); l.delete(2); l.print(); System.out.print(l.len()+"\n"); }
结果:
Head->1->2->3->4->5->Tail 5 Head->1->2->3->5->Tail 4 Head->1->3->5->Tail 3
核心操作:
在节点p后添加节点q:
q.next=p.next;
p.next=q;
删除节点p后面的节点:
p.next=p.next.next;
双链表的核心操作:
在节点p后添加节点q:
q.next=p.next;
p.next.prior=q;
p.next=q;
q.prior=p;(顺序不能随意哦)
在节点p前添加节点q:
q.prior=p.prior;
p.prior.next=q;
p.prior=q;
q.next=p;
删除节点p
p.prior.next=p.next;
p.next.prior=p.prior;
无需死记硬背,找出规律便得心应手!
三、实战
Remove Nth Node From End of List
Given a linked list, remove the n-th node from the end of list and return its head.
Example:
Given linked list: 1->2->3->4->5, and n = 2. After removing the second node from the end, the linked list becomes 1->2->3->5.
Note:
Given n will always be valid.
Follow up:
Could you do this in one pass?
class ListNode { int val; ListNode next; ListNode(int x) { val = x; } } public class LinkList { ListNode removeNthFromEnd(ListNode head, int n) { if(head.next == null) { return null; } ListNode q=head; ListNode p=q; for(int i =1; i<n;i++) { p=p.next; } if(p.next == null) { return head.next; } else { for( ; p.next.next!=null;p=p.next) q=q.next; } if(q.next.next == null) { q.next = null; return head; } else { q.next = q.next.next; return head; } } }
不要忘了判断是不是只有一个节点的情况,就因为这个我多提交了一次