五分钟“手撕”链表

为了提高大家的学习效率,我把代码放开头,供查阅。 

目录

一、链表的实现代码

二、什么是链表

三、链表的分类

四、链表的常见操作

插入

删除 

五、Java自带的LinkedList 

 两个构造方法

 一些常用方法

六、LinkedList的遍历

七、ArrayList和LinkedList的区别  


一、链表的实现代码

//无头单向链表
package demo1;
public class MySingleLinkedList{
    class ListNode{
        public int var;
        public ListNode next;
        public int val;

        public ListNode(int var) {
            this.var = var;
        }
    }
    ListNode head;
    public void creat(){
        ListNode node1=new ListNode(1);
        ListNode node2=new ListNode(1);
        ListNode node3=new ListNode(1);
        ListNode node4=new ListNode(1);

        node1.next=node2;
        node2.next=node3;
        node3.next=node4;
        head=node1;
    }
    //头插法
    public void addFirst(int data) {
        ListNode node=new ListNode(data);
        node.next=head;
        head=node;
    }
    //尾插法
    public void addLast(int data) {
        ListNode node=new ListNode(data);
        if(head==null){
            head=node;
            return;
        }
        ListNode cur=head;
        while (cur.next!=null){
            cur=cur.next;
        }cur.next=node;
    }

    private void IndexCheck(int index)throws IndexNotLegal {
        if(index<0||index>size()){
            throw new IndexNotLegal("index位置不合法");
        }
    }
    //任意位置插入,第一个数据节点为0号下标
    public void addIndex(int index, int data){
        try {
            IndexCheck(index);
        }catch (IndexNotLegal e){
            e.printStackTrace();
            return;
        }
        ListNode node=new ListNode(data);
        ListNode cur=head;
        if(index==0){
            addFirst(data);
            return;
        }if(index==size()){
            addLast(data);
            return;
        }
        //注意这里是index-1
        for (int i = 0; i < index-1; i++) {
            cur= cur.next;
        }
        node.next=cur.next;
        cur.next=node;
    }
    //查找是否包含关键字key是否在单链表当中
    public boolean contains(int key) {
        ListNode cur=head;
        while (cur!=null){
            if(cur.var==key){
                return true;
            }cur=cur.next;
        }return false;
    }
    //删除第一次出现关键字为key的节点
    public void remove(int key) {
        if(head.var==key){
            head=head.next;
            return;
        }if(head==null){
            return;
        }
        ListNode cur=head;
        while (cur.next!=null){
            if(cur.next.var==key){
                cur.next=cur.next.next;
                return;
            }cur=cur.next;
        }
    }
    //删除所有出现的key节点
    public void removeAllKey(int key) {
        if(head==null){
            return;
        }ListNode prev=head;
        ListNode cur=head.next;
        while (cur!=null){
            if(cur.var==key){
                prev.next= cur.next;
                //这里不能写prev=cur
                cur= cur.next;
            }else {
                prev=cur;
                cur= cur.next;
            }
            if(head.var==key){
                head=head.next;
            }
        }

    }
    //得到单链表的长度
    public int size() {
        ListNode cur=head;
        int count=0;
        while (cur!=null){
            count++;
            cur=cur.next;
        }return count;
    }
    //打印链表
    public void display() {
        ListNode cur=head;
        for (int i = 0; i < size(); i++) {
            System.out.print(cur.var+" ");
            cur=cur.next;
        }
        System.out.println();
    }
    //清除链表
    public void clear() {
        head=null;
    }
}

public class IndexNotLegal extends RuntimeException {
    public IndexNotLegal(){

    }public IndexNotLegal(String s){
        super(s);
    }
}











package demo2;
// 无头双向链表
public class MyLinkedList {

    class ListNode {
        public int var;
        ListNode prev;
        ListNode next;

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

    ListNode head;
    ListNode last;

    //头插法
    public void addFirst(int data) {
        ListNode node = new ListNode(data);
        if (head == null) {
            head = last = node;
            return;
        }
        node.next = head;
        head.prev = node;
        head = node;
    }

