算法-链表

一、理论基础

  1. 类型
    1. 单链表:链表是一种通过指针串联在一起的线性结构,每一个节点由两部分组成,一个是数据域一个是指针域(存放指向下一个节点的指针),最后一个节点的指针域指向null
    2. 双链表:每一个节点有两个指针域,一个指向下一个节点,一个指向上一个节点。可以向前查询也可以向后查询。
    3. 循环链表:链表首尾相连。
  2. 存储方式
    1. 数组是在内存中是连续分布的,但是链表在内存中可不是连续分布的。链表是通过指针域的指针链接在内存中各个节点。所以链表中的节点在内存中不是连续分布的 ,而是散乱分布在内存中的某地址上,分配机制取决于操作系统的内存管理。

  3. 定义
    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;
        }
    }
  4. 复杂度
    插入/删除查询                              适用场景
    数组O(n)O(1)数据量固定,频繁查询,较少插入
    链表O(1)O(n)数据量不固定,频繁增删,较少查询

二、移除链表

  1. 题目
    1. 给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点 。
  2. 思路:
    1. 头节点需要单独判断的,一般增加虚拟头节点
  3. 代码  
    class Solution {
        public ListNode removeElements(ListNode head, int val) {
            ListNode root = new ListNode(-1, head);
            ListNode curr = root;
            while (curr.next != null) {
                if (curr.next.val == val) {
                    curr.next = curr.next.next;
                } else {
                    curr = curr.next;
                }
            }
            return root.next;
        }
    }

三、设计链表

  1. 题目
  2. 思路
  3. 代码  
    class MyLinkedList {
        
        class ListNode{
            ListNode next;
            int val;
            ListNode(){}
            ListNode(ListNode next, int val){
                this.next = next;
                this.val = val;
            }
        }
        private ListNode head;
        private int size;
    
        public MyLinkedList() {
            head = new ListNode();
            this.size = 0;
        }
        
        public int get(int index) {
            if (index < 0 || index >= size) {
                return -1;
            }
            ListNode curr = head;
            while (index-- > 0) {
                curr = curr.next;
            }
            return curr.next.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 curr = head;
            while (index-- > 0) {
                curr = curr.next;
            }
            curr.next = new ListNode(curr.next, val);
            size++;
        }
        
        public void deleteAtIndex(int index) {
            if (index < 0 || index >= size) {
                return;
            }
            ListNode curr = head;
            while (index-- > 0) {
                curr = curr.next;
            }
            curr.next = curr.next.next;
            size--;
        }
    }

四、翻转链表

  1. 题目
    1. 给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。
  2. 思路
  3. 代码
    class Solution {
        public ListNode reverseList(ListNode head) {
            ListNode pre = null;
            ListNode curr = head;
            while (curr != null) {
                ListNode next = curr.next;
                curr.next = pre;
                pre = curr;
                curr = next;
            }
            return pre;
        }
    }

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

  1. 题目
    1. 给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点
  2. 思路
  3. 代码
    class Solution {
        public ListNode swapPairs(ListNode head) {
            ListNode root = new ListNode(-1, head);
            ListNode curr = root;
            while (curr.next != null && curr.next.next != null) {
                ListNode next1 = curr.next;
                ListNode next2 = curr.next.next;
                ListNode next3 = curr.next.next.next;
                curr.next = next2;
                next2.next = next1;
                next1.next = next3;
                curr = next1;
            }
            return root.next;
        }
    }

六、删除链表的倒数第N个节点

  1. 题目
    1. 给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
  2. 思路
    1. 先让快指针走n步
    2. 慢指针继续走
    3. 头结点需要特殊处理的采用虚拟头结点
  3. 代码
    class Solution {
        public ListNode removeNthFromEnd(ListNode head, int n) {
            ListNode root = new ListNode(-1, head);
            ListNode fast = root;
            ListNode slow = root;
            while (fast.next != null) {
                if (n > 0) {
                    n--;
                } else {
                    slow = slow.next;
                }
                fast = fast.next;
            }
            slow.next = slow.next.next;
            return root.next;
        }
    }

七、链表相交

  1. 题目
    1. 给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null 。
  2. 思路
  3. 代码
    public class Solution {
        public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
            ListNode a = headA;
            ListNode b = headB;
            while (a != b) {
                a = a == null ? headB : a.next;
                b = b == null ? headA : b.next;
            }
            return a;
        }
    }

八、环形链表II

  1. 题目
    1. 给定一个链表的头节点  head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null
  2. 思路
  3. 代码
    public class Solution {
        public ListNode detectCycle(ListNode head) {
            ListNode fast = head;
            ListNode slow = head;
            while (fast != null && fast.next != null) {
                fast = fast.next.next;
                slow = slow.next;
                // 存在环
                if (fast == slow) {
                    fast = head;
                    while (fast != slow) {
                        fast = fast.next;
                        slow = slow.next;
                    }
                    return slow;
                }
            }
            return null;
        }
    }

九、总结

  1. 虚拟头节点:每次对应头结点的情况都要单独处理,可使用虚拟头结点的技巧

未完待续......

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值