算法攻略-链表

链表是一种数据结构,它由一系列的节点组成,每个节点包含一个数据元素和一个指向下一个节点的指针。链表中的第一个节点叫做头节点,最后一个节点叫做尾节点。链表中的节点不一定是连续存储的,它们可以在内存中的任意位置。

链表的优点是插入和删除操作相对于数组来说更高效,因为只需要修改节点的指针,而无需移动其他节点。缺点是访问元素的效率相对较低,需要遍历整个链表才能找到目标元素。

链表有多种类型,常见的有单链表、双链表和循环链表。单链表每个节点只有一个指针指向下一个节点,双链表每个节点有两个指针,分别指向前一个节点和后一个节点,循环链表的尾节点指向头节点,形成一个闭环。

链表在许多编程问题中都有应用,比如实现栈、队列、图、树等数据结构,以及解决一些算法问题,如反转链表、合并链表等。

反转链表

 public ListNode reverse(ListNode l) {
        ListNode pre = null;
        ListNode cur = l;
        while (cur != null) {
            // 暂存下一个节点
            ListNode next = cur.next;
            // 下一个节点转向
            cur.next = pre;
            // 前一个移动到当前
            pre = cur;
            // 当前移动到下一个
            cur = next;
        }
        return pre;
    }

反转区间链表

  private ListNode reverse(ListNode a, ListNode b) {
        ListNode pre = null;
        ListNode cur = a;
        while (cur != b) {
            // 取出下一个节点暂存
            ListNode next = cur.next;
            // 下一个节点转向
            cur.next = pre;
            // 前一个移动到当前
            pre = cur;
            // 当前移动到下一个节点
            cur = next;
        }
        return pre;
    }

2两数相加

两数相加
题目要求:
给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。

请你将两个数相加,并以相同形式返回一个表示和的链表。

解题思路:
逆序存储,直接遍历链表就是从个位开始的,符合我们计算加法的习惯顺序。如果是正序存储,可能需要 翻转链表 或者使用栈来辅助。

public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        ListNode p1 = l1;
        ListNode p2 = l2;

        ListNode dummy = new ListNode(-1);

        ListNode p = dummy;

        int carry = 0;
        while (p1 != null || p2 != null || carry > 0) {
            int val = carry;
            if (p1 != null) {
                val += p1.val;
                p1 = p1.next;
            }
            if (p2 != null) {
                val += p2.val;
                p2 = p2.next;
            }
            // 处理进位
            carry = val / 10;
            val = val % 10;

            p.next = new ListNode(val);
            p = p.next;
        }

        return dummy.next;
    }

21. 合并两个有序链表

合并两个有序链表
题目要求:
输⼊两个升序链表,将它们合并为⼀个新的升序链表并返回。
解题思路:
「虚拟头结点」技巧,也就是 dummy 节点

public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        ListNode dummy = new ListNode(-1);
        ListNode p = dummy;
        ListNode p1 = l1;
        ListNode p2 = l2;

        // 都不为空比较大小
        while (p1 != null && p2 != null) {
            if (p1.val > p2.val) {
                p.next = p2;
                p2 = p2.next;
            } else {
                p.next = p1;
                p1 = p1.next;
            }
            p = p.next;
        }
        // p2剩余的放到p后面
        if (p1 == null) {
            p.next = p2;
        }
        // p1剩余的放到p后面
        if (p2 == null) {
            p.next = p1;
        }
        return dummy.next;
    }

141. 环形链表

环形链表
题目要求:
给你一个链表的头节点 head ,判断链表中是否有环。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。

如果链表中存在环 ,则返回 true 。 否则,返回 false 。
解题思路:
经典题目,要使用双指针技巧中的快慢指针,每当慢指针 slow 前进⼀步,快指针 fast 就前进两步。
如果 fast 最终遇到空指针,说明链表中没有环;如果 fast 最终和 slow 相遇,那肯定是 fast 超过了slow ⼀圈,说明链表中含有环。

 public boolean hasCycle(ListNode head) {
        if (head == null || head.next == null) {
            return false;
        }
        ListNode slow = head;
        ListNode fast = head;
        while (fast != null && fast.next != null) {
            fast = fast.next.next;
            slow = slow.next;
            // 快慢指针相遇
            if (fast == slow) {
                return true;
            }

        }
        return false;
    }

19. 删除链表的倒数第 N 个结点

删除链表的倒数第 N 个结点
题目要求:
给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
解题思路:
第⼀步,我们先让⼀个指针 p1 指向链表的头节点 head,然后走 k 步。
第⼆步,用⼀个指针 p2 指向链表头节点 head。
第三步,让 p1 和 p2 同时向前走,p1 走到链表末尾的空指针时走了 n - k 步,p2 也走了 n - k 步,也就是链表的倒数第 k 个节点。

ublic ListNode removeNthFromEnd(ListNode head, int n) {
        if (head == null || head.next == null) {
            return null;
        }
        ListNode fast = head;
        ListNode slow = head;
        // 让fast先走n步
        for (int i = 0; i < n; i++) {
            fast = fast.next;
        }
        // 如果fast指针到达末尾,说明需要删除的是头节点(走完了整个链表)
        if (fast == null) {
            return head.next;
        }

        while (fast != null) {
            fast = fast.next;
            slow = slow.next;
        }
        // 删除第n个节点,即将slow节点的next指向它的下下个节点
        slow.next = slow.next.next;

        return head;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值