0
点赞
收藏
分享

微信扫一扫

Java数据结构-认识链表

在觉 2022-01-28 阅读 33

文章目录

一.链表的概念及结构

1.链表的概念

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

2.链表的分类

链表情况非常多样,组合起来共有八种

  • 单向、双向
  • 带头、不带头
  • 循环、非循环
    但是这里我们重点讲两种:单向不带头非循环链表双向不带头非循环链表
    在这里插入图片描述在这里插入图片描述

二.单向不带头非循环链表

1.创建节点类型

这里我们创建一个ListNode类来作为节点类型,包含两个成员变量:val域用来储存数据,next用来存储下一个节点的地址。
再创建一个带参的构造方法来实例化对象,同时给val赋值,next的默认值是null。接下来我们用代码来实现一下:

class ListNode{

    public int val;
    public ListNode next;//next存储的是下一个节点的地址,所以他的类型是一个节点类型

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

然后我们在MyLinkedList里创建一个节点类型:head。可能大家会有疑问了,为什么在MyLinkedList里创建,而不是在节点里创建呢?因为head是链表的头,而不是节点的头,节点只有两个属性:nextval
准备工作做完,我们就可以具体实现链表的增删查改等操作啦!

2.头插法

在这里插入图片描述具体代码实现如下:

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

3.尾插法

在这里插入图片描述
具体的代码实现如下:

//尾插法
    public void addLast(int data){
        ListNode node = new ListNode(data);
        if (this.head == null){
            this.head = node;//说明是第一次插入,直接让node变成head
        }else{
            ListNode cur = this.head;
            while(cur.next != null){
                cur = cur.next;
            }
            // cur走完了所有节点 : cur.next = null
            cur.next = node;
        }
    }

4.打印单链表

具体的代码实现如下:

//打印链表长度
    public void display(){
        ListNode cur = this.head;
        while( cur != null){
            System.out.print(cur.val + " ");
            cur = cur.next;
        }
        System.out.println();
    }

5.查找key是否在单链表中

具体的代码实现如下:

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

6.得到单链表的长度

具体的代码实现如下:

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

7.任意位置插入,第一个数据节点为0号下标

在这里插入图片描述
具体的代码实现如下:

//先找到index-1位置 节点的地址,就是cur位置
    public ListNode findIndex(int index){
        ListNode cur = this.head;
        while(index-1 != 0){
            cur=cur.next;
            index--;
        }
        return cur;
    }

//任意位置插入,第一个数据节点为0号下标
    public void addIndex(int index,int data){
        if (index<0 || index>size()) {//先判断index坐标的合法性
            System.out.println("index位置不合法!");
            return;
        }
        if (index == 0){//头插法
            addFirst(data);
            return;
        }
        if (index == size()){//尾插法
            addLast(data);
            return;
        }
        ListNode cur = findIndex(index);
        ListNode node = new ListNode(data);
        node.next = cur.next;
        cur.next = node;
    }

8.删除第一次出现关键字为key的节点

在这里插入图片描述
具体的代码实现如下:

//先找到key节点的前驱节点
    public  ListNode searchPrev(int key){
        ListNode cur = this.head;
        while (cur.next != null){
            if (cur.next.val == key){//如果找到该前驱节点,返回前驱节点cur
                return cur;
            }
            cur = cur.next;//未找到就继续遍历链表
        }
        return null;//如果循环完都没找到,就返回null
    }
//删除第一次出现关键字为key的节点
    public void remove(int key){
        if (this.head == null){
            System.out.println("链表为空,不能删除!");
            return;
        }
        if (this.head.val == key){//要删除的位置就在头节点
            this.head = this.head.next;
            return;
        }
        ListNode cur = searchPrev(key);//调用刚刚写的函数来寻找前驱cur
        if (cur == null){
            System.out.println("没有你要删除的节点!");
            return;
        }
        ListNode del = cur.next;
        cur.next = del.next;
    }

9.删除所有值为key的节点

在这里插入图片描述
具体代码实现如下:

//删除所有值为key的节点
    public ListNode removeAllKey(int key){
        if (this.head == null)
            return null;

        ListNode prev = this.head;
        ListNode cur = this.head.next;

        while (cur!= null){
            if (cur.val == key){//假如cur节点是要删除的节点,将cur节点删去再继续遍历
                 prev.next = cur.next;
                 cur = cur.next;
            }else{//cur节点不是要删除的节点,继续往后遍历
                prev = cur;
                cur = cur.next;
            }
        }
        //最后处理头,判断头节点的val域是不是key值
        if (this.head.val == key){
            this.head = this.head.next;
        }
        return this.head;
    }

10.清空单链表

