Java数据结构---链表

目录

单链表

模拟实现不带头非循环的单链表

1. 遍历链表

2. 判断关键字key是否存在链表中

 3.得到链表的长度

 4. 头插法

 5. 尾插法

 6, 任意位置插入

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

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

 9. 清空所有节点

快慢指针

模拟实现双链表 

 1. 遍历双链表

2.  求链表的长度

 3.判断关键字key是否存在链表中

4. 头插法

5.尾插法

6, 任意位置插入

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

8. 删除所有值为key的节点 ​编辑 

 9. 清空所有节点

链表的使用

 链表常用的方法


单链表

链表是一种常见的基础数据结构,是一种线性表,由一个一个的节点组成,但是并不会按线性的顺序存储数据,而是在每一个节点里存到下一个节点的地址。

 1. 一个链表分为两个域,val域用来存储数据,next域用来存储下一个节点的地址

 2. 链表物理上不一定是不连续的,但逻辑上是连续的.所谓的物理上就是指存储数值的地址不一定是连续的,逻辑上是指每个数的下一个数是连续的

模拟实现不带头非循环的单链表

public class MYsingleList {

    static class ListNode   {
        public int val; //存储的数据
        public ListNode next; //存储下一个节点的地址

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

    public ListNode head; // 代表当前链表的头节点的引用

    public void createLink() {
        ListNode listNode1 = new ListNode(12);
        ListNode listNode2 = new ListNode(12);
        ListNode listNode3 = new ListNode(23);
        ListNode listNode4 = new ListNode(90);
        listNode1.next = listNode2;
        listNode2.next = listNode3;
        listNode3.next = listNode4;
        head = listNode1;

}

因为我们写的是不带头的单链表,不知道头在哪里,通过定义一个head变量来引用当前链表的头节点,这样一来,head引用那个节点,就代表那个节点是头节点.而且我们在存储数据的时候不知道存储的下一个数据是什么,所以我们给一个 public ListNode(int val) 的构造方法,只用来存储数据.

1. 遍历链表

1. cur = head 如果我们在遍历链表的时候让head != null 那么当我们遍历完成时head为null,所以我们定义一个cur,让cur去完成遍历

2.  通过cur = cur.next让链表往后遍历

2. 判断关键字key是否存在链表中

 3.得到链表的长度

 4. 头插法

1. 在插入的时候首先要有一个节点,我们就需要实例化一个节点

 2.采用头插法插入到链表的头部,新的节点就就是头结点

 3. 插入完成后

 5. 尾插法

在我们插入前要判断链表当中是否有数据,如果没有数据,直接让头节点= 要插入的节点,否则会发生空指针异常

 6, 任意位置插入

插入前

 插入后

 1. 当index = 0时,相当于头插法,当Index = size( )相当于尾插法

 2. 当index < 0 或者 index > size( )时插入的位置不合法,

 3. 当index合法时,找插入的位置,我们让cur走到index - 1位置的节点的位置

 public void addIndex(int index, int data) throws ListIndexOutOfException {
        checkIndex(index);
        if (index == 0) {
            addFirst(data);
            return;
        }
        if (index == size()) {
            addLast(data);
            return;
        }
        ListNode cur = findIndexSubOne(index);
        ListNode listNode = new ListNode(data);
        listNode.next = cur.next;
        cur.next = listNode;

    }

    // 找到 index - 1 位置的节点的位置
    private ListNode findIndexSubOne(int index) {
        ListNode cur = head;
        int count = 0;
        while (count != index - 1) {
            cur = cur.next;
            count++;
        }
        return cur;
    }

