Carl代码随想录算法训练营-Day 3- 链表理论基础、203.移除链表元素、707.设计链表、24.两两交换链表中的节点

本文详细介绍了链表的基本理论,包括单链表的结构、如何删除节点(处理头节点问题)、设计自定义链表(单链表和双链表实现),以及解决LeetCode题目中的两两交换节点问题。通过实例和代码展示了链表操作的关键思路和实现。
摘要由CSDN通过智能技术生成

每日算法学习记录

链表基础理论

链表是计算机系统的基础数据结构,是逻辑数据结构。在人眼看起来,链表都是接在一起的,自然而然,好像和数组没什么两样,但实际上节点各自的地址可以没有任何规律和顺序,可以“随心所欲”。单链表是下面这个样子,本文默认讨论的链表都是单链表。
在这里插入图片描述
这就要求链表的每个节点除了储存数据,还要储存它下一个节点的信息,也就是下一个节点的地址。在这里插入图片描述
如果要用Java定义一个链表节点ListNode类,它应该长成这样:

class ListNode{
	int val;
	ListNode next;
	ListNode(int val, ListNode next) {};
}

因此,我们在定义链表的时候,只需要手里有一个头节点head,通过一个指针p,就可以遍历一整个链表了。遍历操作可以是p = p.next,当p为空的时候,就说明遍历到尾节点了,因为再没有下一个节点了。

203、移除链表节点

LeetCode题目链接

思路分析

要删除一个节点,那么我们只需要将当前节点有关的信息都从链表中移除就可以了。那么当前节点的信息储存在链表的哪个位置呢?答:上一个节点。
因此我们要做的,必然是修改上一个节点的next域,改成谁?当然是改成下一个节点了。

那这时候就会发现一个问题:如果要删头节点,头节点哪有上一个节点啊?
要处理这个问题,一般有两种方法,一个是单独处理删头节点的情况,一个是它没有上一个节点,那我就给它创造一个虚拟节点。那么博主推荐大部分都采用的第二个虚拟头节点法

dummyHead = new ListNode(-1, head)

那么我们可以用图像展现一下删除过程:
在这里插入图片描述
盘点一下需要完成的操作逻辑:

  1. 输入一个链表的头节点head
  2. 定义一个虚拟头节点dummyHead,这个节点的next赋值为head
  3. 定义一个指针p,初始值赋值为dummyHead
  4. 判断p.next所指向的节点是否为空,是的话跳到步骤8
  5. 比较p.next所指向的节点的数据是否等于要删除的数据值,否的话跳到步骤7
  6. 获取p.next所指向的节点的下一个节点的地址,赋值给p.next,返回步骤2
  7. p指针挪向下一个节点
  8. 返回dummyHead的下一个节点

代码展现

public ListNode removeElements(ListNode head, int val) {
            head = new ListNode(0, head);
            ListNode p = head;
            while (p.next != null) {
                if (p.next.val == val) {
                    p.next = p.next.next;
                } else {
                    p = p.next;
                }
            }
            return head.next;
        }

707、设计链表

LeetCode题目链接

思路分析

设计一个自定义链表,可以写单链表,也可以写双链表。本文仅分析单链表写法,但也给出了双链表的代码展现。首先,自定义链表需要包含以下方法:

方法描述
MyLinkedList初始化 MyLinkedList 对象。
get(index)获取链表中第 index 个节点的值。如果索引无效,则返回-1
addAtHead(val)在链表的第一个元素之前添加一个值为 val 的节点。插入后,新节点将成为链表的第一个节点。
addAtTail(val)将值为 val 的节点追加到链表的最后一个元素。
addAtIndex(index,val)在链表中的第 index 个节点之前添加值为 val 的节点。如果 index 等于链表的长度,则该节点将附加到链表的末尾。如果 index 大于链表长度,则不会插入节点。如果index小于0,则在头部插入节点。
deleteAtIndex(index)如果索引 index 有效,则删除链表中的第 index 个节点。

我们仍然需要先定义这个链表的节点类型ListNode
在这里插入图片描述
get方法只需要从头节点开始,往后简单遍历,等到计数达到index,即可返回节点的数据值。
addAtIndexdeleteAtIndex方法都需要区分对头节点的操作和对非头节点的操作,因此同样可以使用虚拟头节点进行简化。
因此MyLinkedList类需要包含两个成员属性:虚拟头节点head、链表大小size
在这里插入图片描述
在这里插入图片描述