  • 暴力清空:直接将头节点置空
//清空单链表
    public void clear(){
        this.head = null;
    }
  • 温柔清空:将节点一个一个释放
//清空单链表
    public void clear(){
        while (this.head != null){
            ListNode curNext = head.next;
            this.head.next = null;
            this.head = curNext;
        }
    }

三.双向不带头循环链表

1.创建节点类型

这里我们创建一个ListNode类来作为节点类型,包含三个成员变量:val域用来储存数据,next用来存储下一个节点的地址,prev用来存储上一个节点的地址。
再创建一个带参的构造方法来实例化对象,同时给val赋值,next和prev的默认值是null。接下来我们用代码来实现一下:

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

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

然后,我们在MyLinkedList里创建两个节点类型,分别是headlast,head指向双链表的头节点,last指向双链表的尾节点。下面,我们来进行双链表的增删查改!

2.头插法

在这里插入图片描述
具体的代码实现如下:

//头插法
        public void addFirst(int data){
            ListNode node = new ListNode(data);
            //先判断是不是第一次插入
            if (this.head == null){
                this.head = node;
                this.last = node;
            }else {
                node.next = this.head;
                this.head.prev = node;
                this.head = node;//最后将头节点改为node
            }
        }

3.尾插法

在这里插入图片描述
具体的代码实现如下:

//尾插法
        public void addLast(int data){
            ListNode node = new ListNode(data);
            //跟头插法一样,还是要先判断是不是第一次插入
            if (this.head == null){
                this.head = node;
                this.last = node;
            }else{
                this.last.next = node;
                node.prev = this.last;
                this.last = node;
            }
        }

4.打印双链表

具体的代码实现如下:

//打印双链表
         public void display(){
            //和单链表的打印方式一样
             ListNode cur = this.head;
             while (cur != null){
                 System.out.print(cur.val + " ");
                 cur = cur.next;
             }
             System.out.println();
         }

5.查找key是否在双链表中

具体的代码实现如下:

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

6.得到双链表的长度

具体的代码实现如下:

//打印双链表
         public void display(){
            //和单链表的打印方式一样
             ListNode cur = this.head;
             while (cur != null){
                 System.out.print(cur.val + " ");
                 cur = cur.next;
             }
             System.out.println();
         }

7.任意位置插入,第一个数据节点为0号下标

在这里插入图片描述具体的代码实现如下:

//找到要插入节点的位置
        public ListNode searchIndex(int index){
            ListNode cur = this.head;
            while (index != 0) {
                cur = cur.next;
                index--;
            }
            return cur;
        }
//任意位置插入,第一个数据节点为0号下标
        public void addIndex(int index,int data){
            ListNode node = new ListNode(data);
            if (index<0 || index>size()){//先判断index位置的合法性
                System.out.println("该位置不合法!");
            }if (index == 0){//头节点插入,采用头插法
                addFirst(data);
            }if (index == size()){//尾节点插入,采用尾插法
                addLast(data);
            }
            ListNode cur = searchIndex(index);
            node.next = cur.prev.next;
            cur.prev.next = node;
            node.prev = cur.prev;
            cur.prev = node;
        }

8.删除第一次出现关键字为key的节点

在这里插入图片描述
具体的代码实现如下:(这段代码可能难理解,建议画图自己写一遍)

//删除第一次出现关键字为key的节点
         public void remove(int key){
            ListNode cur = this.head;
             while (cur != null) {
                 if (cur.val == key) {
                     if (cur == head){//首先判断要删除的节点是不是头节点
                         this.head = this.head.next;//先将头节点往后移一位
                         if (head != null){//如果双链表不是只有一个节点
                             this.head.prev = null;//再将现在头节点的前驱置为空
                         }else{//如果双链表只有一个节点,即head为空了
                             this.last = null;//要把last也置为空
                         }
                     }else {
                         cur.prev.next = cur.next;//将cur的next,赋给cur前驱的next
                         if (cur.next != null) {//说明不是尾节点,是中间位置
                             cur.next.prev = cur.prev;
                         }else{//说明是尾节点,只需要将last往前移一位
                             this.last = this.last.prev;
                         }
                     }
                     return;
                 }
                 cur = cur.next;
             }
         }

9.删除所有值为key的节点

具体的代码实现如下:

//删除所有值为key的节点
         public void removeAllKey(int key){
             ListNode cur = this.head;
             while (cur != null) {
                 if (cur.val == key) {
                     if (cur == head){//首先判断要删除的节点是不是头节点
                         this.head = this.head.next;//先将头节点往后移一位
                         if (head != null){//如果双链表不是只有一个节点
                             this.head.prev = null;//再将现在头节点的前驱置为空
                         }else{//如果双链表只有一个节点,即head为空了
                             this.last = null;//要把last也置为空
                         }
                     }else {
                         cur.prev.next = cur.next;//将cur的next,赋给cur前驱的next
                         if (cur.next != null) {//说明不是尾节点,是中间位置
                             cur.next.prev = cur.prev;
                         }else{//说明是尾节点,只需要将last往前移一位
                             this.last = this.last.prev;
                         }
                     }
                     //删完cur继续往后走,不return
                 }
                 cur = cur.next;
             }
         }

10.清空双链表

  • 暴力清空:将头节点和尾节点都置为空
  • 温柔清空:先将head一个一个清空,最后将last也置空
//清空双向链表
         public void clear(){
             while (head != null) {
                 ListNode curNext = head.next;
                 head.next = null;
                 head.prev = null;
                 head = curNext;
             }
             last = null;//最后将last也全部置为空
         }

四.顺序表和链表的区别

1.从组织上看

  • 顺序表底层是一个数组,是逻辑上和物理上都连续的
  • 链表是一个由若干节点组成的数据结构,逻辑上是连续的,但是物理/内存上不一定连续

2.从操作上看

  • 顺序表适合查找相关操作,因为可以使用下标直接获取某一位置的元素
  • 链表适合需要频繁插入、删除操作。不需要像顺序表一样移动元素,只需要修改指向
  • 顺序表满了后还需要扩容,扩容空间也不一定能完全利用,空间利用率不高。而链表随用随取,不用担心空间的浪费
举报

相关推荐

0 条评论