    //查看是否合法
    private void checkIndex(int index) throws ListIndexOutOfException {
        if (index < 0 || index > size()) {
            throw new ListIndexOutOfException("index位置不合法");
        }
    }

异常

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

1. 我们的链表是单向的,如果我们直接找到要删除的节点,我们节不能知道前一个节点是什么,可以通过cur.next.val找到要删除节点的前一个节点

2. 当 cur.next == null 的时候,说明没有要删除的节点

3. 当删除的是头节点的时候直接让 head = head.next

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

1. 让 cur 从第二个节点开始遍历, prev 是cur的前驱节点,当 cur.val == key 时 prev.next = cur. next, curr = cur.next. 否则我们让cur继续遍历 prev = cur, cur = cur.next.( 同时让prev和cur往后走 )

2. 当cur遍历完整个链表的时候,我们会发现第一个节点没有遍历,新链表的头节点和原来的头节点是同一个节点,当 head.val == key时,head= head. next.这样头节点也是要被删除的节点的情况就会解决

 9. 清空所有节点

链表是由一个一个节点组成的,有一个next域指向下一个节点,如果我们直接把头节点干掉.那就没有节点继续指向下一个节点,就实现了清空所有节点

快慢指针

题目一:  链表的中间节点

 

1. 我们定义fast 和slow 两个节点都指向头节点,,让fast和slow同时往后走,fast每次走两步,slow每次走一步,当fast在终点的时候,slow在中间位置

2. 当链表的个数是奇数个时,当fast == null的时候,slow恰好在中间位置.

3. 当链表的个数是偶数个时,当fast.next == null时,slow恰好在中间位置 

4. 循环结束的条件: fast != null && fast.next != null. 当写成fast.next != null && fast != null时会发生空指针异常

 题目二 :  链表中倒数第k个节点 

通过观察发现,倒数第k个节点和最后一个节点相差k - 1步,我们就可以让fast先走k- 1步,然后和slow同时一步一步往后走,当fast.next == null时.slow是倒数第k个节点

 题目三:  判断链表是否有环

 判断成环的问题,其实就是数学上的追击问题,有环用快慢指针的方法,在环的里面快指针一定可以追上慢指针.

为什么快慢指针快指针走两步,慢指针走一步?

假设链表带环,两个指针最后都会进入环,快指针先进入环,慢指针后进入环.最好的情况是慢指针刚进入环就和快指针相遇了.最差的情况就是两个刚好差一个环的长度.此时,两个指针移动一次,之间的距离就减小一步.在慢指针走到一圈之前,快指针就可以追上慢指针

快指针一次走3步,4步,n步可以吗? 

假设快指针每次走三步,慢指针每次走一步,快指针先进入环,按照上述的方法,是永远不可能相遇的. 

模拟实现双链表 

一个双向链表有三个整数值: 数值、向后的节点链接、向前的节点链接。即前驱和后继 

 1. 遍历双链表

2.  求链表的长度

 3.判断关键字key是否存在链表中

4. 头插法

 

 如果双链表一开始就没有数据,直接让待插入的数据等于头节点和尾节点.

5.尾插法

 因为 last 一直指向的是尾节点,如果是第一次添加,让头节点和尾节点都指向node,否则我们只需要修改last的后继.

6, 任意位置插入

   

 

 如果是第一次插入,就采用头插法,如果是最后一次插入局采用尾插法

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

  ​​​​​​​ 

 当代码走到 return 的时候说明已经删除了第一次出现的 key

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

 和删除第一次出现的key方法类似,找到第一次出现的关键字后继续遍历这个链表,直到删除所有的key.

 9. 清空所有节点

双链表的清空不能和单链表一样,只让头结点置为null的话,因为每个节点都有前驱和后继,还是会有节点指向其他的节点,所以我们要让每个节点的前驱和后继都置为null然后让head 和 last 置为nul

链表的使用

LinkedList 继承了 AbstractSequentialList 类。

LinkedList 实现了 Queue 接口,可作为队列使用。

LinkedList 实现了 List 接口,可进行列表的相关操作。

LinkedList 实现了 Deque 接口,可作为队列使用。

LinkedList 实现了 Cloneable 接口,可实现克隆。

LinkedList 实现了 java.io.Serializable 接口,即可支持序列化,能通过序列化去传输。

LinkedList 类位于 java.util 包中,使用前需要引入它,语法格式如下:

 创建一个简单的链表实例:

 以上实例,执行输出结果为:

 链表常用的方法

 

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值