    //尾插法
    public void addLast(int data) {
        ListNode node = new ListNode(data);
        if (head == null) {
            head = last = node;
            return;
        }
        last.next = node;
        node.prev = last;
        last = node;
    }

    private void indexCheck(int index) throws IndexNotLegal {
        if (index < 0 || index > size()) {
            throw new IndexNotLegal("index位置不合法");
        }
    }

    private ListNode findIndexCur(int index) {
        ListNode cur = head;
        while (index != 0) {
            cur = cur.next;
            index--;
        }
        return cur;
    }

    //任意位置插入,第一个数据节点为0号下标
    public void addIndex(int index, int data) {
        try {
            indexCheck(index);
        } catch (IndexNotLegal e) {
            e.printStackTrace();
        }

        if (index == 0) {
            addFirst(data);
            return;
        }
        if (index == size()) {
            addLast(data);
            return;
        }
        ListNode node = new ListNode(data);
        ListNode cur = findIndexCur(index);
        //先把node插进去,再调整node的前一个,最后再调cur,因为需要cur来调整
        node.next=cur;
        cur.prev.next=node;
        node.prev=cur.prev;
        cur.prev=node;


    }

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

    //删除第一次出现关键字为key的节点
    public void remove(int key) {
        ListNode cur = head;
        while (cur != null) {
            if (cur.var == key) {
                //头节点
                if (head.var == key) {
                    head.next.prev = null;
                    head = head.next;
                    return;
                }
                //尾巴节点
                if (last.var == key) {
                    last.prev.next = null;
                    last = last.prev;
                    return;
                }
                cur.next.prev = cur.prev;
                cur.prev.next = cur.next;
                return;
            }
            cur = cur.next;
        }
    }

    //删除所有值为key的节点
    public void removeAllKey(int key) {
        ListNode cur = head;
        while (cur != null) {
            if (cur.var == key) {
                //头节点
                if (head.var == key) {
                    head.next.prev = null;
                    head = head.next;
                    return;
                }
                //尾巴节点
                if (last.var == key) {
                    last.prev.next = null;
                    last = last.prev;
                    return;
                }
                cur.next.prev = cur.prev;
                cur.prev.next = cur.next;
            }
            cur = cur.next;
        }
    }



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

    public void display() {
        ListNode cur = head;
        for (int i = 0; i < size(); i++) {
            System.out.print(cur.var + " ");
            cur = cur.next;
        }
        System.out.println();
    }

    public void clear() {
        head = last = null;
    }
}


package demo2;

public class IndexNotLegal extends RuntimeException{
    public IndexNotLegal(){

    }public IndexNotLegal(String s){
        super(s);
    }
}

二、什么是链表

简单来说,像链子一样的数据结构。像火车一节一节车厢一样,每个元素是独立的个体(内部类)。 并且他们在空间里是分散的 

 

为什么分散的还可以找到下一个呢?

答:一个节点里面装着两种东西,一个是值,一个的下一个的地址,这样根据下一个的地址就可以找到下一个了。 

三、链表的分类

常见的链表有三种,但是我这里只介绍两种:无头单链,无头双链。剩下的一种之后再补充。

单链和双链的区别?

答: 一个节点都是一个类。单链装的是值和下个节点双链装的是值和上个节点和下个节点

四、链表的常见操作

插入

怎么插入元素?在链表中很简单! 

单链p下个节点改成n1,n0下个节点改为p。

双链 p下个节点改为n1,n0下个节点改为p(先把p插进去,成一个逆时针),p上个节点改为n0,n1的上个节点改为p(修改原来的两点节点)

注意!还需要考虑一些特殊情况,具体请看代码的实现!

删除 

在链表中删除节点也非常方便,只需改变一个节点的引用(指针)即可。 

单链:n0的下个节点指向n1。

双链:n0下个节点改为n1,n1上个节点改为n0 。

问题来了:为什么p的指向n1可以不删?

答;原因是以及没人指向p了, 如果没人引用(指向)的东西,就会被系统自动回收。 像clear()方法,head不指向首个节点,那么首个节点被回收,同理下面也如此。

五、Java自带的LinkedList 

