链表
数组要一块连续的内存空间来存储,对内存的要求比较高。如果我们申请一个100MB大小的数组,当内存中没有连续的、足够大的存储空间 时,即便内存的剩余总可用空间大于 100MB,仍然会申请失败。
链表恰恰相反,它并不需要一块连续的内存空间,它通过“指针”将一组零散的内存块串联起来使用,所以如果我们申请的是 100MB 大小的链表,根本不会有问题。
链表通过指针将一组零散的内存块串联在一起。其中,我们把内存块称为链表的“结点”。为了将所有的结点串起来,每个链表的结点除了存储数据之外,还需要记录链上的下一个结点 的地址。如图所示,我们把这个记录下个结点地址的指针叫作后继指针 next。
单链表(数组)
其中有两个结点是比较特殊的,它们分别是第一个结点和最后一个结点。我们习惯性地把第一个结点叫作头结点,把最后一个结点叫作尾结点。其中,头结点用来记录链表的基地址。有了它,我们就可以遍历得到整条链表。而尾结点特殊的地方是:指针 不是指向下一个结点,而是指向一个空地址 NULL,表示这是链表上最后一个结点。与数组一样,链表也支持数据的查找、插入和删除操作。只不过效率会不高。
循环链表
循环链表是一种特殊的单链表。它跟单链表唯一的区别就在尾结点:单链表的尾结点指针指向空地址,表示这就是最后的结点了。而循环链表的尾结点指针是指向链表的头结点。
双向链表
双向链表需要额外的两个空间来存储后继结点和前驱结点的地址。所以,如果存储同样多的数据,双向链表要比单链表占用更多的内存空间。虽然两个指针比较浪费存储空间,但可以支持双向遍历,这样也带来了双向链表操作的灵活性。那相比单链表,双向链表适解决删除结点中“值等于某个给定值”的结点,删除给定指针指向的结点。
双向循环链表
双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱。所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点。一般我们都构造双向循环链表。
代码实现
单向链表
public class LinkedList<E> {
private class Node {
public E e;
public Node next;
public Node(E e, Node next) {
this.e = e;
this.next = next;
}
public Node(E e) {
this(e, null);
}
public Node() {
this(null, null);
}
@Override
public String toString() {
return e.toString();
}
}
private Node dummyHead;
private int size;
public LinkedList() {
dummyHead = new Node(null, null);
size = 0;
}
public int getSize() {
return size;
}
public boolean isEmpty() {
return size == 0;
}
//为链表头添加新的元素E
public void addFirst(E e) {
add(0, e);
}
//在链表的index(0-based)位置添加新的元素e
//链表中不是一个常用的操作
public void add(int index, E e) {
if (index < 0 || index > size) {
throw new IllegalArgumentException("Add Failed.Illegal index.");
}
Node prev = dummyHead;
for (int i = 0; i < index; i++) {
prev = prev.next;
}
prev.next = new Node(e, prev.next);
size++;
}
public void addLast(E e) {
add(size, e);
}
public E get(int index) {
if (index < 0 || index >= size) {
throw new IllegalArgumentException("Get Failed.Illegal index");
}
Node cur = dummyHead.next;
for (int i = 0; i < index; i++) {
cur = cur.next;
}
return cur.e;
}
//获取链表的第一个元素
public E getFirst() {
return get(0);
}
//获取链表的最后一个元素怒
public E getLast() {
return get(size - 1);
}
//修改链表的index元素(0-based)个位置的元素
//不常用的操作
public void set(int index, E e) {
if (index < 0 || index >= size) {
throw new IllegalArgumentException("Update Failed.Illegal index");
} else {
Node cur = dummyHead.next;
for (int i = 0; i < index; i++) {
cur = cur.next;
}
cur.e = e;
}
}
//查找链表是否存在元素E
public boolean contains(E e) {
Node cur = dummyHead.next;
while (cur != null) {
if (cur.e.equals(e)) {
return true;
}
cur = cur.next;
}
return false;
}
public E remove(int index) {
if (index < 0 || index >= size) {
throw new IllegalArgumentException("Update Failed.Illegal index");
} else {
Node prev = dummyHead;
for (int i = 0; i < index; i++) {
prev = prev.next;
}
Node retNode = prev.next;
prev.next = retNode.next;
retNode.next = null;
size--;
return retNode.e;
}
}
public E removeFirst() {
return remove(0);
}
public E removeLast() {
return remove(size - 1);
}
@Override
public String toString() {
StringBuilder res = new StringBuilder();
for (Node cur = dummyHead.next; cur != null; cur = cur.next) {
res.append(cur + "->");
}
res.append("NULL");
return res.toString();
}
}
public class Main {
public static void main(String[] args) {
LinkedList<Integer> linkedList = new LinkedList<>();
for (int i = 0; i < 5; i++) {
linkedList.addFirst(i);
System.out.println(linkedList);
}
linkedList.add(2, 666);
System.out.println(linkedList);
linkedList.remove(2);
System.out.println(linkedList);
linkedList.removeFirst();
System.out.println(linkedList);
linkedList.removeLast();
System.out.println(linkedList);
}
}
循环链表
public class SinglyLoopLink {
public Node head;
public Node tail;
public int size;
public SinglyLoopLink(){
this.size=0;
this.head=new Node(0);
this.tail=new Node(0);
head.next=tail;
tail.next=head;
}
class Node {
Node next;
int data;
public Node(){
}
public Node(int data){
this.data=data;
}
}
public void add(int data){
Node newNode=new Node(data);
if (head == null) {
newNode.next=tail;
head.next=newNode;
}else{
Node temp=head;
while(temp.next!=tail){
temp=temp.next;
}
temp.next=newNode;
newNode.next=tail;
}
size++;
}
public String add(int index,int data){
Node temp=head;
Node newNode=new Node(data);
if(index<0||index>size){
return "数组指标有误";
}
for(int i=0;i<size;i++){
temp=temp.next;
if(index==0){
head.next=newNode;
newNode.next=temp;
size++;
break;
}else if(i==index-1){
System.out.println("this is "+size);
newNode.next=temp.next;
temp.next=newNode;
size++;
break;
}
}
return null;
}
public String addFirst(int data){
return add(0,data);
}
public String addLast(int data){
return add(size,data);
}
public String disPlay(){
SinglyLoopLink.Node temp=head;
StringBuffer result=new StringBuffer("[=>head=>");
for(int i=0;i<size;i++){
temp=temp.next;
result.append(temp.data+"=>");
}
result.append("tail=>]");
return result.toString();
}
public static void main(String[] args) {
SinglyLoopLink singlyLoopLink=new SinglyLoopLink();
singlyLoopLink.add(1);
singlyLoopLink.add(2);
singlyLoopLink.add(0,-9);
singlyLoopLink.add(1,9);
singlyLoopLink.add(4,10);
singlyLoopLink.addFirst(-99);
singlyLoopLink.addLast(100);
singlyLoopLink.add(0,-155);
singlyLoopLink.add(3,10);
System.out.println(singlyLoopLink.disPlay());
}
}
双向链表
public class LinkList {
Node head;
Node tail;
//申明该变量不会被持久化
transient int size=0;
int data;
class Node{
int data;
Node pre;
Node next;
Node(int data,Node pre,Node next){
this.data=data;
this.pre=pre;
this.next=next;
}
Node(int data){
this.data=data;
}
Node(){
}
}
public LinkList(int size,Node head,Node tail){
this.size=0;
this.head=head;
this.tail=tail;
}
public LinkList(){
head=new Node();
tail=new Node();
}
//添加头部节点(只有一个元素的时候)
public void addHeadNode(int data){
Node node=new Node(data,null,null);
head.next=node;
node.pre=head;
node.next=tail;
tail.pre=node;
size++;
}
//尾插法
public void addLast(int data){
if(head.next==null){
addHeadNode(data);
}else{
Node temp=head;
Node newNode=new Node(data);
for(int i=0;i<size;i++){
temp=temp.next;
}
newNode.pre=temp;
newNode.next=tail;
temp.next=newNode;
tail.pre=newNode;
size++;
}
}
//头插法
public void addBegin(int data){
// add(0,data);
if(head.next==null){
addHeadNode(data);
size++;
}else {
Node newNode = new Node(data);
newNode.next = head.next;
newNode.pre = head;
head.next = newNode;
size++;
}
}
//从指定索引位置插入相应的内容
// public void add(int index,int data){
// //链表里面已经有相应的元素了
//// if(head.next!=null){
// Node temp=head;
// Node newNode=new Node(data);
// if(index==0){
// newNode.pre=head;
// newNode.next=head.next;
// head.next=newNode;
// }
// if(index<size&&index>0){ //最后一个节点处插入需要特殊处理
// for(int i=0;i<index;i++){
// temp=temp.next;
// }
// newNode.next=temp.next;
// newNode.pre=temp;
// temp.next.pre=newNode;
// temp.next=newNode;
// }
// if(index==size&&size!=0){
// for(int i=0;i<index;i++){
// temp=temp.next;
// }
// temp.next=newNode;
// newNode.pre=temp;
// newNode.next=tail;
// tail.pre=newNode;
// }
// size++;
//// }
//
// }
public Node get(int index){
if(index>size || index<0){
throw new RuntimeException("链表下标超出范围了!");
}
Node currentNode=head;
for(int j=0;j<size;j++){
currentNode=head.next;
if(j==index){
return currentNode;
}
}
return null;
}
//根据索引,删除某一个链表元素内容
public String remove(int index){
Node temp=head;
if(index>=size){
return "下标超值";
}
for(int i=0;i<size;i++){
//并非最后一个数值
if(i==index){
temp.next=temp.next.next;
temp.next.pre=temp;
size--;
break;
}
temp=temp.next;
}
return null;
}
public void removeData(Integer data){
Node temp=head;
Node preNode;
for(int i=0;i<size;i++){
preNode=temp;
temp=temp.next;
if(temp.data==data){
preNode.next=temp.next;
size--;
break;
}
}
}
public void removeNode(Node itemNode){
Node preNode=itemNode.pre;
Node nextNode=itemNode.next;
preNode.next=nextNode;
size--;
}
public String removeFirst(){
return remove(0);
}
public String removeLast(){
// if(size==2){ //剩余头尾两个节点和一个元素的时候
// remove(0);
// }
return remove(size-1);
}
public String disPlay(){
Node temp=head.next;
StringBuffer result=new StringBuffer("[head<==>");
int index=0;
for(int i=0;i<size;i++){
index++;
result.append(temp.data+"<==>");
temp=temp.next;
}
result.append("tail]");
return result.toString();
}
public static void main(String[] args) {
LinkList linkList=new LinkList();
linkList.addLast(1);
linkList.addLast(2);
linkList.addLast(3);
linkList.addBegin(34);
linkList.addLast(44);
System.out.println(linkList.disPlay());
linkList.removeData(2);
linkList.removeData(3);
System.out.println(linkList.disPlay());
Node node=linkList.get(0);
linkList.removeNode(node);
System.out.println(linkList.disPlay());
}
双向循环链表
public class DoubleLoopLink {
Node head;
Node tail;
//申明该变量不会被持久化
transient int size=0;
int data;
class Node{
int data;
Node pre;
Node next;
Node(int data,Node pre,Node next){
this.data=data;
this.pre=pre;
this.next=next;
}
Node(int data){
this.data=data;
}
Node(){
}
}
public DoubleLoopLink(int size,Node head,Node tail){
this.size=0;
this.head=head;
this.tail=tail;
}
public DoubleLoopLink(){
head=new Node();
head.pre=tail;
head.next=tail;
tail=new Node();
tail.pre=head;
tail.next=head;
}
//添加头部节点(只有一个元素的时候)
public void addHeadNode(int data){
Node node=new Node(data,null,null);
head.next=node;
node.pre=head;
node.next=tail;
tail.pre=node;
size++;
}
//尾插法
public void add(int data){
if(head.next==null){
addHeadNode(data);
}else{
Node temp=head;
Node newNode=new Node(data);
for(int i=0;i<size;i++){
temp=temp.next;
}
newNode.pre=temp;
newNode.next=tail;
temp.next=newNode;
tail.pre=newNode;
size++;
}
}
//头插法
public void addBegin(int data){
if(head.next==null){
addHeadNode(data);
size++;
}else {
Node newNode = new Node(data);
newNode.next = head.next;
newNode.pre = head;
head.next = newNode;
size++;
}
}
//从指定索引位置插入相应的内容
public void add(int index,int data){
//链表里面已经有相应的元素了
// if(head.next!=null){
Node temp=head;
Node newNode=new Node(data);
if(index==0){
newNode.pre=head;
newNode.next=head.next;
head.next=newNode;
}
if(index<size&&index>0){ //最后一个节点处插入需要特殊处理
for(int i=0;i<index;i++){
temp=temp.next;
}
newNode.next=temp.next;
newNode.pre=temp;
temp.next.pre=newNode;
temp.next=newNode;
}
if(index==size&&size!=0){
for(int i=0;i<index;i++){
temp=temp.next;
}
temp.next=newNode;
newNode.pre=temp;
newNode.next=tail;
tail.pre=newNode;
}
size++;
// }
}
//根据索引,删除某一个链表元素内容
public String remove(int index){
Node temp=head;
if(index>=size){
return "下标超值";
}
for(int i=0;i<size;i++){
//并非最后一个数值
if(i==index){
temp.next=temp.next.next;
temp.next.pre=temp;
size--;
break;
}
temp=temp.next;
}
return null;
}
public String removeFirst(){
return remove(0);
}
public String removeLast(){
return remove(size);
}
public String disPlay(){
Node temp=head.next;
StringBuffer result=new StringBuffer("[<==>head<==>");
int index=0;
for(int i=0;i<size;i++){
index++;
result.append(temp.data+"<==>");
temp=temp.next;
}
result.append("<==>tail<==>]");
return result.toString();
}
public static void main(String[] args) {
DoubleLoopLink doubleLoopLink=new DoubleLoopLink();
doubleLoopLink.add(-1);
doubleLoopLink.add(-2);
doubleLoopLink.add(-3);
System.out.println(doubleLoopLink.disPlay());
}
}
时间复杂度分析
添加操作 | O(n) |
---|---|
addFirst | O(1) |
addLast | O(n) |
add(index,e) | O(n/2)=O(n) |
删除操作 | O(n) |
---|---|
rremoveFirst(e) | O(1) |
removeLast | O(n) |
remove(index,e) | O(n/2)=O(n) |
修改操作 | O(n) |
---|---|
set(index,e) | O(n) |
查找操作 | O(n) |
---|---|
get(index) | O(n) |
contains(e) | O(n) |
如果值对链表头进行操作时间复杂度为O(1)
链表栈
public interface Stack<E> {
int getSize();
boolean isEmpty();
void push(E e);
E pop();
E peek();
}
public class LinkedList<E> {
private class Node{
public E e;
public Node next;
public Node(E e, Node next){
this.e = e;
this.next = next;
}
public Node(E e){
this(e, null);
}
public Node(){
this(null, null);
}
@Override
public String toString(){
return e.toString();
}
}
private Node dummyHead;
private int size;
public LinkedList(){
dummyHead = new Node();
size = 0;
}
// 获取链表中的元素个数
public int getSize(){
return size;
}
// 返回链表是否为空
public boolean isEmpty(){
return size == 0;
}
// 在链表的index(0-based)位置添加新的元素e
// 在链表中不是一个常用的操作,练习用:)
public void add(int index, E e){
if(index < 0 || index > size)
throw new IllegalArgumentException("Add failed. Illegal index.");
Node prev = dummyHead;
for(int i = 0 ; i < index ; i ++)
prev = prev.next;
prev.next = new Node(e, prev.next);
size ++;
}
// 在链表头添加新的元素e
public void addFirst(E e){
add(0, e);
}
// 在链表末尾添加新的元素e
public void addLast(E e){
add(size, e);
}
// 获得链表的第index(0-based)个位置的元素
// 在链表中不是一个常用的操作,练习用:)
public E get(int index){
if(index < 0 || index >= size)
throw new IllegalArgumentException("Get failed. Illegal index.");
Node cur = dummyHead.next;
for(int i = 0 ; i < index ; i ++)
cur = cur.next;
return cur.e;
}
// 获得链表的第一个元素
public E getFirst(){
return get(0);
}
// 获得链表的最后一个元素
public E getLast(){
return get(size - 1);
}
// 修改链表的第index(0-based)个位置的元素为e
// 在链表中不是一个常用的操作,练习用:)
public void set(int index, E e){
if(index < 0 || index >= size)
throw new IllegalArgumentException("Update failed. Illegal index.");
Node cur = dummyHead.next;
for(int i = 0 ; i < index ; i ++)
cur = cur.next;
cur.e = e;
}
// 查找链表中是否有元素e
public boolean contains(E e){
Node cur = dummyHead.next;
while(cur != null){
if(cur.e.equals(e))
return true;
cur = cur.next;
}
return false;
}
// 从链表中删除index(0-based)位置的元素, 返回删除的元素
// 在链表中不是一个常用的操作,练习用:)
public E remove(int index){
if(index < 0 || index >= size)
throw new IllegalArgumentException("Remove failed. Index is illegal.");
// E ret = findNode(index).e; // 两次遍历
Node prev = dummyHead;
for(int i = 0 ; i < index ; i ++)
prev = prev.next;
Node retNode = prev.next;
prev.next = retNode.next;
retNode.next = null;
size --;
return retNode.e;
}
// 从链表中删除第一个元素, 返回删除的元素
public E removeFirst(){
return remove(0);
}
// 从链表中删除最后一个元素, 返回删除的元素
public E removeLast(){
return remove(size - 1);
}
// 从链表中删除元素e
public void removeElement(E e){
Node prev = dummyHead;
while(prev.next != null){
if(prev.next.e.equals(e))
break;
prev = prev.next;
}
if(prev.next != null){
Node delNode = prev.next;
prev.next = delNode.next;
delNode.next = null;
size --;
}
}
@Override
public String toString(){
StringBuilder res = new StringBuilder();
Node cur = dummyHead.next;
while(cur != null){
res.append(cur + "->");
cur = cur.next;
}
res.append("NULL");
return res.toString();
}
}
public class LinkedListStack<E> implements Stack<E> {
private LinkedList<E> list;
public LinkedListStack(){
list = new LinkedList<>();
}
@Override
public int getSize(){
return list.getSize();
}
@Override
public boolean isEmpty(){
return list.isEmpty();
}
@Override
public void push(E e){
list.addFirst(e);
}
@Override
public E pop(){
return list.removeFirst();
}
@Override
public E peek(){
return list.getFirst();
}
@Override
public String toString(){
StringBuilder res = new StringBuilder();
res.append("Stack: top ");
res.append(list);
return res.toString();
}
public static void main(String[] args) {
LinkedListStack<Integer> stack = new LinkedListStack<>();
for(int i = 0 ; i < 5 ; i ++){
stack.push(i);
System.out.println(stack);
}
stack.pop();
System.out.println(stack);
}
}
性能比较
数组栈和链表栈对比测试:
import java.util.Random;
public class Main {
// 测试使用stack运行opCount个push和pop操作所需要的时间,单位:秒
private static double testStack(Stack<Integer> stack, int opCount){
long startTime = System.nanoTime();
Random random = new Random();
for(int i = 0 ; i < opCount ; i ++)
stack.push(random.nextInt(Integer.MAX_VALUE));
for(int i = 0 ; i < opCount ; i ++)
stack.pop();
long endTime = System.nanoTime();
return (endTime - startTime) / 1000000000.0;
}
public static void main(String[] args) {
int opCount = 20000000;
ArrayStack<Integer> arrayStack = new ArrayStack<>();
System.out.println("opCount:"+opCount);
double time1 = testStack(arrayStack, opCount);
System.out.println("ArrayStack, time: " + time1 + " s");
LinkedListStack<Integer> linkedListStack = new LinkedListStack<>();
double time2 = testStack(linkedListStack, opCount);
System.out.println("LinkedListStack, time: " + time2 + " s");
// 其实这个时间比较很复杂,因为LinkedListStack中包含更多的new操作
}
}
链表队列
public class LinkedListQueue<E> implements Queue<E> {
private class Node{
public E e;
public Node next;
public Node(E e, Node next){
this.e = e;
this.next = next;
}
public Node(E e){
this(e, null);
}
public Node(){
this(null, null);
}
@Override
public String toString(){
return e.toString();
}
}
private Node head, tail;
private int size;
public LinkedListQueue(){
head = null;
tail = null;
size = 0;
}
@Override
public int getSize(){
return size;
}
@Override
public boolean isEmpty(){
return size == 0;
}
@Override
public void enqueue(E e){
if(tail == null){
tail = new Node(e);
head = tail;
}
else{
tail.next = new Node(e);
tail = tail.next;
}
size ++;
}
@Override
public E dequeue(){
if(isEmpty())
throw new IllegalArgumentException("Cannot dequeue from an empty queue.");
Node retNode = head;
head = head.next;
retNode.next = null;
if(head == null)
tail = null;
size --;
return retNode.e;
}
@Override
public E getFront(){
if(isEmpty())
throw new IllegalArgumentException("Queue is empty.");
return head.e;
}
@Override
public String toString(){
StringBuilder res = new StringBuilder();
res.append("Queue: front ");
Node cur = head;
while(cur != null) {
res.append(cur + "->");
cur = cur.next;
}
res.append("NULL tail");
return res.toString();
}
public static void main(String[] args){
LinkedListQueue<Integer> queue = new LinkedListQueue<>();
for(int i = 0 ; i < 10 ; i ++){
queue.enqueue(i);
System.out.println(queue);
if(i % 3 == 2){
queue.dequeue();
System.out.println(queue);
}
}
}
}
队列性能
性能比较
import java.util.Random;
public class Main {
// 测试使用q运行opCount个enqueueu和dequeue操作所需要的时间,单位:秒
private static double testQueue(Queue<Integer> q, int opCount){
long startTime = System.nanoTime();
Random random = new Random();
for(int i = 0 ; i < opCount ; i ++)
q.enqueue(random.nextInt(Integer.MAX_VALUE));
for(int i = 0 ; i < opCount ; i ++)
q.dequeue();
long endTime = System.nanoTime();
return (endTime - startTime) / 1000000000.0;
}
public static void main(String[] args) {
int opCount = 100000;
ArrayQueue<Integer> arrayQueue = new ArrayQueue<>();
double time1 = testQueue(arrayQueue, opCount);
System.out.println("ArrayQueue, time: " + time1 + " s");
LoopQueue<Integer> loopQueue = new LoopQueue<>();
double time2 = testQueue(loopQueue, opCount);
System.out.println("LoopQueue, time: " + time2 + " s");
LinkedListQueue<Integer> linkedListQueue = new LinkedListQueue<>();
double time3 = testQueue(linkedListQueue, opCount);
System.out.println("LinkedListQueue, time: " + time3 + " s");
}
}
资料参考
<<数据结构与算法之美>>
<<玩转数据结构>>
Gitee: https://gitee.com/IdeaHome_admin/projects