赞
踩
目录
在上一篇中我们介绍了顺序表。在ArrayList任意位置插入或者删除元素时,就需要将后序元素整体往前或者往后 搬移,时间复杂度为O(n),效率比较低,因此ArrayList不适合做任意位置插入和删除比较多的场景。因此:java 集合中又引入了LinkedList,即链表结构
链表是一种在物理存储结构上非连续学校结构,数据元素的逻辑结构是通过链表中的引用链接次序实现的。
注意:
1.链式结构在逻辑上是连续的,但是在物理上不一定连续。
2.节点(结点)一般都是从堆上申请出来的。
3.从堆上申请的空间,是按照一定的策略来分配的,两次申请的空间有可能连续,但也有可能不连续。
根据带头/不带头、单向/双向、循环/非循环来划分,链表有8种结构。
- 带头单向循环链表
- 不带头单向循环链表
- 带头双向循环链表
- 不带头双向循环链表
- 带头单向非循环链表
- 不带头单向非循环链表
- 带头双向非循环链表
- 不带头双向非循环链表
我们今天实现的是不带头单向非循环链表
ILinkList.java
- package MLinkList;
-
- public interface ILinkList {
- // 1、无头单向非循环链表实现
- //头插法
- public void addFirst(int data);
-
- //尾插法
- public void addLast(int data);
-
- //任意位置插入,第一个数据节点为0号下标
- public void addIndex(int index, int data);
-
- //查找是否包含关键字key是否在单链表当中
- public boolean contains(int key);
-
- //删除第一次出现关键字为key的节点
- public void remove(int key);
- //删除所有值为key的节点
- public void removeAllKey(int key);
-
- //得到单链表的长度
- public int size();
-
- //清空链表
- public void clear();
- //打印单链表
- public void display();
- }
我们需要先创建一个内部类,作为链表的节点。并创建个头指针、尾指针。
- /**
- * ListNode 类定义了一个链表节点。
- * 这个类包含一个整型数据和一个指向下一个节点的指针。
- */
- public class ListNode{
- // 节点存储的数据
- public int data;
- // 指向下一个节点的指针
- public ListNode next;
-
- /**
- * ListNode 类的构造函数,用于创建一个新的链表节点。
- *
- * @param data 节点存储的数据。
- */
- public ListNode(int data)
- {
- this.data = data; // 初始化节点数据
- }
- }
- public ListNode head;//头指针
- public ListNode last;//尾指针
进行头插,需要考虑两种情况:
- 头指针为空,让新节点作为头指针
- 头指针不为空,可以进行头插,头插的时候,先让新节点的指针指向头结点,再让头指针移动到新节点位置。
- public void addFirst(int data) {
- ListNode node = new ListNode(data); // 创建新节点,数据为传入的data
- // 判断链表是否为空,如果为空,说明当前没有节点,直接将新节点作为头节点
- if (head == null) {
- head = node;
- return;
- }
- // 链表不为空时,进行头插操作,新节点的next指向当前头节点,然后更新头节点为新节点
- node.next = head;
- head = node;
- }
进行尾插时,同样需要考虑头结点是否为空
- /**
- * 使用尾插法向链表中添加元素。
- * 该方法会将指定的元素添加到链表的末尾。
- *
- * @param data 要添加到链表的数据。
- */
- public void addLast(int data) {
- ListNode node = new ListNode(data); // 创建新节点,数据为data
-
- if (head == null) {
- head = last = node; // 如果链表为空,将新节点同时设置为头节点和尾节点
- return;
- }
- last.next = node; // 将当前尾节点的next指向新节点
- last = last.next; // 更新尾节点为新添加的节点
-
- }
我们需要考虑3种情况:
- 要插入的位置是否合法
- index位置是在链表两端还是在链表内部,如果index==0,那么则直接调用头插法即可,如果index=size(),那么直接调用尾插法即可。
- 在链表内部,我们就需要找到index的前一个节点,再进行插入。(这里查找index的前一个节点,我们可以定义一个方法进行查找)
- private void checkIndex(int index)throws IndexNoLegalException {
- if (index < 0 || index > size()) {
- throw new IndexNoLegalException("index位置不合法"+"index="+index);
- }
- }
-
- private ListNode findIndex(int index) {
- ListNode cur = head;
- while (index-- != 1) {
- cur = cur.next;
- }
- return cur;
- }
- //任意位置插入,第一个数据节点为0号下标
- public void addIndex(int index, int data) {
- //判断index位置是合法
- try {
- checkIndex(index);
- } catch (IndexNotLegalException e) {
- e.printStackTrace();
- }
- if(index==0) {//头插
- addFirst(data);
- }
- if(index==size()) {//尾插
- addLast(data);
- }
- //创建新节点,并查找index位置的前一个节点
- ListNode node = new ListNode(data);
- ListNode findlist=findIndex(index);
- node.next=findlist.next;
- findlist.next=node;
- }
这里主要是单链表的清空,我们也可以直接让head=last=null,但是更好的是把单链表中的节点都清空。
-
- /**
- * 获取单链表的长度。
- * @return 链表中节点的数目。
- */
- public int size() {
- int count = 0; // 初始化计数器
- ListNode cur = head; // 从头节点开始遍历
- while (cur != null) {
- count++;
- cur = cur.next; // 移动到下一个节点
- }
- return count; // 返回计数结果
- }
-
- /**
- * 清空单链表,使其变为空链表。
- */
- public void clear() {
- if (head == null) {
- return; // 如果链表为空,则直接返回
- }
- ListNode cur = head; // 保存头节点的下一个节点,以避免丢失整个链表
- while (cur != null) {
- ListNode curN=cur.next;
- cur=null;
- cur=curN;
- }
- head = last = null; // 最后将头节点和尾节点都设置为null
- }
-
- /**
- * 打印单链表中的所有节点数据。
- */
- public void display() {
- if (head == null) {
- return; // 如果链表为空,则直接返回
- }
- ListNode cur = head; // 从头节点开始遍历
- while (cur != null) {
- System.out.print(cur.data + " "); // 打印当前节点的数据
- cur = cur.next; // 移动到下一个节点
- }
- System.out.println(); // 换行
- }
我们直接遍历单链表一遍即可,找到返回true
- /**
- * 检查单链表中是否包含指定关键字。
- * @param key 需要在单链表中查找的关键字。
- * @return 如果链表中包含关键字,则返回true;否则返回false。
- */
- public boolean contains(int key){
- // 如果链表头节点为空,直接返回false,表示链表为空,不可能包含关键字
- if(head==null){
- return false;
- }
- // 遍历链表,查找是否存在与key相等的节点
- while(head!=null){
- if(head.data==key){
- // 找到相等的节点,返回true
- return true;
- }
- head=head.next; // 移动到下一个节点继续查找
- }
- // 遍历完整个链表都没有找到相等的节点,返回false
- return false;
- }
7.移除第一次出现key关键字的节点&移除所有key关键字的节点
移除出现一次关键字的节点:
- 判断链表是否为空
- 我们可以查找当前节点的下一个节点的值是否与key相同,如果相同,那就让当前节点的指针指向下一个节点的指针
移除所有出现关键字的节点
- 我们需要用到两个指针,一个用来跟踪prev,一个(cur)用来查找与key相同的节点,如果找到,那就让prev.next指向cur.next,删除key节点。
- 当走完循环时,我们还需要判断头结点是否与key相同,如果相同,那就需要让head指向下一个节点。
- /**
- * 删除链表中第一次出现的关键字为key的节点
- * @param key 需要删除的节点的值
- * 如果链表为空或者没有找到值为key的节点,则链表结构不变
- */
- public void remove(int key){
- // 链表为空,直接返回
- if(head==null){
- return;
- }
- ListNode cur=head;
- // 遍历链表,查找第一个值为key的节点
- while(cur!=null) {
- // 找到后,删除该节点
- if(cur.next.data==key){
- cur.next=cur.next.next;
- return;
- }
- cur=cur.next;
- }
- }
- /**
- * 删除链表中所有值为key的节点
- * @param key 需要删除的节点的值
- * 该方法不返回任何内容
- */
- public void removeAllKey(int key){
- // 如果链表为空,则直接返回
- if(head==null){
- return;
- }
- ListNode prev=head;// 记录当前节点的前一个节点
- ListNode cur=head.next;// 记录当前操作的节点
- while(cur!=null){
- if(cur.data==key){
- prev.next=cur.next; // 如果当前节点的值等于key,则删除该节点
- }else{
- prev=cur; // 否则,继续向前遍历
- }
- cur=cur.next;
- }
- // 如果头节点的值等于key,则更新头节点
- if(head.data==key){
- head=head.next;
- }
- }
这些是单链表的一些基本方法,我们测试一下,确实是我们所想的。
链表:
优点:
1.插入和删除操作简单:由于链表的节点通过指针链接,插入和删除操作只需要修改指针的指向,不需要移动其他元素,因此时间复杂度为O(1)。
2.链表可以根据实际情况动态分配内存空间,只需要在插入新节点时分配新的内存,因此避免了固定大小的存储空间的限制。
缺点:
1.随机访问性能差,链表开辟的空间在内存中上随机开辟的,不一定连续,所以在访问的时候需要遍历查找。时间复杂度为O(N).
2.需要额外的存储空间,由于节点与节点之间是通过指针相连的,所以有额外的空间来存储下一个节点的位置。
顺序表:
优点:
1.支持随机访问:由于顺序表在内存中是连续的,可以通过下标直接访问顺序表中的元素。时间复杂度是O(1).
2.存储效率高,不需要开辟额外的空间存储指针。
缺点:
1.在插入和删除数据的时候,需要搬动其他元素,时间复杂度为O(N)。
2.存储空间固定:如果要扩容的话,可能会造成空间浪费。
接口在上
单链表功能实现:MyLinkltstto.java
- package MLinkList;
-
- import SlowList.IndexNoLegalException;
-
- public class MyLinklistto implements ILinkList{
- // 1、无头单向非循环链表实现
- /**
- * ListNode 类定义了一个链表节点。
- * 这个类包含一个整型数据和一个指向下一个节点的指针。
- */
- public class ListNode{
- // 节点存储的数据
- public int data;
- // 指向下一个节点的指针
- public ListNode next;
-
- /**
- * ListNode 类的构造函数,用于创建一个新的链表节点。
- *
- * @param data 节点存储的数据。
- */
- public ListNode(int data)
- {
- this.data = data; // 初始化节点数据
- }
- }
- public ListNode head;//头指针
- public ListNode last;//尾指针
-
- /**
- * 在链表的头部进行插入操作。
- * 该方法会将新节点插入到链表的头部,如果链表为空,则新节点成为头节点;
- * 如果链表不为空,则新节点将成为新的头节点,原头节点成为新节点的下一个节点。
- *
- * @param data 要插入节点的数据。
- */
- public void addFirst(int data) {
- ListNode node = new ListNode(data); // 创建新节点,数据为传入的data
- // 判断链表是否为空,如果为空,说明当前没有节点,直接将新节点作为头节点
- if (head == null) {
- head =last= node;
- return;
- }
- // 链表不为空时,进行头插操作,新节点的next指向当前头节点,然后更新头节点为新节点
- node.next = head;
- head = node;
- }
- /**
- * 使用尾插法向链表中添加元素。
- * 该方法会将指定的元素添加到链表的末尾。
- *
- * @param data 要添加到链表的数据。
- */
- public void addLast(int data) {
- ListNode node = new ListNode(data); // 创建新节点,数据为data
-
- if (head == null) {
- head = last = node; // 如果链表为空,将新节点同时设置为头节点和尾节点
- return;
- }
- last.next = node; // 将当前尾节点的next指向新节点
- last = last.next; // 更新尾节点为新添加的节点
-
- }
-
- /**
- * 检查索引是否合法。
- * @param index 要检查的索引值。
- * @throws IndexNoLegalException 如果索引不合法,则抛出异常。
- */
- private void checkIndex(int index)throws IndexNoLegalException {
- if (index < 0 || index > size()) { // 索引超出范围不合法
- throw new IndexNoLegalException("index位置不合法"+"index="+index);
- }
- }
-
- /**
- * 查找指定索引位置的节点。
- * @param index 目标索引位置。
- * @return 返回指定索引位置的节点。
- */
- private ListNode findIndex(int index) {
- ListNode cur = head; // 从头节点开始遍历
- while (index-- != 1) { // 递减索引,找到目标位置的前一个节点
- cur = cur.next;
- }
- return cur;
- }
-
- /**
- * 在任意位置插入一个节点。
- * @param index 插入位置的索引。
- * @param data 要插入的数据。
- */
- public void addIndex(int index, int data) {
- // 判断插入位置是否合法
- try {
- checkIndex(index);
- } catch (IndexNotLegalException e) {
- e.printStackTrace();
- }
- if(index==0) { // 如果是插入到头部
- addFirst(data);
- }
- if(index==size()) { // 如果是插入到尾部
- addLast(data);
- }
- // 创建新节点,并插入到指定位置
- ListNode node = new ListNode(data);
- ListNode findlist=findIndex(index); // 找到插入位置的前一个节点
- node.next=findlist.next; // 新节点接在前一个节点之后
- findlist.next=node; // 前一个节点指向新节点
- }
-
-
- /**
- * 检查单链表中是否包含指定关键字。
- * @param key 需要在单链表中查找的关键字。
- * @return 如果链表中包含关键字,则返回true;否则返回false。
- */
- public boolean contains(int key){
- // 如果链表头节点为空,直接返回false,表示链表为空,不可能包含关键字
- if(head==null){
- return false;
- }
- // 遍历链表,查找是否存在与key相等的节点
- while(head!=null){
- if(head.data==key){
- // 找到相等的节点,返回true
- return true;
- }
- head=head.next; // 移动到下一个节点继续查找
- }
- // 遍历完整个链表都没有找到相等的节点,返回false
- return false;
- }
-
- /**
- * 删除链表中第一次出现的关键字为key的节点
- * @param key 需要删除的节点的值
- * 如果链表为空或者没有找到值为key的节点,则链表结构不变
- */
- public void remove(int key){
- // 链表为空,直接返回
- if(head==null){
- return;
- }
- ListNode cur=head;
- // 遍历链表,查找第一个值为key的节点
- while(cur!=null) {
- // 找到后,删除该节点
- if(cur.next.data==key){
- cur.next=cur.next.next;
- return;
- }
- cur=cur.next;
- }
- }
- /**
- * 删除链表中所有值为key的节点
- * @param key 需要删除的节点的值
- * 该方法不返回任何内容
- */
- public void removeAllKey(int key){
- // 如果链表为空,则直接返回
- if(head==null){
- return;
- }
- ListNode prev=head;// 记录当前节点的前一个节点
- ListNode cur=head.next;// 记录当前操作的节点
- while(cur!=null){
- if(cur.data==key){
- prev.next=cur.next; // 如果当前节点的值等于key,则删除该节点
- }else{
- prev=cur; // 否则,继续向前遍历
- }
- cur=cur.next;
- }
- // 如果头节点的值等于key,则更新头节点
- if(head.data==key){
- head=head.next;
- }
- }
-
- /**
- * 获取单链表的长度。
- * @return 链表中节点的数目。
- */
- public int size() {
- int count = 0; // 初始化计数器
- ListNode cur = head; // 从头节点开始遍历
- while (cur != null) {
- count++;
- cur = cur.next; // 移动到下一个节点
- }
- return count; // 返回计数结果
- }
-
- /**
- * 清空单链表,使其变为空链表。
- */
- public void clear() {
- if (head == null) {
- return; // 如果链表为空,则直接返回
- }
- ListNode cur = head; // 保存头节点的下一个节点,以避免丢失整个链表
- while (cur != null) {
- ListNode curN=cur.next;
- cur=null;
- cur=curN;
- }
- head = last = null; // 最后将头节点和尾节点都设置为null
- }
-
- /**
- * 打印单链表中的所有节点数据。
- */
- public void display() {
- if (head == null) {
- return; // 如果链表为空,则直接返回
- }
- ListNode cur = head; // 从头节点开始遍历
- while (cur != null) {
- System.out.print(cur.data + " "); // 打印当前节点的数据
- cur = cur.next; // 移动到下一个节点
- }
- System.out.println(); // 换行
- }
- }
-
-
测试:test2.java
- package MLinkList;
-
- public class test2 {
- public static void main(String[] args) {
- MyLinklistto list = new MyLinklistto();
- list.addFirst(2);
- list.addFirst(3);
- list.addFirst(4);
- list.addFirst(4);
- list.addFirst(4);
- list.addLast(5);
- list.addIndex(2, 6);
- list.display();
- System.out.println("=========");
- list.removeAllKey(4);
- list.display();
- }
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。