 两个构造方法

方法解释
LinkedList()无参构造
public LinkedList(Collection c)使用其他集合容器中元素构造List
public static void main(String[] args) {
// 构造一个空的LinkedList
    List<Integer> list1 = new LinkedList<>();
    List<String> list2 = new java.util.ArrayList<>();
    list2.add("JavaSE");
    list2.add("JavaWeb");
    list2.add("JavaEE");
// 使用ArrayList构造LinkedList
    List<String> list3 = new LinkedList<>(list2);
}

 一些常用方法

方法解释
boolean add(E e)尾插 e
void add(int index, E element)将 e 插入到 index 位置
boolean addAll(Collection c)尾插 c 中的元素
E remove(int index)删除 index 位置元素
boolean remove(Object o)删除遇到的第一个 o
E get(int index)获取下标 index 位置元素
E set(int index, E element)将下标 index 位置元素设置为 element
void clear()清空
boolean contains(Object o)判断 o 是否在线性表中
int indexOf(Object o)返回第一个 o 所在下标
int lastIndexOf(Object o)返回最后一个 o 的下标
List subList(int fromIndex, int toIndex)截取部分 list
public static void main(String[] args) {
    LinkedList<Integer> list = new LinkedList<>();
    list.add(1); // add(elem): 表示尾插
    list.add(2);
    list.add(3);
    list.add(4);
    list.add(5);
    list.add(6);
    list.add(7);
    System.out.println(list.size());
    System.out.println(list);
// 在起始位置插入0
    list.add(0, 0); // add(index, elem): 在index位置插入元素elem
    System.out.println(list);
    list.remove(); // remove(): 删除第一个元素,内部调用的是removeFirst()
    list.removeFirst(); // removeFirst(): 删除第一个元素
    list.removeLast(); // removeLast(): 删除最后元素
    list.remove(1); // remove(index): 删除index位置的元素
    System.out.println(list);
// contains(elem): 检测elem元素是否存在,如果存在返回true,否则返回false
if(!list.contains(1)){
    list.add(0, 1);
}
    list.add(1);
    System.out.println(list);
    System.out.println(list.indexOf(1)); // indexOf(elem): 从前往后找到第一个elem的位置
    System.out.println(list.lastIndexOf(1)); // lastIndexOf(elem): 从后往前找第一个1的位置
    int elem = list.get(0); // get(index): 获取指定位置元素
    list.set(0, 100); // set(index, elem): 将index位置的元素设置为elem
    System.out.println(list);
// subList(from, to): 用list中[from, to)之间的元素构造一个新的LinkedList返回
    List<Integer> copy = list.subList(0, 3); 
    System.out.println(list);
    System.out.println(copy);
    list.clear(); // 将list中元素清空
    System.out.println(list.size());
}

六、LinkedList的遍历

public static void main(String[] args) {
    LinkedList<Integer> list = new LinkedList<>();
    list.add(1); // add(elem): 表示尾插
    list.add(2);
    list.add(3);
    list.add(4);
    list.add(5);
    list.add(6);
    list.add(7);
    System.out.println(list.size());
//for循环遍历
for (int i = 0; i < list.size(); i++) {
    System.out.print(list.get(i)+" ");
}
// foreach遍历
for (int e:list) {
    System.out.print(e + " ");
}
    System.out.println();
// 使用迭代器遍历---正向遍历
    ListIterator<Integer> it = list.listIterator();
while(it.hasNext()){
    System.out.print(it.next()+ " ");
}
    System.out.println();
// 使用反向迭代器---反向遍历
    ListIterator<Integer> rit = list.listIterator(list.size());
while (rit.hasPrevious()){
    System.out.print(rit.previous() +" ");
}
    System.out.println();
}

七、ArrayList和LinkedList的区别  

不同点数组链表
存储方式连续内存空间分散内存空间
容量扩展长度不可变可灵活扩展
内存效率元素占用内存少、但可能浪费空间元素占用内存多
访问元素O(1)O(N)
添加元素O(N)O(1)
删除元素O(N)O(1)

  • 26
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 12
    评论
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值