LinkedList与链表(一)(非循环单向链表)

    • ArrayList的缺陷

通过ArrayList上节课的学习,我们了解到如果ArrayList要删除或插入一个元素,后面的元素都要进行移动,时间复杂度为O(n),效率比较低,因此ArrayList不适合做任意位置的插入和删除操作比较多的场景。因此java集合又引入了ListedList,即链表结构。

    • 链表

2.1链表的结构

链表是一种物理储存上非连续的储存结构,数据元素的逻辑顺序是通过链表中的引用链接次序实现的。

LinkedList与链表(一)(非循环单向链表)_第1张图片
【注意】
从图中可以看出,链表结构在逻辑上是连续的,但是在物理上是不连续的;
实际中的节点都是在堆上的;
从堆上申请的空间,是按照一定的策略进行分配的,两个节点的储存空间可能是连续的可能是不连续的(链表的优点就是不需要物理上连续的大块空间)。

现实中链表的种类很多,但基本就以下8种情况:

  1. 单向或双向

LinkedList与链表(一)(非循环单向链表)_第2张图片
  1. 带头或者不带头

LinkedList与链表(一)(非循环单向链表)_第3张图片
  1. 循环或者非循环

LinkedList与链表(一)(非循环单向链表)_第4张图片

这3种大的类型再任意搭配,如:单向不带头非循环链表,双向不带头循环链表等。但是我们重点掌握的只有两种:

  • 无头单项非循环链表:结构简单,一般不会用来储存数据,实际中更多的是作为其他数据结构的子结构,但是这种结构在笔试和面试中常见。

  • 无头双向链表:在Java中的集合框架LinkedList底层的实现都是无头双向循环链表。

2.2无头单向非循环链表的实现

MyLinkedList的简单实现

package Demo1;

import java.util.LinkedList;

/**
 * Describe:这里简单实现一下LinkedList
 * User:lenovo
 * Date:2023-01-04
 * Time:16:21
 */
class ListNode {
    public int val;
    public ListNode next;

    public ListNode() {}
    public ListNode(int val) {
        this.val = val;
    }

}

public class MyLinkedList {
    ListNode head;

    //为了下面的更好的调试,我们自己创造一个方法,来打印这个链表,但是这个方法并不是有的
    public void display() {
        ListNode cur = head;
        while(cur != null) {
            System.out.print(cur.val + " ");
            cur = cur.next;
        }
        System.out.println();
    }

    //头插法
    public void addFirst(int data) {
        ListNode cur = new ListNode(data);
        cur.next = head;
        head = cur;
    }

    //尾插法
    public void addLast(int data) {
        ListNode cur = head;
        //判断链表是否为空
        if(head == null) {
            head = new ListNode(data);
        }
        //找到链表的最后一个节点
        while(cur.next != null) {
            cur = cur.next;
        }
        ListNode tmp = new ListNode(data);
        cur.next = tmp;
    }

    //任意位置插入,第一个节点为0号下表
    public void addIndex(int index, int data) {
        if(index < 0) {
            throw new ListIndexOutOfException("请检查坐标合法性");
        }
        //如果原链表为空
        if(head == null) {
            head = new ListNode(data);
            return;
        }
        ListNode cur = head;
        ListNode tmp = new ListNode(data);
        int count = 0;
        //如果是头插
        if(index == 0) {
            tmp.next = head;
            head = tmp;
            return;
        }
        //不是头插
        while(cur != null) {
            if(count + 1 == index) {
                tmp.next = cur.next;
                cur.next = tmp;
                return;
            }else {
                count++;
                cur = cur.next;
            }
        }
        if(count != index) {
            throw new ListIndexOutOfException("请检查坐标的合法性");
        }
    }

    //查找关键字key是否在单链表中
    public boolean contains(int key) {
        if(head == null) {
            return true;
        }
        ListNode cur = head;
        while(cur != null) {
            if(cur.val == key) {
                return true;
            }else {
                cur = cur.next;
            }
        }
        return false;
    }