代码展现

//单链表
class MyLinkedList{
    ListNode head;
    int size;

    private class ListNode {
        ListNode next;
        int val;

        public ListNode() {
        }

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

    public MyLinkedList() {
        head = new ListNode(-1, null);
        size = 0;
    }

    public int get(int index) {
        if(index<0||index>=size){
            return -1;
        }else{
            ListNode p=head;
            while(index-->=0){
                p=p.next;
            }
            return  p.val;
        }
    }

    public void addAtHead(int val) {
        addAtIndex(0,val);
    }

    public void addAtTail(int val) {
        addAtIndex(size,val);
    }

    public void addAtIndex(int index, int val) {
        if(index<0||index>size){
            return;
        }
        ListNode p=head;
        while(index-->0){
            p=p.next;
        }
        ListNode tmp=p.next;
        if(tmp==null){
            p.next=new ListNode(val,null);
        }else{
            p.next=new ListNode(val,tmp);
        }
        size++;
    }

    public void deleteAtIndex(int index) {
        if(index<0||index>=size){
            return;
        }
        ListNode p=head;
        while(index-->0){
            p=p.next;
        }
        p.next=p.next.next;
        size--;
    }
}

双链表则要比单链表多维护一个尾节点,但是也因此使得遍历、插入、删除操作更为高效、简单。

//双链表
class MyLinkedList{
    final private ListNode _head;
    final private ListNode _tail;
    private int _size;
    private class ListNode{
        int val;
        ListNode next,prev;
        public ListNode(int val){
            this(val,null,null);
        }
        public ListNode(int val,ListNode next,ListNode prev){
            this.val=val;
            this.next=next;
            this.prev=prev;
        }
        public ListNode(){}
    }
    public MyLinkedList() {
        _head=new ListNode(0);
        _size=0;
        _tail=new ListNode(0,null,_head);
        _head.next=_tail;
    }
    private ListNode get(int index,boolean mark){
        if(index>=_size||index<0){
            return null;
        }
        ListNode p;
        if(index<=_size>>1){
            p=_head;
            for(int i=0;i<=index;i++){
                p=p.next;
            }
        }else{
            p=_tail;
            for(int i=_size-1;i>=index;i--){
                p=p.prev;
            }
        }
        return p;
    }
    public int get(int index) {
        ListNode p = get(index, true);
        if(p==null) return -1;
        return p.val;
    }

    public void addAtHead(int val) {
        ListNode tmp=_head.next;
        _head.next=new ListNode(val,tmp,_head);
        tmp.prev=_head.next;
        _size++;
    }

    public void addAtTail(int val) {
        ListNode tmp = _tail.prev;
        _tail.prev=new ListNode(val,_tail,tmp);
        tmp.next=_tail.prev;
        _size++;
    }

    public void addAtIndex(int index, int val) {
        if(index==_size){
            addAtTail(val);
        }else if(index<_size){
            ListNode p=get(index,true);
            if(p==null) return;
            p.prev.next=new ListNode(val,p,p.prev);
            p.prev=p.prev.next;
            _size++;
        }
    }

    public void deleteAtIndex(int index) {
        ListNode p=get(index,true);
        if(p==null) return;
        p.prev.next=p.next;
        p.next.prev=p.prev;
        _size--;
    }
}

24、两两交换链表中的节点

LeetCode题目链接

思路分析

少废话直接上图,这个图很清晰得展现了两两交换链表节点的3个修改步骤。
在这里插入图片描述

代码展现

public ListNode swapPairs(ListNode head) {
        if(head==null) return head;
        head=new ListNode(0,head);
        ListNode pre=head;
        ListNode cur=head.next;
        while(cur!=null&&cur.next!=null){
            pre.next=cur.next;
            cur.next=pre.next.next;
            pre.next.next=cur;
            pre=pre.next.next;
            cur=pre.next;
        }
        return head.next;
    }

总结和思考

以上就是今天的算法学习记录,我们通过分析思路和实现代码的方式,解决了这道题目。希望对大家的算法学习有所帮助,谢谢阅读!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值