您现在的位置是:首页 >学无止境 >代码随想录算法训练营第三天 | 链表理论基础 ,203.移除链表元素 ,707.设计链表,206.反转链表网站首页学无止境

代码随想录算法训练营第三天 | 链表理论基础 ,203.移除链表元素 ,707.设计链表,206.反转链表

Jesus_night 2024-09-23 12:01:06
简介代码随想录算法训练营第三天 | 链表理论基础 ,203.移除链表元素 ,707.设计链表,206.反转链表

链表理论基础

链表是一种通过指针串联在一起的线性结构,每一个节点由两部分组成,一个是数据域一个是指针域(存放指向下一个节点的指针),最后一个节点的指针域指向null(空指针的意思)。

链表的入口节点称为链表的头结点也就是head。

如图所示:
删除节点

  • 链表的存储方式

数组是在内存中是连续分布的,但是链表在内存中可不是连续分布的。

链表是通过指针域的指针链接在内存中各个节点。

所以链表中的节点在内存中不是连续分布的 ,而是散乱分布在内存中的某地址上,分配机制取决于操作系统的内存管理。

  • 链表的定义
public class ListNode{
    int val;            // 节点上储存的元素
    ListNode next;      // 指向下一个节点的指针
    public ListNode() {}       // 无参构造
    // 带参构造
    public ListNode(int val) {
        this.val=val;
    }
    public ListNode(int val, ListNode next) {
        this.val=val;
        this.next=next;
    }
}
  • 链表的操作

    • 删除节点
      删除节点

    • 添加节点
      添加节点

    可以看出链表的增添和删除都是O(1)操作,也不会影响到其他节点。
    但是要注意,要是删除第五个节点,需要从头节点查找到第四个节点通过next指针进行删除操作,查找的时间复杂度是O(n)。

  • 性能分析
    链表性能分析

    数组在定义的时候,长度就是固定的,如果想改动数组的长度,就需要重新定义一个新的数组。
    链表的长度可以是不固定的,并且可以动态增删, 适合数据量不固定,频繁增删,较少查询的场景。

203.移除链表元素

203.移除链表元素

思路:
1. 找到满足条件节点的前一个节点,将该节点的下一个节点指向下下个节点(cur.next=cur.next.next)
2. 退出条件:目前节点的下个节点为空(cur.next!=null)

双指针法:

  1. 定义pre和cur两个指针,cur负责遍历链表,pre保持在cur前一个节点,循环结束条件为遍历所有节点(cur!=null)
  2. 找到满足条件的cur,使得该节点的前一个节点(pre)指向cur的下一个节点

注意:
涉及到头结点的更改最好定义一个虚拟头结点(dummy),新的头结点为虚拟节点的下一个节点

  • Java代码

    • 双指针法
    class Solution {
        public ListNode removeElements(ListNode head, int val) {
            ListNode dummy = new ListNode(0);
            dummy.next=head;
            ListNode cur = head;
            ListNode pre = dummy;
            while (cur!=null) {         // 遍历所有的链表数据
                if (cur.val==val) {
                    pre.next=cur.next;
                } else {                // 使得 pre 节点一直保持在 cur 的前一个节点
                    pre=cur;
                }
                cur=cur.next;
            }
            return dummy.next;
        }
    }
    

    时间复杂度:O(n)
    空间复杂度:O(1)

    • 完整代码
    package topic2ListNode;
    
    /*203. 移除链表元素
    
    给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点 。
    
    示例 1:
    
    输入:head = [1,2,6,3,4,5,6], val = 6
    输出:[1,2,3,4,5]
    链接:https://leetcode.cn/problems/remove-linked-list-elements/*/
    
    public class topic1_203 {
        public static void main(String[] args) {
            Solution solution = new topic1_203().new Solution();
            int[] arr = {1,2,6,3,4,5,6};
            int val = 6;
            ListNode head = new ListNode(arr);
            head.print();
            ListNode newHead = solution.removeElements(head, val);
            newHead.print();
        }
        class Solution {
            public ListNode removeElements(ListNode head, int val) {
                ListNode dummy = new ListNode(0);
                dummy.next=head;
                ListNode cur = head;
                ListNode pre = dummy;
    //            while (cur.next!=null) {          // 思路有问题的
    //                if (cur.next.val==val) {
    //                    cur.next=cur.next.next;
    //                }
    //                if (cur==null) {
    //                    break;
    //                }
    //                cur=cur.next;
    //            }
                while (cur!=null) {         // 遍历所有的链表数据
                    if (cur.val==val) {
                        pre.next=cur.next;
                    } else {                // 使得 pre 节点一直保持在 cur 的前一个节点
                        pre=cur;
                    }
                    cur=cur.next;
                }
                return dummy.next;
            }
        }
    }
    

