链表:动态数据结构

本文深入探讨了链表作为动态数据结构的特性,无需处理固定容量问题,但失去了随机访问能力。讲解了链表节点的定义,并展示了如何在链表头和中间添加元素,以及如何利用虚拟头结点简化操作。此外,还阐述了链表的遍历、查询、修改、删除元素的方法。最后,讨论了链表在实现栈和队列中的应用,并对比了链表与数组在这些场景下的时间复杂度差异。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

链表:动态数据结构

1、什么是链表

动态数组、栈和队列:底层依托静态数组;靠resize解决固定容量问题

链表:

  • 真正的动态数据结构。不需要处理固定容量的问题。但是丧失了随机访问的能力。

  • 更深入的理解引用(或者指针);更深入的理解递归。

  • 辅助组成其他数据结构。


class Node{

​ E e;

​ Node next;

}

public class LinkedList<E> {
	private class Node{
		public E e;
		public Node next;//在LinkedList类中可以随意访问和修改
		public Node(E e,Node next) {
			this.e=e;
			this.next=next;
		}
    }
}

2、链表操作–添加元素

(1)在链表头添加元素:

node.next=head; head=node;

	public void addFirst(E e) {
//		Node node=new Node(e);
//		node.next=head;
//		head=node;
		head.next=new Node(e,head);
		size++;
	}

(2)在链表中间添加元素:

find prev(添加的节点的前一个节点); node.next=prev.next; prev.next=node

特殊:在首部添加节点

public void add(int index,E e) {
		if(index<0||index>size) {
			throw new IllegalArgumentException("illegal index");
		}
		if(index==0) {
			addFirst(e);
		}else {
			Node prev=head;
			for(int i=0;i<index-1;i++) {
				prev=prev.next;
			}
			prev.next=new Node(e,prev.next);
			size++;
		}
}

3、虚拟头结点dummyHead

不需区分首部和中间节点。添加元素。

private Node dummyHead;
dummyHead=new Node(null,null);
#核心代码
Node prev=dummyHead;
for(int i=0;i<index;i++) {
	prev=prev.next;
}
prev.next=new Node(e,prev.next);
size++;

4、链表的遍历、查询和修改

set、get、contains

public E get(int index) {
		if(index<0||index>size) {
			throw new IllegalArgumentException("illegal index");
		}
		Node cur=dummyHead.next;
		for(int i=0;i<index;i++) {
			cur=cur.next;
		}
		return cur.e;   //set:cur.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;
}

5、链表元素删除

在这里插入图片描述

#按index删除
public E remove(int index) {
		if(index<0||index>=size) {
			throw new IllegalArgumentException("illegal index");
		}
		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 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 --;
        }
}

6、时间复杂度分析

在这里插入图片描述

8、使用链表实现栈

LinkedListStack

push–addFirst 链表头是栈顶

public class LinkedListStack<E> implements Stack<E>{
	private LinkedList<E> list;
	public LinkedListStack() {
		list=new LinkedList<>();
	}
	public int getSize() {
		return list.getSize();
	}
    ……isEmpty\push\pop\peek等操作
}

数组栈和链表栈的比较:

时间复杂度差别不大

9、使用链表实现队列(无虚拟头结点)

增加尾指针tail,插入元素变得方便,但是删除元素仍要遍历找到retNode的前一个节点,复杂度较高。

因此,实现队列:

从head端删除元素,从tail端插入元素。

由于没有dummyHead,要注意链表为空的情况。

public class LinkedListQueue<E> implements Queue<E>{
    //内部类Node,Node head和tail,size
    //LinkedListQueue初始化
}
#增加尾指针
private Node head,tail;
	@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("opration failed");
		}
		Node retNode=head;
		head=head.next;
		retNode.next=null;
		if(head==null)
			tail=null;
		size--;
		return retNode.e;
		
	}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值