链表基础
链表是一个真正动态的数据结构,数据存在节点(Node),一个节点挂这里一个节点,不需要处理固定容量问题.
一个节点的表现形式

整个链表的表示方法

节点Node的设计
作为链表的内部类
// 节点作为链表的内部类设计private class Node {// 存放在节点中的类型,数据public E e;// 指向下一个节点的变量// 下一个节点也是node,这是节点设计中最重要的一环public Node next;// 提供构造方法1public Node(E e, Node next) {this.e = e;// 构造的这个新的节点,直接指向用户传进来的这个节点this.next = next;}// 构造方法2public Node(E e) {// 如果只传数据,那么这个节点不指向任何节点,this(e, null);}// 提供无参构造public Node() {this(null, null);}public String toString() {return e.toString();}}
链表的基础设计
需要一个head属性作为链表的头结点
size变量表示链表的长度
public class LinkedList<E> {//内部类
//................//链表的框架设计private Node head;//头结点private int size;//链表长度//提供构造函数public LinkedList(){head = new Node();size = 0;}//获取链表中的元素个数public int getSize(){return size;}//判断链表是否为空public boolean isEmpty(){return size == 0;}}
链表的添加操作
头部添加分析
创建一个新节点,指向链表的头节点
维护原链表的头节点
维护size

//在链表的头部添加public void addFirst(E e){//构造线性节点Node node = new Node(e);//让这个节点的下一个节点指向头节点node.next = head;//维护头节点head = node;//维护sizesize++;//根据节点的构造Node(E e, Node next)//前三行代码可写成//head = new Node(e,head);}
中间添加节点
为了方便理解,引入索引概念(链表中没有索引概念)
创建一个元素为e的新的节点
找到插入位置的前一个节点,定义为 Node prev
创建的新节点的node指向要插入的节点prev.next
插入位置的前一个节点prev,指向插入的节点

注意顺序很重要
public void add(E e,int index){//判断要插入位置的有效性if(index < 0 || index > size){throw new IllegalArgumentException("Add failed. Illegal index");}//先判断是否为头节点if(index == 0){addFirst(e);}else{//1.定义插入位置的前一个节点(默认位置头节点)Node prev = head;//2.寻找prev的位置//!!!循环此链表,寻找需要插入位置的前一个节点//就是把头结点(prev,head),向后推,移动index- 1次//就到了需要插入节点位置的前一个for(int i = 0;i < index -1;i++){//找到插入位置的前一个节点prev = prev.next;}//构造待插入节点Node node = new Node(e);//插入节点指向prev的下一个节点(插入位置节点)node.next = prev.next;//prev节点的指向的下一个节点就是插入节点prev.next = node;size++;//三行合并prev.next = new Node(e,prev.next);}}
链表的操作优化
可以设置一个虚拟头节点,可以把添加操作合并
所以整体结构稍微改变,主要是声明变量声明是虚拟头节点
public LinkedList<E>{private Node dummyHead;//虚拟头结点private int size;//设置构造public LinkedList(){//初始化时是一个节点dummyHead = new Node(null, null);size = 0;}//添加操作public void add(int index, E e){//此位置判断位置的有效性,省略//..........//1.定义要插入节点的前一个节点Node prev = dummyHead;//遍历寻找prev的位置,如果要插入的头节点,这样的话,头节点也有前一个节点//就是dummyHead//因为是虚拟节点(头节点的前一个节点),//所以比之前还要多向后移一下for(int i = 0 ; i < index; i++){prev = prev.next;}prev.next = new Node(e, prev.next);size++;}//添加头public void addFirst(E e){add(0, e);}// 在链表末尾添加新的元素epublic void addLast(E e){add(size, e);}}
获取指定位置元素
public E get(int 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);}
链表的修改
和获取思路一样
public void set(int index, E e){//判断传入位置的正确性//......//同获取一样,先得要需要修改节点的位置Node cur = dummyHead.next;for(int i= 0; i < index; i++){cur = cur.next;}cur.e = e;}
判断聊表中是否包含元素
//判断链表中是否包含元素public boolean contains(E e){//定义当前节点Node cur = dummyHead.next;//整条链表遍历for(int i = 0; i< size;i++){//如果找到,直接return,退出函数if(cur.e.equals(e)){return true;}}return false;}
链表的删除操作
思路
1.找到需要删除节点的前一个节点prev
2.让prev节点的下一个节点指向,需要删除节点的下一个节点(跨过出删除的节点)
3.将删除节点的指向下一个节点至空,脱离链表,方便垃圾回收机制回收
简单图示

//删除指定位置元素,返回删除元素public E remove(int 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;//维护sizesize--;return retNode.e;}
结束语
喜欢的小伙伴可以点个关注,点个赞赏呗

文章转载自敲代码的人,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。