707.设计链表

707.设计链表

实现链表的功能

注意:index取值范围为 [0, size-1]

  • Java代码

    • 代码实现
    class MyLinkedList {
        int val;
        MyLinkedList next;
        int size=0;
        public MyLinkedList() {
    
        }
        public MyLinkedList(int val, MyLinkedList next) {
            this.val=val;
            this.next=next;
        }
    
        public int get(int index) {
            if (index<0 || index>=size) return -1;
            MyLinkedList cur = this;
            while (index-- >= 0) {
                cur=cur.next;
            }
            return cur.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) {
                MyLinkedList cur = this;
                while (index-->0) {
                    cur=cur.next;
                }
                MyLinkedList newNode = new MyLinkedList(val, cur.next);
                cur.next=newNode;
                size++;
            }
        }
    
        public void deleteAtIndex(int index) {
            if (index>=0 && index<size) {       // 不是 index<=size ,index取值为 [0, size-1]
                MyLinkedList pre = this;
                MyLinkedList cur = pre.next;
                while (index-- > 0) {
                    pre=cur;
                    cur=cur.next;
                }
                pre.next=cur.next;
                size--;
            }
        }
    }
    

    链表和节点最好分开操作,链表中有长度以及节点

    class ListNode {
        int val;
        ListNode next;
    
        public ListNode() {}
        public ListNode(int val) {
            this.val=val;
        }
        public ListNode(int val, ListNode next) {
            this.val=val;
            this.next=next;
        }
    }
    
    class MyLinkedList {
        int size;
        ListNode head;
        public MyLinkedList() {
            size=0;
            head = new ListNode();
        }
        
        public int get(int index) {
            if (index<0 || index>=size) return -1;
            ListNode cur = head;
            while (index-- >= 0) {
                cur=cur.next;
            }
            return cur.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) {
                ListNode cur = head;
                while (index-- > 0) {
                    cur=cur.next;
                }
                ListNode newNode = new ListNode(val, cur.next);
                cur.next=newNode;
                size++;
            }
        }
        
        public void deleteAtIndex(int index) {
            if (index>=0 && index<size) {
                ListNode cur = head;
                while (index-- > 0) {
                    cur=cur.next;
                }
                cur.next=cur.next.next;
                size--;
            }
        }
    }
    

206.反转链表

206.反转链表

思路:

  1. 双指针指向前一个(pre)和后一个节点(cur),保存下一个节点(temp)
  2. 将后一个节点指向前一个节点(cur.next=pre),并移动双指针(pre=cur, cur=temp)
  3. 结束条件:后一个节点为空(cur!=null),返回前一个节点(pre)
    补充:
  4. 使用虚拟头结点,并置为空(pre=null, cur=head)
  5. 增加剪枝操作,针对空链表和只有一个节点的链表
  • Java代码

    • 双指针法
    class Solution {
        public ListNode reverseList(ListNode head) {
            if (head==null || head.next==null) return head;
            ListNode pre = null;
            ListNode cur = head;
            ListNode temp;
            while (cur!=null) {
                temp=cur.next;
                cur.next=pre;
                pre=cur;
                cur=temp;
            }
            return pre;
        }
    }
    

    时间复杂度:O(n)
    空间复杂度:O(1)

    • 完整代码
    package topic2ListNode;
    
    /*206. 反转链表
    
        给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。
    
        示例 1:
    
    
        输入:head = [1,2,3,4,5]
        输出:[5,4,3,2,1]
        链接:https://leetcode.cn/problems/reverse-linked-list/*/
    
    public class topic3_206 {
        public static void main(String[] args) {
            Solution solution = new topic3_206().new Solution();
            int[] arr = {1,2,3,4,5};
            ListNode head = new ListNode(arr);
            head.print();
            ListNode newHead = solution.reverseList(head);
            newHead.print();
        }
        class Solution {
            public ListNode reverseList(ListNode head) {
                if (head==null || head.next==null) return head;
                ListNode pre = null;
                ListNode cur = head;
                ListNode temp;
                while (cur!=null) {
                    temp=cur.next;
                    cur.next=pre;
                    pre=cur;
                    cur=temp;
                }
                return pre;
            }
        }
    }
    
风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。