    //删除第一次出现关键字的节点
    public void remove(int key) {
        if(head == null) {
            return;
        }
        ListNode cur = head;
        //第一个节点判断
        if(head.val == key) {
            head = head.next;
            return;
        }
        //其他节点的判断
        while(cur.next != null) {
            if(cur.next.val == key) {
                cur.next = cur.next.next;
                return;
            }else {
                cur = cur.next;
            }
        }
    }

    //删除所有的关键字key的节点
    public void removeAllKey(int key) {
        //链表为空
        if(head == null) {
            return;
        }
        //除了第一个节点,其他节点的判断
        ListNode cur = head;
        while(cur.next != null) {
            if(cur.next.val == key) {
                cur.next = cur.next.next;
            }else {
                cur = cur.next;
            }
        }
        //第一个节点的判断
        if(head.val == key) {
            head = head.next;
        }
    }

    //清除所有节点
    public void clear() {
        head = null;
    }

    //得到单链表的长度
    public int size() {
        int count = 0;
        ListNode cur = head;
        while(cur != null) {
            count++;
            cur = cur.next;
        }
        return count;
    }

}

异常类型

package Demo1;

/**
 * Describe:
 * User:lenovo
 * Date:2023-01-04
 * Time:19:13
 */
public class ListIndexOutOfException extends RuntimeException{
    public ListIndexOutOfException() {}
    public ListIndexOutOfException(String message) {
        super(message);
    }
}

对代码进行测试

import Demo1.MyLinkedList;

import java.util.ArrayList;
import java.util.LinkedList;

/**
 * Describe:
 * User:lenovo
 * Date:2023-01-04
 * Time:15:47
 */
public class Test {
    public static void main(String[] args) {
        MyLinkedList a = new MyLinkedList();
        System.out.println("============头插法============");
        a.addFirst(6);
        a.addFirst(5);
        a.addFirst(4);
        a.addFirst(3);
        a.addFirst(2);
        a.addFirst(1);
        a.display();
        System.out.println("===========尾插法==============");
        a.addLast(7);
        a.addLast(8);
        a.display();
        System.out.println("==========插入任意位置========");
        a.addIndex(0,0);
        a.addIndex(1,1);
        a.addIndex(10,9);
        a.display();
        System.out.println("==========在链表中查找是否包含关键字=======");
        System.out.println(a.contains(0));
        System.out.println(a.contains(99));
        System.out.println("============删除第一个0============");
        a.remove(0);
        a.remove(99);
        a.display();
        System.out.println("===========删除所有的1============");
        a.removeAllKey(1);
        a.removeAllKey(9);
        a.display();
        System.out.println("===========获得链表的长度==========");
        System.out.println(a.size());
        System.out.println("==============清空链表=============");
        a.clear();
        a.display();
    }
}

结果展示

LinkedList与链表(一)(非循环单向链表)_第5张图片

    • 单项链表相关习题

1.现有一个链表,给定一个X值,将小于等于X的节点排在链表的前面,且不改变原来的相对顺序。

2.有两个链表,它们可能有公共的节点,请返回它们的公共节点。

3.有一个单向非循环链表,请判断它是否是回文结构。

4.将两个非递减的单向非循环链表合成一个新的有序的单向非循环链表,打印这个链表。

5.有一个单向非循环链表,找出链表倒数第K个节点。【要求】只遍历一遍链表。

6.有一个单向非循环链表,请找到链表的中间节点,如果链表有两个中间节点,返回第二个中间节点。【要求】只遍历一遍链表

7.有一个单向非循环链表,反转这个链表并输出。

8.在一个单项链表中,删除链表中等于给定值val的节点。

9.有一个单向链表,请判断它是否带环。

10.有一个单向链表,如果有环找到链表的第一个入环节点,如果没有环,返回null

题目的先后顺序可能会有变化,有的题是令一个题的进阶,如果感觉题目有难度,可以先看别的题,最后再看不会的题。

你可能感兴趣的:(java,笔记,java,数据结构)